• 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 
17 package com.android.voicemail.impl.sms;
18 
19 import android.annotation.TargetApi;
20 import android.app.Activity;
21 import android.app.PendingIntent;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.os.Build.VERSION_CODES;
27 import android.os.Bundle;
28 import android.support.annotation.MainThread;
29 import android.support.annotation.Nullable;
30 import android.support.annotation.WorkerThread;
31 import android.telecom.PhoneAccountHandle;
32 import android.telephony.SmsManager;
33 import android.telephony.VisualVoicemailSms;
34 import com.android.voicemail.impl.Assert;
35 import com.android.voicemail.impl.OmtpConstants;
36 import com.android.voicemail.impl.OmtpService;
37 import com.android.voicemail.impl.OmtpVvmCarrierConfigHelper;
38 import com.android.voicemail.impl.VvmLog;
39 import com.android.voicemail.impl.protocol.VisualVoicemailProtocol;
40 import java.io.Closeable;
41 import java.io.IOException;
42 import java.util.concurrent.CancellationException;
43 import java.util.concurrent.CompletableFuture;
44 import java.util.concurrent.ExecutionException;
45 import java.util.concurrent.TimeUnit;
46 import java.util.concurrent.TimeoutException;
47 
48 /** Intercepts a incoming STATUS SMS with a blocking call. */
49 @SuppressWarnings("AndroidApiChecker") /* CompletableFuture is java8*/
50 @TargetApi(VERSION_CODES.O)
51 public class StatusSmsFetcher extends BroadcastReceiver implements Closeable {
52 
53   private static final String TAG = "VvmStatusSmsFetcher";
54 
55   private static final long STATUS_SMS_TIMEOUT_MILLIS = 60_000;
56 
57   private static final String ACTION_REQUEST_SENT_INTENT =
58       "com.android.voicemailomtp.sms.REQUEST_SENT";
59   private static final int ACTION_REQUEST_SENT_REQUEST_CODE = 0;
60 
61   private CompletableFuture<Bundle> mFuture = new CompletableFuture<>();
62 
63   private final Context mContext;
64   private final PhoneAccountHandle mPhoneAccountHandle;
65 
StatusSmsFetcher(Context context, PhoneAccountHandle phoneAccountHandle)66   public StatusSmsFetcher(Context context, PhoneAccountHandle phoneAccountHandle) {
67     mContext = context;
68     mPhoneAccountHandle = phoneAccountHandle;
69     IntentFilter filter = new IntentFilter(ACTION_REQUEST_SENT_INTENT);
70     filter.addAction(OmtpService.ACTION_SMS_RECEIVED);
71     context.registerReceiver(this, filter);
72   }
73 
74   @Override
close()75   public void close() throws IOException {
76     mContext.unregisterReceiver(this);
77   }
78 
79   @WorkerThread
80   @Nullable
get()81   public Bundle get()
82       throws InterruptedException, ExecutionException, TimeoutException, CancellationException {
83     Assert.isNotMainThread();
84     return mFuture.get(STATUS_SMS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
85   }
86 
getSentIntent()87   public PendingIntent getSentIntent() {
88     Intent intent = new Intent(ACTION_REQUEST_SENT_INTENT);
89     intent.setPackage(mContext.getPackageName());
90     // Because the receiver is registered dynamically, implicit intent must be used.
91     // There should only be a single status SMS request at a time.
92     return PendingIntent.getBroadcast(
93         mContext, ACTION_REQUEST_SENT_REQUEST_CODE, intent, PendingIntent.FLAG_CANCEL_CURRENT);
94   }
95 
96   @Override
97   @MainThread
onReceive(Context context, Intent intent)98   public void onReceive(Context context, Intent intent) {
99     Assert.isMainThread();
100     if (ACTION_REQUEST_SENT_INTENT.equals(intent.getAction())) {
101       int resultCode = getResultCode();
102 
103       if (resultCode == Activity.RESULT_OK) {
104         VvmLog.d(TAG, "Request SMS successfully sent");
105         return;
106       }
107 
108       VvmLog.e(TAG, "Request SMS send failed: " + sentSmsResultToString(resultCode));
109       mFuture.cancel(true);
110       return;
111     }
112 
113     VisualVoicemailSms sms = intent.getExtras().getParcelable(OmtpService.EXTRA_VOICEMAIL_SMS);
114 
115     if (!mPhoneAccountHandle.equals(sms.getPhoneAccountHandle())) {
116       return;
117     }
118     String eventType = sms.getPrefix();
119 
120     if (eventType.equals(OmtpConstants.STATUS_SMS_PREFIX)) {
121       mFuture.complete(sms.getFields());
122       return;
123     }
124 
125     if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
126       return;
127     }
128 
129     VvmLog.i(
130         TAG,
131         "VVM SMS with event " + eventType + " received, attempting to translate to STATUS SMS");
132     OmtpVvmCarrierConfigHelper helper =
133         new OmtpVvmCarrierConfigHelper(context, mPhoneAccountHandle);
134     VisualVoicemailProtocol protocol = helper.getProtocol();
135     if (protocol == null) {
136       return;
137     }
138     Bundle translatedBundle = protocol.translateStatusSmsBundle(helper, eventType, sms.getFields());
139 
140     if (translatedBundle != null) {
141       VvmLog.i(TAG, "Translated to STATUS SMS");
142       mFuture.complete(translatedBundle);
143     }
144   }
145 
sentSmsResultToString(int resultCode)146   private static String sentSmsResultToString(int resultCode) {
147     switch (resultCode) {
148       case Activity.RESULT_OK:
149         return "OK";
150       case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
151         return "RESULT_ERROR_GENERIC_FAILURE";
152       case SmsManager.RESULT_ERROR_NO_SERVICE:
153         return "RESULT_ERROR_GENERIC_FAILURE";
154       case SmsManager.RESULT_ERROR_NULL_PDU:
155         return "RESULT_ERROR_GENERIC_FAILURE";
156       case SmsManager.RESULT_ERROR_RADIO_OFF:
157         return "RESULT_ERROR_GENERIC_FAILURE";
158       default:
159         return "UNKNOWN CODE: " + resultCode;
160     }
161   }
162 }
163