• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.fetch;
17 
18 import android.annotation.Nullable;
19 import android.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.database.Cursor;
25 import android.net.ConnectivityManager;
26 import android.net.Network;
27 import android.net.NetworkRequest;
28 import android.net.Uri;
29 import android.provider.VoicemailContract;
30 import android.provider.VoicemailContract.Voicemails;
31 import android.telecom.PhoneAccountHandle;
32 import android.telephony.TelephonyManager;
33 import android.text.TextUtils;
34 import com.android.internal.telephony.Phone;
35 import com.android.phone.PhoneUtils;
36 import com.android.phone.VoicemailStatus;
37 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
38 import com.android.phone.vvm.omtp.VvmLog;
39 import com.android.phone.vvm.omtp.imap.ImapHelper;
40 import com.android.phone.vvm.omtp.imap.ImapHelper.InitializingException;
41 import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
42 import com.android.phone.vvm.omtp.sync.VvmNetworkRequestCallback;
43 import java.util.concurrent.Executor;
44 import java.util.concurrent.Executors;
45 
46 public class FetchVoicemailReceiver extends BroadcastReceiver {
47 
48     private static final String TAG = "FetchVoicemailReceiver";
49 
50     final static String[] PROJECTION = new String[]{
51             Voicemails.SOURCE_DATA,      // 0
52             Voicemails.PHONE_ACCOUNT_ID, // 1
53             Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, // 2
54     };
55 
56     public static final int SOURCE_DATA = 0;
57     public static final int PHONE_ACCOUNT_ID = 1;
58     public static final int PHONE_ACCOUNT_COMPONENT_NAME = 2;
59 
60     // Timeout used to call ConnectivityManager.requestNetwork
61     private static final int NETWORK_REQUEST_TIMEOUT_MILLIS = 60 * 1000;
62 
63     // Number of retries
64     private static final int NETWORK_RETRY_COUNT = 3;
65 
66     private ContentResolver mContentResolver;
67     private Uri mUri;
68     private NetworkRequest mNetworkRequest;
69     private VvmNetworkRequestCallback mNetworkCallback;
70     private Context mContext;
71     private String mUid;
72     private ConnectivityManager mConnectivityManager;
73     private PhoneAccountHandle mPhoneAccount;
74     private int mRetryCount = NETWORK_RETRY_COUNT;
75 
76     @Override
onReceive(final Context context, Intent intent)77     public void onReceive(final Context context, Intent intent) {
78         if (VoicemailContract.ACTION_FETCH_VOICEMAIL.equals(intent.getAction())) {
79             VvmLog.i(TAG, "ACTION_FETCH_VOICEMAIL received");
80             mContext = context;
81             mContentResolver = context.getContentResolver();
82             mUri = intent.getData();
83 
84             if (mUri == null) {
85                 VvmLog.w(TAG,
86                         VoicemailContract.ACTION_FETCH_VOICEMAIL + " intent sent with no data");
87                 return;
88             }
89 
90             if (!context.getPackageName().equals(
91                     mUri.getQueryParameter(VoicemailContract.PARAM_KEY_SOURCE_PACKAGE))) {
92                 // Ignore if the fetch request is for a voicemail not from this package.
93                 VvmLog.e(TAG,
94                         "ACTION_FETCH_VOICEMAIL from foreign pacakge " + context.getPackageName());
95                 return;
96             }
97 
98             Cursor cursor = mContentResolver.query(mUri, PROJECTION, null, null, null);
99             if (cursor == null) {
100                 VvmLog.i(TAG, "ACTION_FETCH_VOICEMAIL query returned null");
101                 return;
102             }
103             try {
104                 if (cursor.moveToFirst()) {
105                     mUid = cursor.getString(SOURCE_DATA);
106                     String accountId = cursor.getString(PHONE_ACCOUNT_ID);
107                     if (TextUtils.isEmpty(accountId)) {
108                         TelephonyManager telephonyManager = (TelephonyManager)
109                                 context.getSystemService(Context.TELEPHONY_SERVICE);
110                         accountId = telephonyManager.getSimSerialNumber();
111 
112                         if (TextUtils.isEmpty(accountId)) {
113                             VvmLog.e(TAG, "Account null and no default sim found.");
114                             return;
115                         }
116                     }
117 
118                     mPhoneAccount = new PhoneAccountHandle(
119                             ComponentName.unflattenFromString(
120                                     cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME)),
121                             cursor.getString(PHONE_ACCOUNT_ID));
122                     if (!OmtpVvmSourceManager.getInstance(context)
123                             .isVvmSourceRegistered(mPhoneAccount)) {
124                         mPhoneAccount = getAccountFromMarshmallowAccount(context, mPhoneAccount);
125                         if (mPhoneAccount == null) {
126                             VvmLog.w(TAG, "Account not registered - cannot retrieve message.");
127                             return;
128                         }
129                         VvmLog.i(TAG, "Fetching voicemail with Marshmallow PhoneAccountHandle");
130                     }
131 
132                     int subId = PhoneUtils.getSubIdForPhoneAccountHandle(mPhoneAccount);
133                     OmtpVvmCarrierConfigHelper carrierConfigHelper =
134                             new OmtpVvmCarrierConfigHelper(context, subId);
135                     VvmLog.i(TAG, "Requesting network to fetch voicemail");
136                     mNetworkCallback = new fetchVoicemailNetworkRequestCallback(context,
137                             mPhoneAccount);
138                     mNetworkCallback.requestNetwork();
139                 }
140             } finally {
141                 cursor.close();
142             }
143         }
144     }
145 
146     /**
147      * In ag/930496 the format of PhoneAccountHandle has changed between Marshmallow and Nougat.
148      * This method attempts to search the account from the old database in registered sources using
149      * the old format. There's a chance of M phone account collisions on multi-SIM devices, but
150      * visual voicemail is not supported on M multi-SIM.
151      */
152     @Nullable
getAccountFromMarshmallowAccount(Context context, PhoneAccountHandle oldAccount)153     private static PhoneAccountHandle getAccountFromMarshmallowAccount(Context context,
154             PhoneAccountHandle oldAccount) {
155         for (PhoneAccountHandle handle : OmtpVvmSourceManager.getInstance(context)
156                 .getOmtpVvmSources()) {
157             Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(handle);
158             if (phone == null) {
159                 continue;
160             }
161             // getIccSerialNumber() is used for ID before N, and getFullIccSerialNumber() after.
162             if (phone.getIccSerialNumber().equals(oldAccount.getId())) {
163                 return handle;
164             }
165         }
166         return null;
167     }
168 
169     private class fetchVoicemailNetworkRequestCallback extends VvmNetworkRequestCallback {
170 
fetchVoicemailNetworkRequestCallback(Context context, PhoneAccountHandle phoneAccount)171         public fetchVoicemailNetworkRequestCallback(Context context,
172                 PhoneAccountHandle phoneAccount) {
173             super(context, phoneAccount, VoicemailStatus.edit(context, phoneAccount));
174         }
175 
176         @Override
onAvailable(final Network network)177         public void onAvailable(final Network network) {
178             super.onAvailable(network);
179             fetchVoicemail(network, getVoicemailStatusEditor());
180         }
181     }
182 
fetchVoicemail(final Network network, final VoicemailStatus.Editor status)183     private void fetchVoicemail(final Network network, final VoicemailStatus.Editor status) {
184         Executor executor = Executors.newCachedThreadPool();
185         executor.execute(new Runnable() {
186             @Override
187             public void run() {
188                 try {
189                     while (mRetryCount > 0) {
190                         VvmLog.i(TAG, "fetching voicemail, retry count=" + mRetryCount);
191                         try (ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount,
192                             network, status)) {
193                             boolean success = imapHelper.fetchVoicemailPayload(
194                                     new VoicemailFetchedCallback(mContext, mUri, mPhoneAccount),
195                                     mUid);
196                             if (!success && mRetryCount > 0) {
197                                 VvmLog.i(TAG, "fetch voicemail failed, retrying");
198                                 mRetryCount--;
199                             } else {
200                                 return;
201                             }
202                         } catch (InitializingException e) {
203                           VvmLog.w(TAG, "Can't retrieve Imap credentials ", e);
204                             return;
205                         }
206                     }
207                 } finally {
208                     if (mNetworkCallback != null) {
209                         mNetworkCallback.releaseNetwork();
210                     }
211                 }
212             }
213         });
214     }
215 }
216