/* * Copyright (C) 2008 Esmertec AG. * 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. */ package com.android.im.imps; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.android.im.engine.SmsService.SmsListener; public class SmsAssembler implements SmsListener { // WVaaBBcccDD // aa - version number; 12 for 1.2, 13 for 1.3; "XX" for version discovery // BB - message type, case insensitive // ccc - transaction id in range 0-999 without preceding zero // DD - multiple SMSes identifier private static final Pattern sPreamplePattern = Pattern.compile("\\AWV(\\d{2})(\\p{Alpha}{2})(\\d{1,3})(\\p{Alpha}{2})?"); private SmsListener mListener; private HashMap<String, RawPtsData> mPtsCache; public SmsAssembler() { mPtsCache = new HashMap<String, RawPtsData>(); } public void setSmsListener(SmsListener listener) { mListener = listener; } public void onIncomingSms(byte[] data) { String preamble = extractPreamble(data); if (preamble == null) { ImpsLog.logError("Received non PTS SMS"); return; } Matcher m = sPreamplePattern.matcher(preamble); if (!m.matches()) { ImpsLog.logError("Received non PTS SMS"); return; } String dd = m.group(4); if (dd == null || dd.length() == 0) { notifyAssembledSms(data); } else { int totalSegmentsCount = dd.charAt(1) - 'a' + 1; int index = dd.charAt(0) - 'a'; if (index < 0 || index >= totalSegmentsCount) { ImpsLog.logError("Invalid multiple SMSes identifier"); return; } String transId = m.group(3); RawPtsData pts = mPtsCache.get(transId); if (pts == null) { pts = new RawPtsData(preamble.length(), totalSegmentsCount); mPtsCache.put(transId, pts); } pts.setSegment(index, data); if (pts.isAllSegmentsReceived()) { mPtsCache.remove(transId); notifyAssembledSms(pts.assemble()); } } } private String extractPreamble(byte[] data) { int N = data.length; int preambleIndex = 0; while (data[preambleIndex] != ' ' && preambleIndex < N) { preambleIndex++; } if (preambleIndex >= N) { return null; } try { return new String(data, 0, preambleIndex, "UTF-8"); } catch (UnsupportedEncodingException e) { // impossible return null; } } private void notifyAssembledSms(byte[] data) { if (mListener != null) { mListener.onIncomingSms(data); } } private static class RawPtsData { private int mOrigPreambeLen; private byte[][] mSegments; public RawPtsData(int origPreambleLen, int totalSegments) { mOrigPreambeLen = origPreambleLen; mSegments = new byte[totalSegments][]; } public void setSegment(int index, byte[] segment) { mSegments[index] = segment; } public boolean isAllSegmentsReceived() { for (byte[] segment : mSegments) { if (segment == null) { return false; } } return true; } public byte[] assemble() { int len = calculateLength(); byte[] res = new byte[len]; int index = 0; // copy the preamble System.arraycopy(mSegments[0], 0, res, index, mOrigPreambeLen - 2); index += mOrigPreambeLen - 2; res[index++] = ' '; for (byte[] segment : mSegments) { int payloadStart = mOrigPreambeLen + 1; int payloadLen = segment.length - payloadStart; System.arraycopy(segment, payloadStart, res, index, payloadLen); index += payloadLen; } return res; } private int calculateLength() { // don't have 'dd' in assembled data int preambleLen = mOrigPreambeLen - 2; int total = preambleLen + 1;// a space after preamble for (byte[] segment : mSegments) { int segmentPayload = segment.length - (mOrigPreambeLen + 1); total += segmentPayload; } return total; } } }