13081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen/* 23081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * Copyright (C) 2016 The Android Open Source Project 33081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * 43081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * Licensed under the Apache License, Version 2.0 (the "License"); 53081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * you may not use this file except in compliance with the License. 63081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * You may obtain a copy of the License at 73081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * 83081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * http://www.apache.org/licenses/LICENSE-2.0 93081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * 103081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * Unless required by applicable law or agreed to in writing, software 113081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * distributed under the License is distributed on an "AS IS" BASIS, 123081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * See the License for the specific language governing permissions and 143081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * limitations under the License 153081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen */ 163081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yenpackage com.android.internal.telephony; 173081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen 18585c46499c06297f19129a024047e2feacd30753Ta-wei Yenimport android.annotation.Nullable; 193081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yenimport android.content.Context; 203081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yenimport android.content.Intent; 213081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yenimport android.provider.VoicemailContract; 229452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yenimport android.telephony.SmsMessage; 233081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yenimport android.telephony.TelephonyManager; 245580eae0ac10c1b38aae54b6e9f45b4259f409e2Ta-wei Yenimport android.telephony.VisualVoicemailSmsFilterSettings; 25585c46499c06297f19129a024047e2feacd30753Ta-wei Yenimport android.util.ArrayMap; 263081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yenimport android.util.Log; 273081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yenimport com.android.internal.telephony.VisualVoicemailSmsParser.WrappedMessageData; 289452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yenimport java.nio.charset.StandardCharsets; 29585c46499c06297f19129a024047e2feacd30753Ta-wei Yenimport java.util.ArrayList; 30585c46499c06297f19129a024047e2feacd30753Ta-wei Yenimport java.util.List; 31585c46499c06297f19129a024047e2feacd30753Ta-wei Yenimport java.util.Map; 32585c46499c06297f19129a024047e2feacd30753Ta-wei Yenimport java.util.regex.Pattern; 339452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen 343081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yenpublic class VisualVoicemailSmsFilter { 353081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen 363081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen private static final String TAG = "VvmSmsFilter"; 373081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen 383081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen private static final String SYSTEM_VVM_CLIENT_PACKAGE = "com.android.phone"; 395580eae0ac10c1b38aae54b6e9f45b4259f409e2Ta-wei Yen 40585c46499c06297f19129a024047e2feacd30753Ta-wei Yen private static Map<String, List<Pattern>> sPatterns; 41585c46499c06297f19129a024047e2feacd30753Ta-wei Yen 423081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen /** 433081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * Attempt to parse the incoming SMS as a visual voicemail SMS. If the parsing succeeded, A 443081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * {@link VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED} intent will be sent to the visual 453081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * voicemail client, and the SMS should be dropped. 463081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * 473081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * <p>The accepted format for a visual voicemail SMS is a generalization of the OMTP format: 483081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * 493081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * <p>[clientPrefix]:[prefix]:([key]=[value];)* 503081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * 51585c46499c06297f19129a024047e2feacd30753Ta-wei Yen * Additionally, if the SMS does not match the format, but matches the regex specified by the 52585c46499c06297f19129a024047e2feacd30753Ta-wei Yen * carrier in {@link com.android.internal.R.array.config_vvmSmsFilterRegexes}, the SMS will 53585c46499c06297f19129a024047e2feacd30753Ta-wei Yen * still be dropped and a {@link VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED} with {@link 54585c46499c06297f19129a024047e2feacd30753Ta-wei Yen * VoicemailContract#EXTRA_VOICEMAIL_SMS_MESSAGE_BODY} will be sent. 55585c46499c06297f19129a024047e2feacd30753Ta-wei Yen * 563081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen * @return true if the SMS has been parsed to be a visual voicemail SMS and should be dropped 573081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen */ 583081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen public static boolean filter(Context context, byte[][] pdus, String format, int destPort, 593081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen int subId) { 6054543184e3fd345fe0c96d4d96022ad4f058e410Amit Mahajan TelephonyManager telephonyManager = 6154543184e3fd345fe0c96d4d96022ad4f058e410Amit Mahajan (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 623081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen 633081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen // TODO: select client package. 643081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen String vvmClientPackage = SYSTEM_VVM_CLIENT_PACKAGE; 653081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen 665580eae0ac10c1b38aae54b6e9f45b4259f409e2Ta-wei Yen VisualVoicemailSmsFilterSettings settings = 675580eae0ac10c1b38aae54b6e9f45b4259f409e2Ta-wei Yen telephonyManager.getVisualVoicemailSmsFilterSettings(vvmClientPackage, subId); 685580eae0ac10c1b38aae54b6e9f45b4259f409e2Ta-wei Yen if (settings == null) { 693081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen return false; 703081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen } 713081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen // TODO: filter base on originating number and destination port. 723081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen 733081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen String messageBody = getFullMessage(pdus, format); 749452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen 75649d6ef94525802e2911d4063431e3207e452810Ta-wei Yen if(messageBody == null){ 769452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen // Verizon WAP push SMS is not recognized by android, which has a ascii PDU. 779452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen // Attempt to parse it. 789452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen Log.i(TAG, "Unparsable SMS received"); 799452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen String asciiMessage = parseAsciiPduMessage(pdus); 809452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen WrappedMessageData messageData = VisualVoicemailSmsParser 819452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen .parseAlternativeFormat(asciiMessage); 829452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen if (messageData != null) { 83585c46499c06297f19129a024047e2feacd30753Ta-wei Yen sendVvmSmsBroadcast(context, vvmClientPackage, subId, messageData, null); 849452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen } 859452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen // Confidence for what the message actually is is low. Don't remove the message and let 869452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen // system decide. Usually because it is not parsable it will be dropped. 87649d6ef94525802e2911d4063431e3207e452810Ta-wei Yen return false; 88585c46499c06297f19129a024047e2feacd30753Ta-wei Yen } 89585c46499c06297f19129a024047e2feacd30753Ta-wei Yen String clientPrefix = settings.clientPrefix; 90585c46499c06297f19129a024047e2feacd30753Ta-wei Yen WrappedMessageData messageData = VisualVoicemailSmsParser 91585c46499c06297f19129a024047e2feacd30753Ta-wei Yen .parse(clientPrefix, messageBody); 92585c46499c06297f19129a024047e2feacd30753Ta-wei Yen if (messageData != null) { 93585c46499c06297f19129a024047e2feacd30753Ta-wei Yen sendVvmSmsBroadcast(context, vvmClientPackage, subId, messageData, null); 94585c46499c06297f19129a024047e2feacd30753Ta-wei Yen return true; 95585c46499c06297f19129a024047e2feacd30753Ta-wei Yen } 96585c46499c06297f19129a024047e2feacd30753Ta-wei Yen 97585c46499c06297f19129a024047e2feacd30753Ta-wei Yen buildPatternsMap(context); 98585c46499c06297f19129a024047e2feacd30753Ta-wei Yen String mccMnc = telephonyManager.getSimOperator(subId); 99585c46499c06297f19129a024047e2feacd30753Ta-wei Yen 100585c46499c06297f19129a024047e2feacd30753Ta-wei Yen List<Pattern> patterns = sPatterns.get(mccMnc); 101585c46499c06297f19129a024047e2feacd30753Ta-wei Yen if (patterns == null || patterns.isEmpty()) { 102585c46499c06297f19129a024047e2feacd30753Ta-wei Yen return false; 103585c46499c06297f19129a024047e2feacd30753Ta-wei Yen } 104585c46499c06297f19129a024047e2feacd30753Ta-wei Yen 105585c46499c06297f19129a024047e2feacd30753Ta-wei Yen for (Pattern pattern : patterns) { 106585c46499c06297f19129a024047e2feacd30753Ta-wei Yen if (pattern.matcher(messageBody).matches()) { 107585c46499c06297f19129a024047e2feacd30753Ta-wei Yen Log.w(TAG, "Incoming SMS matches pattern " + pattern + " but has illegal format, " 108585c46499c06297f19129a024047e2feacd30753Ta-wei Yen + "still dropping as VVM SMS"); 109585c46499c06297f19129a024047e2feacd30753Ta-wei Yen sendVvmSmsBroadcast(context, vvmClientPackage, subId, null, messageBody); 1109452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen return true; 1119452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen } 112649d6ef94525802e2911d4063431e3207e452810Ta-wei Yen } 1133081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen return false; 1143081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen } 1153081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen 116585c46499c06297f19129a024047e2feacd30753Ta-wei Yen private static void buildPatternsMap(Context context) { 117585c46499c06297f19129a024047e2feacd30753Ta-wei Yen if (sPatterns != null) { 118585c46499c06297f19129a024047e2feacd30753Ta-wei Yen return; 119585c46499c06297f19129a024047e2feacd30753Ta-wei Yen } 120585c46499c06297f19129a024047e2feacd30753Ta-wei Yen sPatterns = new ArrayMap<>(); 121585c46499c06297f19129a024047e2feacd30753Ta-wei Yen // TODO(twyen): build from CarrierConfig once public API can be updated. 122585c46499c06297f19129a024047e2feacd30753Ta-wei Yen for (String entry : context.getResources() 123585c46499c06297f19129a024047e2feacd30753Ta-wei Yen .getStringArray(com.android.internal.R.array.config_vvmSmsFilterRegexes)) { 124585c46499c06297f19129a024047e2feacd30753Ta-wei Yen String[] mccMncList = entry.split(";")[0].split(","); 125585c46499c06297f19129a024047e2feacd30753Ta-wei Yen Pattern pattern = Pattern.compile(entry.split(";")[1]); 126585c46499c06297f19129a024047e2feacd30753Ta-wei Yen 127585c46499c06297f19129a024047e2feacd30753Ta-wei Yen for (String mccMnc : mccMncList) { 128585c46499c06297f19129a024047e2feacd30753Ta-wei Yen if (!sPatterns.containsKey(mccMnc)) { 129585c46499c06297f19129a024047e2feacd30753Ta-wei Yen sPatterns.put(mccMnc, new ArrayList<>()); 130585c46499c06297f19129a024047e2feacd30753Ta-wei Yen } 131585c46499c06297f19129a024047e2feacd30753Ta-wei Yen sPatterns.get(mccMnc).add(pattern); 132585c46499c06297f19129a024047e2feacd30753Ta-wei Yen } 133585c46499c06297f19129a024047e2feacd30753Ta-wei Yen } 134585c46499c06297f19129a024047e2feacd30753Ta-wei Yen } 135585c46499c06297f19129a024047e2feacd30753Ta-wei Yen 1369452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen private static void sendVvmSmsBroadcast(Context context, String vvmClientPackage, int subId, 137585c46499c06297f19129a024047e2feacd30753Ta-wei Yen @Nullable WrappedMessageData messageData, @Nullable String messageBody) { 1389452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen Log.i(TAG, "VVM SMS received"); 1399452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen Intent intent = new Intent(VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED); 140585c46499c06297f19129a024047e2feacd30753Ta-wei Yen if (messageData != null) { 141585c46499c06297f19129a024047e2feacd30753Ta-wei Yen intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_PREFIX, messageData.prefix); 142585c46499c06297f19129a024047e2feacd30753Ta-wei Yen intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS, messageData.fields); 143585c46499c06297f19129a024047e2feacd30753Ta-wei Yen } 144585c46499c06297f19129a024047e2feacd30753Ta-wei Yen if (messageBody != null) { 145585c46499c06297f19129a024047e2feacd30753Ta-wei Yen intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_MESSAGE_BODY, messageBody); 146585c46499c06297f19129a024047e2feacd30753Ta-wei Yen } 1479452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID, subId); 1489452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen intent.setPackage(vvmClientPackage); 1499452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen context.sendBroadcast(intent); 1509452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen } 1519452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen 152585c46499c06297f19129a024047e2feacd30753Ta-wei Yen /** 153585c46499c06297f19129a024047e2feacd30753Ta-wei Yen * @return the message body of the SMS, or {@code null} if it can not be parsed. 154585c46499c06297f19129a024047e2feacd30753Ta-wei Yen */ 155585c46499c06297f19129a024047e2feacd30753Ta-wei Yen @Nullable 1563081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen private static String getFullMessage(byte[][] pdus, String format) { 1573081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen StringBuilder builder = new StringBuilder(); 1583081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen for (byte pdu[] : pdus) { 159585c46499c06297f19129a024047e2feacd30753Ta-wei Yen SmsMessage message = SmsMessage.createFromPdu(pdu, format); 160649d6ef94525802e2911d4063431e3207e452810Ta-wei Yen 1619452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen if (message == null) { 1629452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen // The PDU is not recognized by android 163649d6ef94525802e2911d4063431e3207e452810Ta-wei Yen return null; 164649d6ef94525802e2911d4063431e3207e452810Ta-wei Yen } 165649d6ef94525802e2911d4063431e3207e452810Ta-wei Yen String body = message.getMessageBody(); 1663081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen if (body != null) { 1673081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen builder.append(body); 1683081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen } 1693081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen } 1703081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen return builder.toString(); 1713081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen } 1729452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen 1739452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen private static String parseAsciiPduMessage(byte[][] pdus) { 1749452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen StringBuilder builder = new StringBuilder(); 1759452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen for (byte pdu[] : pdus) { 1769452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen builder.append(new String(pdu, StandardCharsets.US_ASCII)); 1779452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen } 1789452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen return builder.toString(); 1799452da00610c4d8c4378ee112de88214d4ec4383Ta-wei Yen } 1803081824ff4942b9bd19db7a0d9918e188c7f69d1Ta-wei Yen} 181