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