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