2 * Copyright (C) 2008 Esmertec AG.
3 * Copyright (C) 2008 The Android Open Source Project
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 package com.android.im.imps;
19 import java.io.UnsupportedEncodingException;
20 import java.util.HashMap;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
24 import com.android.im.engine.SmsService.SmsListener;
26 public class SmsAssembler implements SmsListener {
28 // aa - version number; 12 for 1.2, 13 for 1.3; "XX" for version discovery
29 // BB - message type, case insensitive
30 // ccc - transaction id in range 0-999 without preceding zero
31 // DD - multiple SMSes identifier
32 private static final Pattern sPreamplePattern =
33 Pattern.compile("\\AWV(\\d{2})(\\p{Alpha}{2})(\\d{1,3})(\\p{Alpha}{2})?");
35 private SmsListener mListener;
36 private HashMap<String, RawPtsData> mPtsCache;
38 public SmsAssembler() {
39 mPtsCache = new HashMap<String, RawPtsData>();
42 public void setSmsListener(SmsListener listener) {
46 public void onIncomingSms(byte[] data) {
47 String preamble = extractPreamble(data);
48 if (preamble == null) {
49 ImpsLog.logError("Received non PTS SMS");
53 Matcher m = sPreamplePattern.matcher(preamble);
55 ImpsLog.logError("Received non PTS SMS");
58 String dd = m.group(4);
59 if (dd == null || dd.length() == 0) {
60 notifyAssembledSms(data);
62 int totalSegmentsCount = dd.charAt(1) - 'a' + 1;
63 int index = dd.charAt(0) - 'a';
64 if (index < 0 || index >= totalSegmentsCount) {
65 ImpsLog.logError("Invalid multiple SMSes identifier");
69 String transId = m.group(3);
70 RawPtsData pts = mPtsCache.get(transId);
72 pts = new RawPtsData(preamble.length(), totalSegmentsCount);
73 mPtsCache.put(transId, pts);
76 pts.setSegment(index, data);
77 if (pts.isAllSegmentsReceived()) {
78 mPtsCache.remove(transId);
79 notifyAssembledSms(pts.assemble());
84 private String extractPreamble(byte[] data) {
86 int preambleIndex = 0;
87 while (data[preambleIndex] != ' ' && preambleIndex < N) {
91 if (preambleIndex >= N) {
96 return new String(data, 0, preambleIndex, "UTF-8");
97 } catch (UnsupportedEncodingException e) {
103 private void notifyAssembledSms(byte[] data) {
104 if (mListener != null) {
105 mListener.onIncomingSms(data);
109 private static class RawPtsData {
110 private int mOrigPreambeLen;
111 private byte[][] mSegments;
113 public RawPtsData(int origPreambleLen, int totalSegments) {
114 mOrigPreambeLen = origPreambleLen;
115 mSegments = new byte[totalSegments][];
118 public void setSegment(int index, byte[] segment) {
119 mSegments[index] = segment;
122 public boolean isAllSegmentsReceived() {
123 for (byte[] segment : mSegments) {
124 if (segment == null) {
131 public byte[] assemble() {
132 int len = calculateLength();
133 byte[] res = new byte[len];
136 System.arraycopy(mSegments[0], 0, res, index, mOrigPreambeLen - 2);
137 index += mOrigPreambeLen - 2;
140 for (byte[] segment : mSegments) {
141 int payloadStart = mOrigPreambeLen + 1;
142 int payloadLen = segment.length - payloadStart;
143 System.arraycopy(segment, payloadStart, res, index, payloadLen);
149 private int calculateLength() {
150 // don't have 'dd' in assembled data
151 int preambleLen = mOrigPreambeLen - 2;
153 int total = preambleLen + 1;// a space after preamble
154 for (byte[] segment : mSegments) {
155 int segmentPayload = segment.length - (mOrigPreambeLen + 1);
156 total += segmentPayload;