1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.telephony;
18
19import com.android.internal.telephony.SmsHeader;
20import java.util.Arrays;
21
22import static android.telephony.SmsMessage.MessageClass;
23import android.provider.Telephony;
24
25/**
26 * Base class declaring the specific methods and members for SmsMessage.
27 * {@hide}
28 */
29public abstract class SmsMessageBase {
30    private static final String LOG_TAG = "SMS";
31
32    /** {@hide} The address of the SMSC. May be null */
33    protected String scAddress;
34
35    /** {@hide} The address of the sender */
36    protected SmsAddress originatingAddress;
37
38    /** {@hide} The message body as a string. May be null if the message isn't text */
39    protected String messageBody;
40
41    /** {@hide} */
42    protected String pseudoSubject;
43
44    /** {@hide} Non-null if this is an email gateway message */
45    protected String emailFrom;
46
47    /** {@hide} Non-null if this is an email gateway message */
48    protected String emailBody;
49
50    /** {@hide} */
51    protected boolean isEmail;
52
53    /** {@hide} */
54    protected long scTimeMillis;
55
56    /** {@hide} The raw PDU of the message */
57    protected byte[] mPdu;
58
59    /** {@hide} The raw bytes for the user data section of the message */
60    protected byte[] userData;
61
62    /** {@hide} */
63    protected SmsHeader userDataHeader;
64
65    // "Message Waiting Indication Group"
66    // 23.038 Section 4
67    /** {@hide} */
68    protected boolean isMwi;
69
70    /** {@hide} */
71    protected boolean mwiSense;
72
73    /** {@hide} */
74    protected boolean mwiDontStore;
75
76    /**
77     * Indicates status for messages stored on the ICC.
78     */
79    protected int statusOnIcc = -1;
80
81    /**
82     * Record index of message in the EF.
83     */
84    protected int indexOnIcc = -1;
85
86    /** TP-Message-Reference - Message Reference of sent message. @hide */
87    public int messageRef;
88
89    /**
90     * For a specific text string, this object describes protocol
91     * properties of encoding it for transmission as message user
92     * data.
93     */
94    public static class TextEncodingDetails {
95        /**
96         *The number of SMS's required to encode the text.
97         */
98        public int msgCount;
99
100        /**
101         * The number of code units consumed so far, where code units
102         * are basically characters in the encoding -- for example,
103         * septets for the standard ASCII and GSM encodings, and 16
104         * bits for Unicode.
105         */
106        public int codeUnitCount;
107
108        /**
109         * How many code units are still available without spilling
110         * into an additional message.
111         */
112        public int codeUnitsRemaining;
113
114        /**
115         * The encoding code unit size (specified using
116         * android.telephony.SmsMessage ENCODING_*).
117         */
118        public int codeUnitSize;
119
120        /**
121         * The GSM national language table to use, or 0 for the default 7-bit alphabet.
122         */
123        public int languageTable;
124
125        /**
126         * The GSM national language shift table to use, or 0 for the default 7-bit extension table.
127         */
128        public int languageShiftTable;
129
130        @Override
131        public String toString() {
132            return "TextEncodingDetails " +
133                    "{ msgCount=" + msgCount +
134                    ", codeUnitCount=" + codeUnitCount +
135                    ", codeUnitsRemaining=" + codeUnitsRemaining +
136                    ", codeUnitSize=" + codeUnitSize +
137                    ", languageTable=" + languageTable +
138                    ", languageShiftTable=" + languageShiftTable +
139                    " }";
140        }
141    }
142
143    // TODO(): This class is duplicated in SmsMessage.java. Refactor accordingly.
144    public static abstract class SubmitPduBase  {
145        public byte[] encodedScAddress; // Null if not applicable.
146        public byte[] encodedMessage;
147
148        public String toString() {
149            return "SubmitPdu: encodedScAddress = "
150                    + Arrays.toString(encodedScAddress)
151                    + ", encodedMessage = "
152                    + Arrays.toString(encodedMessage);
153        }
154    }
155
156    /**
157     * Returns the address of the SMS service center that relayed this message
158     * or null if there is none.
159     */
160    public String getServiceCenterAddress() {
161        return scAddress;
162    }
163
164    /**
165     * Returns the originating address (sender) of this SMS message in String
166     * form or null if unavailable
167     */
168    public String getOriginatingAddress() {
169        if (originatingAddress == null) {
170            return null;
171        }
172
173        return originatingAddress.getAddressString();
174    }
175
176    /**
177     * Returns the originating address, or email from address if this message
178     * was from an email gateway. Returns null if originating address
179     * unavailable.
180     */
181    public String getDisplayOriginatingAddress() {
182        if (isEmail) {
183            return emailFrom;
184        } else {
185            return getOriginatingAddress();
186        }
187    }
188
189    /**
190     * Returns the message body as a String, if it exists and is text based.
191     * @return message body is there is one, otherwise null
192     */
193    public String getMessageBody() {
194        return messageBody;
195    }
196
197    /**
198     * Returns the class of this message.
199     */
200    public abstract MessageClass getMessageClass();
201
202    /**
203     * Returns the message body, or email message body if this message was from
204     * an email gateway. Returns null if message body unavailable.
205     */
206    public String getDisplayMessageBody() {
207        if (isEmail) {
208            return emailBody;
209        } else {
210            return getMessageBody();
211        }
212    }
213
214    /**
215     * Unofficial convention of a subject line enclosed in parens empty string
216     * if not present
217     */
218    public String getPseudoSubject() {
219        return pseudoSubject == null ? "" : pseudoSubject;
220    }
221
222    /**
223     * Returns the service centre timestamp in currentTimeMillis() format
224     */
225    public long getTimestampMillis() {
226        return scTimeMillis;
227    }
228
229    /**
230     * Returns true if message is an email.
231     *
232     * @return true if this message came through an email gateway and email
233     *         sender / subject / parsed body are available
234     */
235    public boolean isEmail() {
236        return isEmail;
237    }
238
239    /**
240     * @return if isEmail() is true, body of the email sent through the gateway.
241     *         null otherwise
242     */
243    public String getEmailBody() {
244        return emailBody;
245    }
246
247    /**
248     * @return if isEmail() is true, email from address of email sent through
249     *         the gateway. null otherwise
250     */
251    public String getEmailFrom() {
252        return emailFrom;
253    }
254
255    /**
256     * Get protocol identifier.
257     */
258    public abstract int getProtocolIdentifier();
259
260    /**
261     * See TS 23.040 9.2.3.9 returns true if this is a "replace short message"
262     * SMS
263     */
264    public abstract boolean isReplace();
265
266    /**
267     * Returns true for CPHS MWI toggle message.
268     *
269     * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section
270     *         B.4.2
271     */
272    public abstract boolean isCphsMwiMessage();
273
274    /**
275     * returns true if this message is a CPHS voicemail / message waiting
276     * indicator (MWI) clear message
277     */
278    public abstract boolean isMWIClearMessage();
279
280    /**
281     * returns true if this message is a CPHS voicemail / message waiting
282     * indicator (MWI) set message
283     */
284    public abstract boolean isMWISetMessage();
285
286    /**
287     * returns true if this message is a "Message Waiting Indication Group:
288     * Discard Message" notification and should not be stored.
289     */
290    public abstract boolean isMwiDontStore();
291
292    /**
293     * returns the user data section minus the user data header if one was
294     * present.
295     */
296    public byte[] getUserData() {
297        return userData;
298    }
299
300    /**
301     * Returns an object representing the user data header
302     *
303     * {@hide}
304     */
305    public SmsHeader getUserDataHeader() {
306        return userDataHeader;
307    }
308
309    /**
310     * TODO(cleanup): The term PDU is used in a seemingly non-unique
311     * manner -- for example, what is the difference between this byte
312     * array and the contents of SubmitPdu objects.  Maybe a more
313     * illustrative term would be appropriate.
314     */
315
316    /**
317     * Returns the raw PDU for the message.
318     */
319    public byte[] getPdu() {
320        return mPdu;
321    }
322
323    /**
324     * For an SMS-STATUS-REPORT message, this returns the status field from
325     * the status report.  This field indicates the status of a previously
326     * submitted SMS, if requested.  See TS 23.040, 9.2.3.15 TP-Status for a
327     * description of values.
328     *
329     * @return 0 indicates the previously sent message was received.
330     *         See TS 23.040, 9.9.2.3.15 for a description of other possible
331     *         values.
332     */
333    public abstract int getStatus();
334
335    /**
336     * Return true iff the message is a SMS-STATUS-REPORT message.
337     */
338    public abstract boolean isStatusReportMessage();
339
340    /**
341     * Returns true iff the <code>TP-Reply-Path</code> bit is set in
342     * this message.
343     */
344    public abstract boolean isReplyPathPresent();
345
346    /**
347     * Returns the status of the message on the ICC (read, unread, sent, unsent).
348     *
349     * @return the status of the message on the ICC.  These are:
350     *         SmsManager.STATUS_ON_ICC_FREE
351     *         SmsManager.STATUS_ON_ICC_READ
352     *         SmsManager.STATUS_ON_ICC_UNREAD
353     *         SmsManager.STATUS_ON_ICC_SEND
354     *         SmsManager.STATUS_ON_ICC_UNSENT
355     */
356    public int getStatusOnIcc() {
357        return statusOnIcc;
358    }
359
360    /**
361     * Returns the record index of the message on the ICC (1-based index).
362     * @return the record index of the message on the ICC, or -1 if this
363     *         SmsMessage was not created from a ICC SMS EF record.
364     */
365    public int getIndexOnIcc() {
366        return indexOnIcc;
367    }
368
369    protected void parseMessageBody() {
370        // originatingAddress could be null if this message is from a status
371        // report.
372        if (originatingAddress != null && originatingAddress.couldBeEmailGateway()) {
373            extractEmailAddressFromMessageBody();
374        }
375    }
376
377    /**
378     * Try to parse this message as an email gateway message
379     * There are two ways specified in TS 23.040 Section 3.8 :
380     *  - SMS message "may have its TP-PID set for Internet electronic mail - MT
381     * SMS format: [<from-address><space>]<message> - "Depending on the
382     * nature of the gateway, the destination/origination address is either
383     * derived from the content of the SMS TP-OA or TP-DA field, or the
384     * TP-OA/TP-DA field contains a generic gateway address and the to/from
385     * address is added at the beginning as shown above." (which is supported here)
386     * - Multiple addresses separated by commas, no spaces, Subject field delimited
387     * by '()' or '##' and '#' Section 9.2.3.24.11 (which are NOT supported here)
388     */
389    protected void extractEmailAddressFromMessageBody() {
390
391        /* Some carriers may use " /" delimiter as below
392         *
393         * 1. [x@y][ ]/[subject][ ]/[body]
394         * -or-
395         * 2. [x@y][ ]/[body]
396         */
397         String[] parts = messageBody.split("( /)|( )", 2);
398         if (parts.length < 2) return;
399         emailFrom = parts[0];
400         emailBody = parts[1];
401         isEmail = Telephony.Mms.isEmailAddress(emailFrom);
402    }
403
404}
405