1 /*
2  * Copyright (C) 2015 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 package com.android.phone.vvm.omtp.sms;
17 
18 import android.content.BroadcastReceiver;
19 import android.content.ContentUris;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.os.UserManager;
24 import android.provider.Telephony;
25 import android.provider.VoicemailContract;
26 import android.telecom.PhoneAccountHandle;
27 import android.telecom.Voicemail;
28 import android.telephony.SmsMessage;
29 import android.util.Log;
30 
31 import com.android.internal.telephony.PhoneConstants;
32 import com.android.phone.PhoneGlobals;
33 import com.android.phone.PhoneUtils;
34 import com.android.phone.settings.VisualVoicemailSettingsUtil;
35 import com.android.phone.vvm.omtp.LocalLogHelper;
36 import com.android.phone.vvm.omtp.OmtpConstants;
37 import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
38 import com.android.phone.vvm.omtp.sync.OmtpVvmSyncService;
39 import com.android.phone.vvm.omtp.sync.VoicemailsQueryHelper;
40 
41 /**
42  * Receive SMS messages and send for processing by the OMTP visual voicemail source.
43  */
44 public class OmtpMessageReceiver extends BroadcastReceiver {
45     private static final String TAG = "OmtpMessageReceiver";
46 
47     private Context mContext;
48     private PhoneAccountHandle mPhoneAccount;
49 
50     @Override
onReceive(Context context, Intent intent)51     public void onReceive(Context context, Intent intent) {
52         if (!UserManager.get(context).isUserUnlocked()) {
53             Log.i(TAG, "Received message on locked device");
54             // A full sync will happen after the device is unlocked, so nothing need to be done.
55             return;
56         }
57 
58         mContext = context;
59         mPhoneAccount = PhoneUtils.makePstnPhoneAccountHandle(
60                 intent.getExtras().getInt(PhoneConstants.PHONE_KEY));
61 
62         if (mPhoneAccount == null) {
63             Log.w(TAG, "Received message for null phone account");
64             return;
65         }
66 
67         if (!VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(mContext, mPhoneAccount)) {
68             Log.v(TAG, "Received vvm message for disabled vvm source.");
69             return;
70         }
71 
72         SmsMessage[] messages = Telephony.Sms.Intents.getMessagesFromIntent(intent);
73 
74         if (messages == null) {
75             Log.w(TAG, "Message does not exist in the intent.");
76             return;
77         }
78 
79         StringBuilder messageBody = new StringBuilder();
80 
81         for (int i = 0; i < messages.length; i++) {
82             if (messages[i].mWrappedSmsMessage != null) {
83                 messageBody.append(messages[i].getMessageBody());
84             }
85         }
86 
87         WrappedMessageData messageData = OmtpSmsParser.parse(messageBody.toString());
88         if (messageData != null) {
89             if (messageData.getPrefix() == OmtpConstants.SYNC_SMS_PREFIX) {
90                 SyncMessage message = new SyncMessage(messageData);
91 
92                 Log.v(TAG, "Received SYNC sms for " + mPhoneAccount.getId() +
93                         " with event " + message.getSyncTriggerEvent());
94                 LocalLogHelper.log(TAG, "Received SYNC sms for " + mPhoneAccount.getId() +
95                         " with event " + message.getSyncTriggerEvent());
96                 processSync(message);
97             } else if (messageData.getPrefix() == OmtpConstants.STATUS_SMS_PREFIX) {
98                 Log.v(TAG, "Received STATUS sms for " + mPhoneAccount.getId());
99                 LocalLogHelper.log(TAG, "Received Status sms for " + mPhoneAccount.getId());
100                 StatusMessage message = new StatusMessage(messageData);
101                 updateSource(message);
102             } else {
103                 Log.e(TAG, "This should never have happened");
104             }
105         }
106         // Let this fall through: this is not a message we're interested in.
107     }
108 
109     /**
110      * A sync message has two purposes: to signal a new voicemail message, and to indicate the
111      * voicemails on the server have changed remotely (usually through the TUI). Save the new
112      * message to the voicemail provider if it is the former case and perform a full sync in the
113      * latter case.
114      *
115      * @param message The sync message to extract data from.
116      */
processSync(SyncMessage message)117     private void processSync(SyncMessage message) {
118         Intent serviceIntent = null;
119         switch (message.getSyncTriggerEvent()) {
120             case OmtpConstants.NEW_MESSAGE:
121                 Voicemail.Builder builder = Voicemail.createForInsertion(
122                         message.getTimestampMillis(), message.getSender())
123                         .setPhoneAccount(mPhoneAccount)
124                         .setSourceData(message.getId())
125                         .setDuration(message.getLength())
126                         .setSourcePackage(mContext.getPackageName());
127                 Voicemail voicemail = builder.build();
128 
129                 VoicemailsQueryHelper queryHelper = new VoicemailsQueryHelper(mContext);
130                 if (queryHelper.isVoicemailUnique(voicemail)) {
131                     Uri uri = VoicemailContract.Voicemails.insert(mContext, voicemail);
132                     voicemail = builder.setId(ContentUris.parseId(uri)).setUri(uri).build();
133                     serviceIntent = OmtpVvmSyncService.getSyncIntent(mContext,
134                             OmtpVvmSyncService.SYNC_DOWNLOAD_ONE_TRANSCRIPTION, mPhoneAccount,
135                             voicemail, true /* firstAttempt */);
136                 }
137                 break;
138             case OmtpConstants.MAILBOX_UPDATE:
139                 serviceIntent = OmtpVvmSyncService.getSyncIntent(
140                         mContext, OmtpVvmSyncService.SYNC_DOWNLOAD_ONLY, mPhoneAccount,
141                         true /* firstAttempt */);
142                 break;
143             case OmtpConstants.GREETINGS_UPDATE:
144                 // Not implemented in V1
145                 break;
146             default:
147                Log.e(TAG, "Unrecognized sync trigger event: " + message.getSyncTriggerEvent());
148                break;
149         }
150 
151         if (serviceIntent != null) {
152             mContext.startService(serviceIntent);
153         }
154     }
155 
updateSource(StatusMessage message)156     private void updateSource(StatusMessage message) {
157         OmtpVvmSourceManager vvmSourceManager =
158                 OmtpVvmSourceManager.getInstance(mContext);
159 
160         if (OmtpConstants.SUCCESS.equals(message.getReturnCode())) {
161             VoicemailContract.Status.setStatus(mContext, mPhoneAccount,
162                     VoicemailContract.Status.CONFIGURATION_STATE_OK,
163                     VoicemailContract.Status.DATA_CHANNEL_STATE_OK,
164                     VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK);
165 
166             // Save the IMAP credentials in preferences so they are persistent and can be retrieved.
167             VisualVoicemailSettingsUtil.setVisualVoicemailCredentialsFromStatusMessage(
168                     mContext,
169                     mPhoneAccount,
170                     message);
171 
172             // Add the source to indicate that it is active.
173             vvmSourceManager.addSource(mPhoneAccount);
174 
175             Intent serviceIntent = OmtpVvmSyncService.getSyncIntent(
176                     mContext, OmtpVvmSyncService.SYNC_FULL_SYNC, mPhoneAccount,
177                     true /* firstAttempt */);
178             mContext.startService(serviceIntent);
179 
180             PhoneGlobals.getInstance().clearMwiIndicator(
181                     PhoneUtils.getSubIdForPhoneAccountHandle(mPhoneAccount));
182         } else {
183             Log.w(TAG, "Visual voicemail not available for subscriber.");
184             // Override default isEnabled setting to false since visual voicemail is unable to
185             // be accessed for some reason.
186             VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(mContext, mPhoneAccount,
187                     /* isEnabled */ false, /* isUserSet */ true);
188         }
189     }
190 }
191