1 /*
2  * Copyright (C) 2014 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.settings.sim;
18 
19 import static android.provider.Settings.ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS;
20 import static android.provider.Settings.ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS;
21 import static android.provider.Settings.EXTRA_ENABLE_MMS_DATA_REQUEST_REASON;
22 import static android.provider.Settings.EXTRA_SUB_ID;
23 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE;
24 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
25 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
26 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS;
27 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE;
28 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_NAMES;
29 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE;
30 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA;
31 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE;
32 import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;
33 import static android.telephony.data.ApnSetting.TYPE_MMS;
34 
35 import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
36 
37 import android.app.Notification;
38 import android.app.NotificationChannel;
39 import android.app.NotificationManager;
40 import android.app.PendingIntent;
41 import android.content.BroadcastReceiver;
42 import android.content.Context;
43 import android.content.Intent;
44 import android.content.res.Resources;
45 import android.provider.Settings;
46 import android.telephony.SubscriptionInfo;
47 import android.telephony.SubscriptionManager;
48 import android.telephony.TelephonyManager;
49 import android.util.Log;
50 
51 import androidx.annotation.NonNull;
52 import androidx.annotation.WorkerThread;
53 
54 import com.android.internal.annotations.VisibleForTesting;
55 import com.android.settings.HelpTrampoline;
56 import com.android.settings.R;
57 import com.android.settings.network.SatelliteRepository;
58 import com.android.settings.network.SubscriptionUtil;
59 
60 import com.google.common.util.concurrent.ListenableFuture;
61 
62 import java.util.concurrent.ExecutionException;
63 import java.util.concurrent.Executor;
64 import java.util.concurrent.Executors;
65 import java.util.concurrent.TimeUnit;
66 import java.util.concurrent.TimeoutException;
67 
68 public class SimSelectNotification extends BroadcastReceiver {
69     private static final String TAG = "SimSelectNotification";
70 
71     private static final int DEFAULT_TIMEOUT_MS = 1000;
72 
73     @VisibleForTesting
74     public static final int SIM_SELECT_NOTIFICATION_ID = 1;
75     @VisibleForTesting
76     public static final int ENABLE_MMS_NOTIFICATION_ID = 2;
77     @VisibleForTesting
78     public static final int SIM_WARNING_NOTIFICATION_ID = 3;
79 
80     @VisibleForTesting
81     public static final String SIM_SELECT_NOTIFICATION_CHANNEL =
82             "sim_select_notification_channel";
83 
84     @VisibleForTesting
85     public static final String ENABLE_MMS_NOTIFICATION_CHANNEL =
86             "enable_mms_notification_channel";
87 
88     @VisibleForTesting
89     public static final String SIM_WARNING_NOTIFICATION_CHANNEL =
90             "sim_warning_notification_channel";
91 
92     @Override
onReceive(Context context, Intent intent)93     public void onReceive(Context context, Intent intent) {
94         if (!SubscriptionUtil.isSimHardwareVisible(context)) {
95             Log.w(TAG, "Received unexpected intent with null action.");
96             return;
97         }
98         String action = intent.getAction();
99 
100         if (action == null) {
101             Log.w(TAG, "Received unexpected intent with null action.");
102             return;
103         }
104 
105         switch (action) {
106             case TelephonyManager.ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED:
107                 PrimarySubscriptionListChangedService.scheduleJob(context, intent);
108                 break;
109             case Settings.ACTION_ENABLE_MMS_DATA_REQUEST:
110                 onEnableMmsDataRequest(context, intent);
111                 break;
112             default:
113                 Log.w(TAG, "Received unexpected intent " + intent.getAction());
114         }
115     }
116 
onEnableMmsDataRequest(Context context, Intent intent)117     private void onEnableMmsDataRequest(Context context, Intent intent) {
118         // Getting subId from extra.
119         int subId = intent.getIntExtra(EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
120         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
121             subId = SubscriptionManager.getDefaultSmsSubscriptionId();
122         }
123 
124         SubscriptionManager subscriptionManager = ((SubscriptionManager) context.getSystemService(
125                 Context.TELEPHONY_SUBSCRIPTION_SERVICE));
126         if (!subscriptionManager.isActiveSubscriptionId(subId)) {
127             Log.w(TAG, "onEnableMmsDataRequest invalid sub ID " + subId);
128             return;
129         }
130         final SubscriptionInfo info = subscriptionManager.getActiveSubscriptionInfo(subId);
131         if (info == null) {
132             Log.w(TAG, "onEnableMmsDataRequest null SubscriptionInfo for sub ID " + subId);
133             return;
134         }
135 
136         // Getting request reason from extra, which will determine the notification title.
137         CharSequence notificationTitle = null;
138         int requestReason = intent.getIntExtra(EXTRA_ENABLE_MMS_DATA_REQUEST_REASON, -1);
139         if (requestReason == ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS) {
140             notificationTitle = context.getResources().getText(
141                     R.string.enable_receiving_mms_notification_title);
142         } else if (requestReason == ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS) {
143             notificationTitle = context.getResources().getText(
144                     R.string.enable_sending_mms_notification_title);
145         } else {
146             Log.w(TAG, "onEnableMmsDataRequest invalid request reason " + requestReason);
147             return;
148         }
149 
150         TelephonyManager tm = ((TelephonyManager) context.getSystemService(
151                 Context.TELEPHONY_SERVICE)).createForSubscriptionId(subId);
152 
153         if (tm.isDataEnabledForApn(TYPE_MMS)) {
154             Log.w(TAG, "onEnableMmsDataRequest MMS data already enabled on sub ID " + subId);
155             return;
156         }
157 
158         CharSequence notificationSummary = context.getResources().getString(
159                 R.string.enable_mms_notification_summary,
160                 SubscriptionUtil.getUniqueSubscriptionDisplayName(info, context));
161 
162         cancelEnableMmsNotification(context);
163 
164         createEnableMmsNotification(context, notificationTitle, notificationSummary, subId);
165     }
166 
167     /**
168      * Handles changes to the primary subscription list, performing actions only
169      * if the device is not currently in a satellite session. This method is
170      * intended to be executed on a worker thread.
171      *
172      * @param context The application context
173      * @param intent  The intent signaling a primary subscription change
174      */
175     @WorkerThread
onPrimarySubscriptionListChanged(@onNull Context context, @NonNull Intent intent)176     public static void onPrimarySubscriptionListChanged(@NonNull Context context,
177             @NonNull Intent intent) {
178         Log.d(TAG, "Checking satellite enabled status");
179         Executor executor = Executors.newSingleThreadExecutor();
180         ListenableFuture<Boolean> isSatelliteSessionStartedFuture =
181                 new SatelliteRepository(context).requestIsSessionStarted(executor);
182         boolean isSatelliteSessionStarted = false;
183         try {
184             isSatelliteSessionStarted =
185                     isSatelliteSessionStartedFuture.get(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
186         } catch (InterruptedException | ExecutionException | TimeoutException e) {
187             Log.w(TAG, "Can't get satellite session status", e);
188         } finally {
189             if (isSatelliteSessionStarted) {
190                 Log.i(TAG, "Device is in a satellite session.g Unable to handle primary"
191                         + " subscription list changes");
192             } else {
193                 Log.i(TAG, "Device is not in a satellite session. Handle primary"
194                         + " subscription list changes");
195                 startSimSelectDialogIfNeeded(context, intent);
196                 sendSimCombinationWarningIfNeeded(context, intent);
197             }
198         }
199     }
200 
startSimSelectDialogIfNeeded(Context context, Intent intent)201     private static void startSimSelectDialogIfNeeded(Context context, Intent intent) {
202         int dialogType = intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE,
203                 EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE);
204 
205         if (dialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE) {
206             return;
207         }
208 
209         // Cancel any previous notifications
210         cancelSimSelectNotification(context);
211 
212         // If the dialog type is to dismiss.
213         if (dialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS) {
214             SimDialogProhibitService.dismissDialog(context);
215             return;
216         }
217 
218         // Create a notification to tell the user that some defaults are missing
219         createSimSelectNotification(context);
220 
221         if (dialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL) {
222             int subId = intent.getIntExtra(EXTRA_SUBSCRIPTION_ID,
223                     SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
224             int slotIndex = SubscriptionManager.getSlotIndex(subId);
225             // If there is only one subscription, ask if user wants to use if for everything
226             Intent newIntent = new Intent(context, SimDialogActivity.class);
227             newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
228             newIntent.putExtra(SimDialogActivity.DIALOG_TYPE_KEY,
229                     SimDialogActivity.PREFERRED_PICK);
230             newIntent.putExtra(SimDialogActivity.PREFERRED_SIM, slotIndex);
231             context.startActivity(newIntent);
232         } else if (dialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA) {
233             // If there are multiple, ensure they pick default data
234             Intent newIntent = new Intent(context, SimDialogActivity.class);
235             newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
236             newIntent.putExtra(SimDialogActivity.DIALOG_TYPE_KEY, SimDialogActivity.DATA_PICK);
237             context.startActivity(newIntent);
238         }
239     }
240 
sendSimCombinationWarningIfNeeded(Context context, Intent intent)241     private static void sendSimCombinationWarningIfNeeded(Context context, Intent intent) {
242         final int warningType = intent.getIntExtra(EXTRA_SIM_COMBINATION_WARNING_TYPE,
243                 EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE);
244 
245         // Cancel any previous notifications
246         cancelSimCombinationWarningNotification(context);
247 
248         if (warningType == EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA) {
249             // Create a notification to tell the user that there's a sim combination warning.
250             createSimCombinationWarningNotification(context, intent);
251         }
252     }
253 
createSimSelectNotification(Context context)254     private static void createSimSelectNotification(Context context) {
255         final Resources resources = context.getResources();
256 
257         NotificationChannel notificationChannel = new NotificationChannel(
258                 SIM_SELECT_NOTIFICATION_CHANNEL,
259                 resources.getText(R.string.sim_selection_channel_title),
260                 NotificationManager.IMPORTANCE_LOW);
261 
262         Notification.Builder builder =
263                 new Notification.Builder(context, SIM_SELECT_NOTIFICATION_CHANNEL)
264                         .setSmallIcon(R.drawable.ic_sim_alert)
265                         .setColor(context.getColor(R.color.sim_noitification))
266                         .setContentTitle(resources.getText(R.string.sim_notification_title))
267                         .setContentText(resources.getText(R.string.sim_notification_summary))
268                         .setAutoCancel(true);
269         Intent resultIntent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
270         resultIntent.setPackage(SETTINGS_PACKAGE_NAME);
271         resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
272         PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, resultIntent,
273                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
274         builder.setContentIntent(resultPendingIntent);
275         NotificationManager notificationManager =
276                 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
277         notificationManager.createNotificationChannel(notificationChannel);
278         notificationManager.notify(SIM_SELECT_NOTIFICATION_ID, builder.build());
279     }
280 
cancelSimSelectNotification(Context context)281     public static void cancelSimSelectNotification(Context context) {
282         NotificationManager notificationManager =
283                 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
284         notificationManager.cancel(SIM_SELECT_NOTIFICATION_ID);
285     }
286 
createEnableMmsNotification(Context context, CharSequence titleString, CharSequence notificationSummary, int subId)287     private void createEnableMmsNotification(Context context, CharSequence titleString,
288             CharSequence notificationSummary, int subId) {
289         final Resources resources = context.getResources();
290 
291         NotificationChannel notificationChannel = new NotificationChannel(
292                 ENABLE_MMS_NOTIFICATION_CHANNEL,
293                 resources.getText(R.string.enable_mms_notification_channel_title),
294                 NotificationManager.IMPORTANCE_HIGH);
295 
296         Notification.Builder builder =
297                 new Notification.Builder(context, ENABLE_MMS_NOTIFICATION_CHANNEL)
298                         .setSmallIcon(R.drawable.ic_settings_24dp)
299                         .setColor(context.getColor(R.color.sim_noitification))
300                         .setContentTitle(titleString)
301                         .setContentText(notificationSummary)
302                         .setStyle(new Notification.BigTextStyle().bigText(notificationSummary))
303                         .setAutoCancel(true);
304 
305         // Create the pending intent that will lead to the subscription setting page.
306         Intent resultIntent = new Intent(Settings.ACTION_MMS_MESSAGE_SETTING);
307         resultIntent.setPackage(SETTINGS_PACKAGE_NAME);
308         resultIntent.putExtra(Settings.EXTRA_SUB_ID, subId);
309         PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, resultIntent,
310                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
311         builder.setContentIntent(resultPendingIntent);
312 
313         // Notify the notification.
314         NotificationManager notificationManager =
315                 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
316         notificationManager.createNotificationChannel(notificationChannel);
317         notificationManager.notify(ENABLE_MMS_NOTIFICATION_ID, builder.build());
318     }
319 
cancelEnableMmsNotification(Context context)320     private void cancelEnableMmsNotification(Context context) {
321         NotificationManager notificationManager =
322                 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
323         notificationManager.cancel(ENABLE_MMS_NOTIFICATION_ID);
324     }
325 
createSimCombinationWarningNotification(Context context, Intent intent)326     private static void createSimCombinationWarningNotification(Context context, Intent intent) {
327         final Resources resources = context.getResources();
328         final String simNames = intent.getStringExtra(EXTRA_SIM_COMBINATION_NAMES);
329 
330         if (simNames == null) {
331             return;
332         }
333 
334         CharSequence dualCdmaSimWarningSummary = resources.getString(
335                 R.string.dual_cdma_sim_warning_notification_summary, simNames);
336 
337         NotificationChannel notificationChannel = new NotificationChannel(
338                 SIM_WARNING_NOTIFICATION_CHANNEL,
339                 resources.getText(R.string.dual_cdma_sim_warning_notification_channel_title),
340                 NotificationManager.IMPORTANCE_HIGH);
341 
342         Notification.Builder builder =
343                 new Notification.Builder(context, SIM_WARNING_NOTIFICATION_CHANNEL)
344                         .setSmallIcon(R.drawable.ic_sim_alert)
345                         .setColor(context.getColor(R.color.sim_noitification))
346                         .setContentTitle(resources.getText(
347                                 R.string.sim_combination_warning_notification_title))
348                         .setContentText(dualCdmaSimWarningSummary)
349                         .setStyle(new Notification.BigTextStyle().bigText(
350                                 dualCdmaSimWarningSummary))
351                         .setAutoCancel(true);
352 
353         // Create the pending intent that will lead to the helper page.
354         Intent resultIntent = new Intent(context, HelpTrampoline.class);
355         resultIntent.putExtra(Intent.EXTRA_TEXT, "help_uri_sim_combination_warning");
356 
357         PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, resultIntent,
358                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
359         builder.setContentIntent(resultPendingIntent);
360 
361         NotificationManager notificationManager =
362                 context.getSystemService(NotificationManager.class);
363         notificationManager.createNotificationChannel(notificationChannel);
364         notificationManager.notify(SIM_WARNING_NOTIFICATION_ID, builder.build());
365     }
366 
cancelSimCombinationWarningNotification(Context context)367     public static void cancelSimCombinationWarningNotification(Context context) {
368         NotificationManager notificationManager =
369                 context.getSystemService(NotificationManager.class);
370         notificationManager.cancel(SIM_WARNING_NOTIFICATION_ID);
371     }
372 }
373