1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/* 2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project 3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License"); 5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License. 6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at 7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * http://www.apache.org/licenses/LICENSE-2.0 9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software 11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS, 12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and 14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License. 15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.sms; 18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context; 20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.Cursor; 21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.Uri; 22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.provider.BaseColumns; 23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils; 24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.util.Patterns; 25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.SqliteWrapper; 27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil; 28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.HashSet; 30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Set; 31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.regex.Matcher; 32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.regex.Pattern; 33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/** 35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Utility functions for the Messaging Service 36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class MmsSmsUtils { 38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private MmsSmsUtils() { 39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Forbidden being instantiated. 40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // An alias (or commonly called "nickname") is: 43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Nickname must begin with a letter. 44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Only letters a-z, numbers 0-9, or . are allowed in Nickname field. 45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean isAlias(final String string, final int subId) { 46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!MmsConfig.get(subId).isAliasEnabled()) { 47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int len = string == null ? 0 : string.length(); 51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (len < MmsConfig.get(subId).getAliasMinChars() || 53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd len > MmsConfig.get(subId).getAliasMaxChars()) { 54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!Character.isLetter(string.charAt(0))) { // Nickname begins with a letter 58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 1; i < len; i++) { 61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final char c = string.charAt(i); 62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!(Character.isLetterOrDigit(c) || c == '.')) { 63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * mailbox = name-addr 72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * name-addr = [display-name] angle-addr 73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * angle-addr = [CFWS] "<" addr-spec ">" [CFWS] 74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final Pattern NAME_ADDR_EMAIL_PATTERN = 76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*"); 77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String extractAddrSpec(final String address) { 79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Matcher match = NAME_ADDR_EMAIL_PATTERN.matcher(address); 80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (match.matches()) { 82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return match.group(2); 83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return address; 85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Returns true if the address is an email address 89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param address the input address to be tested 91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return true if address is an email address 92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean isEmailAddress(final String address) { 94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.isEmpty(address)) { 95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String s = extractAddrSpec(address); 99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Matcher match = Patterns.EMAIL_ADDRESS.matcher(s); 100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return match.matches(); 101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Returns true if the number is a Phone number 105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param number the input number to be tested 107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return true if number is a Phone number 108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean isPhoneNumber(final String number) { 110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.isEmpty(number)) { 111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Matcher match = Patterns.PHONE.matcher(number); 115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return match.matches(); 116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Check if MMS is required when sending to email address 120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param destinationHasEmailAddress destination includes an email address 122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return true if MMS is required. 123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean getRequireMmsForEmailAddress(final boolean destinationHasEmailAddress, 125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int subId) { 126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(MmsConfig.get(subId).getEmailGateway())) { 127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return destinationHasEmailAddress; 130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Helper functions for the "threads" table used by MMS and SMS. 135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final class Threads implements android.provider.Telephony.ThreadsColumns { 137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String[] ID_PROJECTION = { BaseColumns._ID }; 138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final Uri THREAD_ID_CONTENT_URI = Uri.parse( 139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "content://mms-sms/threadID"); 140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final Uri CONTENT_URI = Uri.withAppendedPath( 141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd android.provider.Telephony.MmsSms.CONTENT_URI, "conversations"); 142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // No one should construct an instance of this class. 144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private Threads() { 145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * This is a single-recipient version of 149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * getOrCreateThreadId. It's convenient for use with SMS 150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * messages. 151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static long getOrCreateThreadId(final Context context, final String recipient) { 153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Set<String> recipients = new HashSet<String>(); 154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipients.add(recipient); 156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return getOrCreateThreadId(context, recipients); 157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Given the recipients list and subject of an unsaved message, 161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * return its thread ID. If the message starts a new thread, 162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * allocate a new thread ID. Otherwise, use the appropriate 163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * existing thread ID. 164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Find the thread ID of the same set of recipients (in 166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * any order, without any additions). If one 167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * is found, return it. Otherwise, return a unique thread ID. 168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static long getOrCreateThreadId( 170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context, final Set<String> recipients) { 171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon(); 172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (String recipient : recipients) { 174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (isEmailAddress(recipient)) { 175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipient = extractAddrSpec(recipient); 176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd uriBuilder.appendQueryParameter("recipient", recipient); 179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri uri = uriBuilder.build(); 182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //if (DEBUG) Rlog.v(TAG, "getOrCreateThreadId uri: " + uri); 183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(), 185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd uri, ID_PROJECTION, null, null, null); 186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return cursor.getLong(0); 190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(LogUtil.BUGLE_DATAMODEL_TAG, 192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "getOrCreateThreadId returned no rows!"); 193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(LogUtil.BUGLE_DATAMODEL_TAG, "getOrCreateThreadId failed with " 200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + LogUtil.sanitizePII(recipients.toString())); 201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd throw new IllegalArgumentException("Unable to find or allocate a thread ID."); 202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd} 205