| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| package com.android.phone.vvm.omtp.fetch; |
| |
| import android.annotation.Nullable; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.database.Cursor; |
| import android.net.ConnectivityManager; |
| import android.net.Network; |
| import android.net.NetworkRequest; |
| import android.net.Uri; |
| import android.provider.VoicemailContract; |
| import android.provider.VoicemailContract.Voicemails; |
| import android.telecom.PhoneAccountHandle; |
| import android.telephony.TelephonyManager; |
| import android.text.TextUtils; |
| import com.android.internal.telephony.Phone; |
| import com.android.phone.PhoneUtils; |
| import com.android.phone.VoicemailStatus; |
| import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper; |
| import com.android.phone.vvm.omtp.VvmLog; |
| import com.android.phone.vvm.omtp.imap.ImapHelper; |
| import com.android.phone.vvm.omtp.imap.ImapHelper.InitializingException; |
| import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager; |
| import com.android.phone.vvm.omtp.sync.VvmNetworkRequestCallback; |
| import java.util.concurrent.Executor; |
| import java.util.concurrent.Executors; |
| |
| public class FetchVoicemailReceiver extends BroadcastReceiver { |
| |
| private static final String TAG = "FetchVoicemailReceiver"; |
| |
| final static String[] PROJECTION = new String[]{ |
| Voicemails.SOURCE_DATA, // 0 |
| Voicemails.PHONE_ACCOUNT_ID, // 1 |
| Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, // 2 |
| }; |
| |
| public static final int SOURCE_DATA = 0; |
| public static final int PHONE_ACCOUNT_ID = 1; |
| public static final int PHONE_ACCOUNT_COMPONENT_NAME = 2; |
| |
| // Timeout used to call ConnectivityManager.requestNetwork |
| private static final int NETWORK_REQUEST_TIMEOUT_MILLIS = 60 * 1000; |
| |
| // Number of retries |
| private static final int NETWORK_RETRY_COUNT = 3; |
| |
| private ContentResolver mContentResolver; |
| private Uri mUri; |
| private NetworkRequest mNetworkRequest; |
| private VvmNetworkRequestCallback mNetworkCallback; |
| private Context mContext; |
| private String mUid; |
| private ConnectivityManager mConnectivityManager; |
| private PhoneAccountHandle mPhoneAccount; |
| private int mRetryCount = NETWORK_RETRY_COUNT; |
| |
| @Override |
| public void onReceive(final Context context, Intent intent) { |
| if (VoicemailContract.ACTION_FETCH_VOICEMAIL.equals(intent.getAction())) { |
| VvmLog.i(TAG, "ACTION_FETCH_VOICEMAIL received"); |
| mContext = context; |
| mContentResolver = context.getContentResolver(); |
| mUri = intent.getData(); |
| |
| if (mUri == null) { |
| VvmLog.w(TAG, |
| VoicemailContract.ACTION_FETCH_VOICEMAIL + " intent sent with no data"); |
| return; |
| } |
| |
| if (!context.getPackageName().equals( |
| mUri.getQueryParameter(VoicemailContract.PARAM_KEY_SOURCE_PACKAGE))) { |
| // Ignore if the fetch request is for a voicemail not from this package. |
| VvmLog.e(TAG, |
| "ACTION_FETCH_VOICEMAIL from foreign pacakge " + context.getPackageName()); |
| return; |
| } |
| |
| Cursor cursor = mContentResolver.query(mUri, PROJECTION, null, null, null); |
| if (cursor == null) { |
| VvmLog.i(TAG, "ACTION_FETCH_VOICEMAIL query returned null"); |
| return; |
| } |
| try { |
| if (cursor.moveToFirst()) { |
| mUid = cursor.getString(SOURCE_DATA); |
| String accountId = cursor.getString(PHONE_ACCOUNT_ID); |
| if (TextUtils.isEmpty(accountId)) { |
| TelephonyManager telephonyManager = (TelephonyManager) |
| context.getSystemService(Context.TELEPHONY_SERVICE); |
| accountId = telephonyManager.getSimSerialNumber(); |
| |
| if (TextUtils.isEmpty(accountId)) { |
| VvmLog.e(TAG, "Account null and no default sim found."); |
| return; |
| } |
| } |
| |
| mPhoneAccount = new PhoneAccountHandle( |
| ComponentName.unflattenFromString( |
| cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME)), |
| cursor.getString(PHONE_ACCOUNT_ID)); |
| if (!OmtpVvmSourceManager.getInstance(context) |
| .isVvmSourceRegistered(mPhoneAccount)) { |
| mPhoneAccount = getAccountFromMarshmallowAccount(context, mPhoneAccount); |
| if (mPhoneAccount == null) { |
| VvmLog.w(TAG, "Account not registered - cannot retrieve message."); |
| return; |
| } |
| VvmLog.i(TAG, "Fetching voicemail with Marshmallow PhoneAccountHandle"); |
| } |
| |
| int subId = PhoneUtils.getSubIdForPhoneAccountHandle(mPhoneAccount); |
| OmtpVvmCarrierConfigHelper carrierConfigHelper = |
| new OmtpVvmCarrierConfigHelper(context, subId); |
| VvmLog.i(TAG, "Requesting network to fetch voicemail"); |
| mNetworkCallback = new fetchVoicemailNetworkRequestCallback(context, |
| mPhoneAccount); |
| mNetworkCallback.requestNetwork(); |
| } |
| } finally { |
| cursor.close(); |
| } |
| } |
| } |
| |
| /** |
| * In ag/930496 the format of PhoneAccountHandle has changed between Marshmallow and Nougat. |
| * This method attempts to search the account from the old database in registered sources using |
| * the old format. There's a chance of M phone account collisions on multi-SIM devices, but |
| * visual voicemail is not supported on M multi-SIM. |
| */ |
| @Nullable |
| private static PhoneAccountHandle getAccountFromMarshmallowAccount(Context context, |
| PhoneAccountHandle oldAccount) { |
| for (PhoneAccountHandle handle : OmtpVvmSourceManager.getInstance(context) |
| .getOmtpVvmSources()) { |
| Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(handle); |
| if (phone == null) { |
| continue; |
| } |
| // getIccSerialNumber() is used for ID before N, and getFullIccSerialNumber() after. |
| if (phone.getIccSerialNumber().equals(oldAccount.getId())) { |
| return handle; |
| } |
| } |
| return null; |
| } |
| |
| private class fetchVoicemailNetworkRequestCallback extends VvmNetworkRequestCallback { |
| |
| public fetchVoicemailNetworkRequestCallback(Context context, |
| PhoneAccountHandle phoneAccount) { |
| super(context, phoneAccount, VoicemailStatus.edit(context, phoneAccount)); |
| } |
| |
| @Override |
| public void onAvailable(final Network network) { |
| super.onAvailable(network); |
| fetchVoicemail(network, getVoicemailStatusEditor()); |
| } |
| } |
| |
| private void fetchVoicemail(final Network network, final VoicemailStatus.Editor status) { |
| Executor executor = Executors.newCachedThreadPool(); |
| executor.execute(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| while (mRetryCount > 0) { |
| VvmLog.i(TAG, "fetching voicemail, retry count=" + mRetryCount); |
| try (ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, |
| network, status)) { |
| boolean success = imapHelper.fetchVoicemailPayload( |
| new VoicemailFetchedCallback(mContext, mUri, mPhoneAccount), |
| mUid); |
| if (!success && mRetryCount > 0) { |
| VvmLog.i(TAG, "fetch voicemail failed, retrying"); |
| mRetryCount--; |
| } else { |
| return; |
| } |
| } catch (InitializingException e) { |
| VvmLog.w(TAG, "Can't retrieve Imap credentials ", e); |
| return; |
| } |
| } |
| } finally { |
| if (mNetworkCallback != null) { |
| mNetworkCallback.releaseNetwork(); |
| } |
| } |
| } |
| }); |
| } |
| } |