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 SmsAssembler()38 public SmsAssembler() { 39 mPtsCache = new HashMap<String, RawPtsData>(); 40 } 41 setSmsListener(SmsListener listener)42 public void setSmsListener(SmsListener listener) { 43 mListener = listener; 44 } 45 onIncomingSms(byte[] data)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 extractPreamble(byte[] data)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 notifyAssembledSms(byte[] data)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 RawPtsData(int origPreambleLen, int totalSegments)113 public RawPtsData(int origPreambleLen, int totalSegments) { 114 mOrigPreambeLen = origPreambleLen; 115 mSegments = new byte[totalSegments][]; 116 } 117 setSegment(int index, byte[] segment)118 public void setSegment(int index, byte[] segment) { 119 mSegments[index] = segment; 120 } 121 isAllSegmentsReceived()122 public boolean isAllSegmentsReceived() { 123 for (byte[] segment : mSegments) { 124 if (segment == null) { 125 return false; 126 } 127 } 128 return true; 129 } 130 assemble()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 calculateLength()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 } 163