FileDocCategorySizeDatePackage
SmsAssembler.javaAPI DocAndroid 1.5 API5207Wed May 06 22:42:46 BST 2009com.android.im.imps

SmsAssembler.java

/*
 * 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;
        }
    }

}