| /* |
| * 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.voicemail.impl.sms; |
| |
| import android.annotation.TargetApi; |
| import android.app.Activity; |
| import android.app.PendingIntent; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.os.Build.VERSION_CODES; |
| import android.os.Bundle; |
| import android.support.annotation.MainThread; |
| import android.support.annotation.Nullable; |
| import android.support.annotation.WorkerThread; |
| import android.telecom.PhoneAccountHandle; |
| import android.telephony.SmsManager; |
| import android.telephony.VisualVoicemailSms; |
| import com.android.voicemail.impl.Assert; |
| import com.android.voicemail.impl.OmtpConstants; |
| import com.android.voicemail.impl.OmtpService; |
| import com.android.voicemail.impl.OmtpVvmCarrierConfigHelper; |
| import com.android.voicemail.impl.VvmLog; |
| import com.android.voicemail.impl.protocol.VisualVoicemailProtocol; |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.util.concurrent.CancellationException; |
| import java.util.concurrent.CompletableFuture; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| |
| /** Intercepts a incoming STATUS SMS with a blocking call. */ |
| @TargetApi(VERSION_CODES.O) |
| public class StatusSmsFetcher extends BroadcastReceiver implements Closeable { |
| |
| private static final String TAG = "VvmStatusSmsFetcher"; |
| |
| private static final long STATUS_SMS_TIMEOUT_MILLIS = 60_000; |
| |
| private static final String PERMISSION_DIALER_ORIGIN = |
| "com.android.dialer.permission.DIALER_ORIGIN"; |
| |
| private static final String ACTION_REQUEST_SENT_INTENT = |
| "com.android.voicemailomtp.sms.REQUEST_SENT"; |
| |
| private static final int ACTION_REQUEST_SENT_REQUEST_CODE = 0; |
| |
| private CompletableFuture<Bundle> future = new CompletableFuture<>(); |
| |
| private final Context context; |
| private final PhoneAccountHandle phoneAccountHandle; |
| |
| public StatusSmsFetcher(Context context, PhoneAccountHandle phoneAccountHandle) { |
| this.context = context; |
| this.phoneAccountHandle = phoneAccountHandle; |
| IntentFilter filter = new IntentFilter(ACTION_REQUEST_SENT_INTENT); |
| filter.addAction(OmtpService.ACTION_SMS_RECEIVED); |
| context.registerReceiver(this, filter, PERMISSION_DIALER_ORIGIN, /* scheduler= */ null); |
| } |
| |
| @Override |
| public void close() throws IOException { |
| context.unregisterReceiver(this); |
| } |
| |
| @WorkerThread |
| @Nullable |
| public Bundle get() |
| throws InterruptedException, ExecutionException, TimeoutException, CancellationException { |
| Assert.isNotMainThread(); |
| return future.get(STATUS_SMS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); |
| } |
| |
| public PendingIntent getSentIntent() { |
| Intent intent = new Intent(ACTION_REQUEST_SENT_INTENT); |
| intent.setPackage(context.getPackageName()); |
| // Because the receiver is registered dynamically, implicit intent must be used. |
| // There should only be a single status SMS request at a time. |
| return PendingIntent.getBroadcast( |
| context, |
| ACTION_REQUEST_SENT_REQUEST_CODE, |
| intent, |
| PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); |
| } |
| |
| @Override |
| @MainThread |
| public void onReceive(Context context, Intent intent) { |
| Assert.isMainThread(); |
| if (ACTION_REQUEST_SENT_INTENT.equals(intent.getAction())) { |
| int resultCode = getResultCode(); |
| |
| if (resultCode == Activity.RESULT_OK) { |
| VvmLog.d(TAG, "Request SMS successfully sent"); |
| return; |
| } |
| |
| VvmLog.e(TAG, "Request SMS send failed: " + sentSmsResultToString(resultCode)); |
| future.cancel(true); |
| return; |
| } |
| |
| VisualVoicemailSms sms = intent.getExtras().getParcelable(OmtpService.EXTRA_VOICEMAIL_SMS); |
| |
| if (!phoneAccountHandle.equals(sms.getPhoneAccountHandle())) { |
| return; |
| } |
| String eventType = sms.getPrefix(); |
| |
| if (eventType.equals(OmtpConstants.STATUS_SMS_PREFIX)) { |
| future.complete(sms.getFields()); |
| return; |
| } |
| |
| if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) { |
| return; |
| } |
| |
| VvmLog.i( |
| TAG, |
| "VVM SMS with event " + eventType + " received, attempting to translate to STATUS SMS"); |
| OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(context, phoneAccountHandle); |
| VisualVoicemailProtocol protocol = helper.getProtocol(); |
| if (protocol == null) { |
| return; |
| } |
| Bundle translatedBundle = protocol.translateStatusSmsBundle(helper, eventType, sms.getFields()); |
| |
| if (translatedBundle != null) { |
| VvmLog.i(TAG, "Translated to STATUS SMS"); |
| future.complete(translatedBundle); |
| } |
| } |
| |
| private static String sentSmsResultToString(int resultCode) { |
| switch (resultCode) { |
| case Activity.RESULT_OK: |
| return "OK"; |
| case SmsManager.RESULT_ERROR_GENERIC_FAILURE: |
| return "RESULT_ERROR_GENERIC_FAILURE"; |
| case SmsManager.RESULT_ERROR_NO_SERVICE: |
| return "RESULT_ERROR_GENERIC_FAILURE"; |
| case SmsManager.RESULT_ERROR_NULL_PDU: |
| return "RESULT_ERROR_GENERIC_FAILURE"; |
| case SmsManager.RESULT_ERROR_RADIO_OFF: |
| return "RESULT_ERROR_GENERIC_FAILURE"; |
| default: |
| return "UNKNOWN CODE: " + resultCode; |
| } |
| } |
| } |