OSDN Git Service

auto import from //depot/cupcake/@132589
[android-x86/packages-apps-IM.git] / src / com / android / im / imps / SmsAssembler.java
1 /*
2  * Copyright (C) 2008 Esmertec AG.
3  * Copyright (C) 2008 The Android Open Source Project
4  *
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
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17 package com.android.im.imps;
18
19 import java.io.UnsupportedEncodingException;
20 import java.util.HashMap;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
23
24 import com.android.im.engine.SmsService.SmsListener;
25
26 public class SmsAssembler implements SmsListener {
27     // WVaaBBcccDD
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})?");
34
35     private SmsListener mListener;
36     private HashMap<String, RawPtsData> mPtsCache;
37
38     public SmsAssembler() {
39         mPtsCache = new HashMap<String, RawPtsData>();
40     }
41
42     public void setSmsListener(SmsListener listener) {
43         mListener = listener;
44     }
45
46     public void onIncomingSms(byte[] data) {
47         String preamble = extractPreamble(data);
48         if (preamble == null) {
49             ImpsLog.logError("Received non PTS SMS");
50             return;
51         }
52
53         Matcher m = sPreamplePattern.matcher(preamble);
54         if (!m.matches()) {
55             ImpsLog.logError("Received non PTS SMS");
56             return;
57         }
58         String dd = m.group(4);
59         if (dd == null || dd.length() == 0) {
60             notifyAssembledSms(data);
61         } else {
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");
66                 return;
67             }
68
69             String transId = m.group(3);
70             RawPtsData pts = mPtsCache.get(transId);
71             if (pts == null) {
72                 pts = new RawPtsData(preamble.length(), totalSegmentsCount);
73                 mPtsCache.put(transId, pts);
74             }
75
76             pts.setSegment(index, data);
77             if (pts.isAllSegmentsReceived()) {
78                 mPtsCache.remove(transId);
79                 notifyAssembledSms(pts.assemble());
80             }
81         }
82     }
83
84     private String extractPreamble(byte[] data) {
85         int N = data.length;
86         int preambleIndex = 0;
87         while (data[preambleIndex] != ' ' && preambleIndex < N) {
88             preambleIndex++;
89         }
90
91         if (preambleIndex >= N) {
92             return null;
93         }
94
95         try {
96             return new String(data, 0, preambleIndex, "UTF-8");
97         } catch (UnsupportedEncodingException e) {
98             // impossible
99             return null;
100         }
101     }
102
103     private void notifyAssembledSms(byte[] data) {
104         if (mListener != null) {
105             mListener.onIncomingSms(data);
106         }
107     }
108
109     private static class RawPtsData {
110         private int mOrigPreambeLen;
111         private byte[][] mSegments;
112
113         public RawPtsData(int origPreambleLen, int totalSegments) {
114             mOrigPreambeLen = origPreambleLen;
115             mSegments = new byte[totalSegments][];
116         }
117
118         public void setSegment(int index, byte[] segment) {
119             mSegments[index] = segment;
120         }
121
122         public boolean isAllSegmentsReceived() {
123             for (byte[] segment : mSegments) {
124                 if (segment == null) {
125                     return false;
126                 }
127             }
128             return true;
129         }
130
131         public byte[] assemble() {
132             int len = calculateLength();
133             byte[] res = new byte[len];
134             int index = 0;
135             // copy the preamble
136             System.arraycopy(mSegments[0], 0, res, index, mOrigPreambeLen - 2);
137             index += mOrigPreambeLen - 2;
138             res[index++] = ' ';
139
140             for (byte[] segment : mSegments) {
141                 int payloadStart = mOrigPreambeLen + 1;
142                 int payloadLen = segment.length - payloadStart;
143                 System.arraycopy(segment, payloadStart, res, index, payloadLen);
144                 index += payloadLen;
145             }
146             return res;
147         }
148
149         private int calculateLength() {
150             // don't have 'dd' in assembled data
151             int preambleLen = mOrigPreambeLen - 2;
152
153             int total = preambleLen + 1;// a space after preamble
154             for (byte[] segment : mSegments) {
155                 int segmentPayload = segment.length - (mOrigPreambeLen + 1);
156                 total += segmentPayload;
157             }
158             return total;
159         }
160     }
161
162 }