1 /*
2  * Copyright (C) 2011 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.cellbroadcastreceiver;
18 
19 import android.app.ActivityManager;
20 import android.content.BroadcastReceiver;
21 import android.content.ComponentName;
22 import android.content.ContentProviderClient;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.SharedPreferences;
26 import android.content.SharedPreferences.Editor;
27 import android.content.pm.ActivityInfo;
28 import android.content.pm.PackageInfo;
29 import android.content.pm.PackageManager;
30 import android.content.res.Resources;
31 import android.os.Bundle;
32 import android.os.RemoteException;
33 import android.os.SystemProperties;
34 import android.os.UserManager;
35 import android.provider.Telephony;
36 import android.provider.Telephony.CellBroadcasts;
37 import android.telephony.CarrierConfigManager;
38 import android.telephony.ServiceState;
39 import android.telephony.SubscriptionManager;
40 import android.telephony.TelephonyManager;
41 import android.telephony.cdma.CdmaSmsCbProgramData;
42 import android.text.TextUtils;
43 import android.util.EventLog;
44 import android.util.Log;
45 import android.widget.Toast;
46 
47 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
48 import androidx.preference.PreferenceManager;
49 
50 import com.android.cellbroadcastservice.CellBroadcastStatsLog;
51 import com.android.internal.annotations.VisibleForTesting;
52 
53 import java.util.ArrayList;
54 
55 
56 public class CellBroadcastReceiver extends BroadcastReceiver {
57     private static final String TAG = "CellBroadcastReceiver";
58     static final boolean DBG = true;
59     static final boolean VDBG = false;    // STOPSHIP: change to false before ship
60 
61     // Key to access the shared preference of reminder interval default value.
62     @VisibleForTesting
63     public static final String CURRENT_INTERVAL_DEFAULT = "current_interval_default";
64 
65     // Key to access the shared preference of cell broadcast testing mode.
66     @VisibleForTesting
67     public static final String TESTING_MODE = "testing_mode";
68 
69     // Key to access the shared preference of service state.
70     private static final String SERVICE_STATE = "service_state";
71 
72     // shared preference under developer settings
73     private static final String ENABLE_ALERT_MASTER_PREF = "enable_alerts_master_toggle";
74 
75     // shared preference for alert reminder interval
76     private static final String ALERT_REMINDER_INTERVAL_PREF = "alert_reminder_interval";
77 
78     // SharedPreferences key used to store the last carrier
79     private static final String CARRIER_ID_FOR_DEFAULT_SUB_PREF = "carrier_id_for_default_sub";
80     // initial value for saved carrier ID. This helps us detect newly updated users or first boot
81     private static final int NO_PREVIOUS_CARRIER_ID = -2;
82 
83     public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
84     public static final String EXTRA_VOICE_REG_STATE = "voiceRegState";
85 
86     // Intent actions and extras
87     public static final String CELLBROADCAST_START_CONFIG_ACTION =
88             "com.android.cellbroadcastreceiver.intent.START_CONFIG";
89     public static final String ACTION_MARK_AS_READ =
90             "com.android.cellbroadcastreceiver.intent.action.MARK_AS_READ";
91     public static final String EXTRA_DELIVERY_TIME =
92             "com.android.cellbroadcastreceiver.intent.extra.ID";
93 
94     public static final String ACTION_TESTING_MODE_CHANGED =
95             "com.android.cellbroadcastreceiver.intent.ACTION_TESTING_MODE_CHANGED";
96 
97     private Context mContext;
98 
99     /**
100      * this method is to make this class unit-testable, because CellBroadcastSettings.getResources()
101      * is a static method and cannot be stubbed.
102      * @return resources
103      */
104     @VisibleForTesting
getResourcesMethod()105     public Resources getResourcesMethod() {
106         return CellBroadcastSettings.getResourcesForDefaultSubId(mContext);
107     }
108 
109     @Override
onReceive(Context context, Intent intent)110     public void onReceive(Context context, Intent intent) {
111         if (DBG) log("onReceive " + intent);
112 
113         mContext = context.getApplicationContext();
114         String action = intent.getAction();
115         Resources res = getResourcesMethod();
116 
117         if (ACTION_MARK_AS_READ.equals(action)) {
118             // The only way this'll be called is if someone tries to maliciously set something as
119             // read. Log an event.
120             EventLog.writeEvent(0x534e4554, "162741784", -1, null);
121         } else if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) {
122             if (!intent.getBooleanExtra(
123                     "android.telephony.extra.REBROADCAST_ON_UNLOCK", false)) {
124                 int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
125                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
126                 initializeSharedPreference(context, subId);
127                 enableLauncher();
128                 startConfigServiceToEnableChannels();
129                 // Some OEMs do not have legacyMigrationProvider active during boot-up, thus we
130                 // need to retry data migration from another trigger point.
131                 boolean hasMigrated = getDefaultSharedPreferences()
132                         .getBoolean(CellBroadcastDatabaseHelper.KEY_LEGACY_DATA_MIGRATION, false);
133                 if (res.getBoolean(R.bool.retry_message_history_data_migration) && !hasMigrated) {
134                     // migrate message history from legacy app on a background thread.
135                     new CellBroadcastContentProvider.AsyncCellBroadcastTask(
136                             mContext.getContentResolver()).execute(
137                             (CellBroadcastContentProvider.CellBroadcastOperation) provider -> {
138                                 provider.call(CellBroadcastContentProvider.CALL_MIGRATION_METHOD,
139                                         null, null);
140                                 return true;
141                             });
142                 }
143             }
144         } else if (ACTION_SERVICE_STATE.equals(action)) {
145             // lower layer clears channel configurations under APM, thus need to resend
146             // configurations once moving back from APM. This should be fixed in lower layer
147             // going forward.
148             int ss = intent.getIntExtra(EXTRA_VOICE_REG_STATE, ServiceState.STATE_IN_SERVICE);
149             if (ss != ServiceState.STATE_POWER_OFF
150                     && getServiceState(context) == ServiceState.STATE_POWER_OFF) {
151                 startConfigServiceToEnableChannels();
152             }
153             setServiceState(ss);
154         } else if (SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED.equals(action)) {
155             startConfigServiceToEnableChannels();
156         } else if (Telephony.Sms.Intents.ACTION_SMS_EMERGENCY_CB_RECEIVED.equals(action) ||
157                 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) {
158             intent.setClass(mContext, CellBroadcastAlertService.class);
159             mContext.startService(intent);
160         } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION
161                 .equals(action)) {
162             ArrayList<CdmaSmsCbProgramData> programDataList =
163                     intent.getParcelableArrayListExtra("program_data");
164             CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_REPORTED,
165                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__TYPE__CDMA_SPC,
166                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__SOURCE__CB_RECEIVER_APP);
167             if (programDataList != null) {
168                 handleCdmaSmsCbProgramData(programDataList);
169             } else {
170                 loge("SCPD intent received with no program_data");
171             }
172         } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
173             // rename registered notification channels on locale change
174             CellBroadcastAlertService.createNotificationChannels(mContext);
175         } else if (TelephonyManager.ACTION_SECRET_CODE.equals(action)) {
176             if (SystemProperties.getInt("ro.debuggable", 0) == 1
177                     || res.getBoolean(R.bool.allow_testing_mode_on_user_build)) {
178                 setTestingMode(!isTestingMode(mContext));
179                 int msgId = (isTestingMode(mContext)) ? R.string.testing_mode_enabled
180                         : R.string.testing_mode_disabled;
181                 String msg =  res.getString(msgId);
182                 Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
183                 LocalBroadcastManager.getInstance(mContext)
184                         .sendBroadcast(new Intent(ACTION_TESTING_MODE_CHANGED));
185                 log(msg);
186             }
187         } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
188             new CellBroadcastContentProvider.AsyncCellBroadcastTask(
189                     mContext.getContentResolver()).execute((CellBroadcastContentProvider
190                     .CellBroadcastOperation) provider -> {
191                         provider.resyncToSmsInbox(mContext);
192                         return true;
193                     });
194         } else if (TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED.equals(action)) {
195             int sim_state = intent.getIntExtra(
196                 TelephonyManager.EXTRA_SIM_STATE, TelephonyManager.SIM_STATE_UNKNOWN);
197 
198             if (sim_state == TelephonyManager.SIM_STATE_ABSENT
199                 || sim_state == TelephonyManager.SIM_STATE_PRESENT) {
200                 CellBroadcastChannelManager.clearAllCellBroadcastChannelRanges();
201             }
202         } else {
203             Log.w(TAG, "onReceive() unexpected action " + action);
204         }
205     }
206 
207     /**
208      * Send an intent to reset the users WEA settings if there is a new carrier on the default subId
209      *
210      * Do nothing in other cases:
211      *   - SIM insertion for the non-default subId
212      *   - SIM insertion/bootup with no new carrier
213      *   - SIM removal
214      *   - Device just received the update which adds this carrier tracking logic
215      * @param context the context
216      * @param subId subId of the carrier config event
217      */
resetSettingsIfCarrierChanged(Context context, int subId)218     private void resetSettingsIfCarrierChanged(Context context, int subId) {
219         // subId may be -1 if carrier config broadcast is being sent on SIM removal
220         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
221             if (getPreviousCarrierIdForDefaultSub() == NO_PREVIOUS_CARRIER_ID) {
222                 // on first boot only, if no SIM is inserted we save the carrier ID -1.
223                 // This allows us to detect the carrier change from -1 to the carrier of the first
224                 // SIM when it is inserted.
225                 saveCarrierIdForDefaultSub(TelephonyManager.UNKNOWN_CARRIER_ID);
226             }
227             Log.d(TAG, "ignoring carrier config broadcast because subId=-1");
228             return;
229         }
230 
231         final int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
232         Log.d(TAG, "subId=" + subId + " defaultSubId=" + defaultSubId);
233         if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
234             Log.d(TAG, "ignoring carrier config broadcast because defaultSubId=-1");
235             return;
236         }
237 
238         if (subId != defaultSubId) {
239             Log.d(TAG, "ignoring carrier config broadcast for subId=" + subId
240                     + " because it does not match defaultSubId=" + defaultSubId);
241             return;
242         }
243 
244         TelephonyManager tm = context.getSystemService(TelephonyManager.class);
245         // carrierId is loaded before carrier config, so this should be safe
246         int carrierId = tm.createForSubscriptionId(subId).getSimCarrierId();
247         if (carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) {
248             Log.e(TAG, "ignoring unknown carrier ID");
249             return;
250         }
251 
252         int previousCarrierId = getPreviousCarrierIdForDefaultSub();
253         if (previousCarrierId == NO_PREVIOUS_CARRIER_ID) {
254             // on first boot if a SIM is inserted, assume it is not new and don't apply settings
255             Log.d(TAG, "ignoring carrier config broadcast for subId=" + subId
256                     + " for first boot");
257             saveCarrierIdForDefaultSub(carrierId);
258             return;
259         }
260 
261         if (carrierId != previousCarrierId) {
262             saveCarrierIdForDefaultSub(carrierId);
263             startConfigService(context,
264                     CellBroadcastConfigService.ACTION_UPDATE_SETTINGS_FOR_CARRIER);
265         } else {
266             Log.d(TAG, "ignoring carrier config broadcast for subId=" + subId
267                     + " because carrier has not changed. carrierId=" + carrierId);
268         }
269     }
270 
getPreviousCarrierIdForDefaultSub()271     private int getPreviousCarrierIdForDefaultSub() {
272         return getDefaultSharedPreferences()
273                 .getInt(CARRIER_ID_FOR_DEFAULT_SUB_PREF, NO_PREVIOUS_CARRIER_ID);
274     }
275 
saveCarrierIdForDefaultSub(int carrierId)276     private void saveCarrierIdForDefaultSub(int carrierId) {
277         getDefaultSharedPreferences().edit().putInt(CARRIER_ID_FOR_DEFAULT_SUB_PREF, carrierId)
278                 .apply();
279     }
280 
281         /**
282      * Enable/disable cell broadcast receiver testing mode.
283      *
284      * @param on {@code true} if testing mode is on, otherwise off.
285      */
286     @VisibleForTesting
setTestingMode(boolean on)287     public void setTestingMode(boolean on) {
288         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
289         sp.edit().putBoolean(TESTING_MODE, on).commit();
290     }
291 
292     /**
293      * @return {@code true} if operating in testing mode, which enables some features for testing
294      * purposes.
295      */
isTestingMode(Context context)296     public static boolean isTestingMode(Context context) {
297         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
298         return sp.getBoolean(TESTING_MODE, false);
299     }
300 
301     /**
302      * Store the current service state for voice registration.
303      *
304      * @param ss current voice registration service state.
305      */
setServiceState(int ss)306     private void setServiceState(int ss) {
307         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
308         sp.edit().putInt(SERVICE_STATE, ss).commit();
309     }
310 
311     /**
312      * @return the stored voice registration service state
313      */
getServiceState(Context context)314     private static int getServiceState(Context context) {
315         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
316         return sp.getInt(SERVICE_STATE, ServiceState.STATE_IN_SERVICE);
317     }
318 
319     /**
320      * update reminder interval
321      */
322     @VisibleForTesting
adjustReminderInterval()323     public void adjustReminderInterval() {
324         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
325         String currentIntervalDefault = sp.getString(CURRENT_INTERVAL_DEFAULT, "0");
326 
327         // If interval default changes, reset the interval to the new default value.
328         String newIntervalDefault = CellBroadcastSettings.getResourcesForDefaultSubId(mContext)
329                 .getString(R.string.alert_reminder_interval_in_min_default);
330         if (!newIntervalDefault.equals(currentIntervalDefault)) {
331             Log.d(TAG, "Default interval changed from " + currentIntervalDefault + " to " +
332                     newIntervalDefault);
333 
334             Editor editor = sp.edit();
335             // Reset the value to default.
336             editor.putString(
337                     CellBroadcastSettings.KEY_ALERT_REMINDER_INTERVAL, newIntervalDefault);
338             // Save the new default value.
339             editor.putString(CURRENT_INTERVAL_DEFAULT, newIntervalDefault);
340             editor.commit();
341         } else {
342             if (DBG) Log.d(TAG, "Default interval " + currentIntervalDefault + " did not change.");
343         }
344     }
345     /**
346      * This method's purpose is to enable unit testing
347      * @return sharedePreferences for mContext
348      */
349     @VisibleForTesting
getDefaultSharedPreferences()350     public SharedPreferences getDefaultSharedPreferences() {
351         return PreferenceManager.getDefaultSharedPreferences(mContext);
352     }
353 
354     /**
355      * return if there are default values in shared preferences
356      * @return boolean
357      */
358     @VisibleForTesting
sharedPrefsHaveDefaultValues()359     public Boolean sharedPrefsHaveDefaultValues() {
360         return mContext.getSharedPreferences(PreferenceManager.KEY_HAS_SET_DEFAULT_VALUES,
361                 Context.MODE_PRIVATE).getBoolean(PreferenceManager.KEY_HAS_SET_DEFAULT_VALUES,
362                 false);
363     }
364     /**
365      * initialize shared preferences before starting services
366      */
367     @VisibleForTesting
initializeSharedPreference(Context context, int subId)368     public void initializeSharedPreference(Context context, int subId) {
369         if (isSystemUser()) {
370             Log.d(TAG, "initializeSharedPreference");
371 
372             resetSettingsIfCarrierChanged(context, subId);
373 
374             SharedPreferences sp = getDefaultSharedPreferences();
375 
376             if (!sharedPrefsHaveDefaultValues()) {
377                 // Sets the default values of the shared preference if there isn't any.
378                 PreferenceManager.setDefaultValues(mContext, R.xml.preferences, false);
379 
380                 sp.edit().putBoolean(CellBroadcastSettings.KEY_OVERRIDE_DND_SETTINGS_CHANGED,
381                         false).apply();
382 
383                 // migrate sharedpref from legacy app
384                 migrateSharedPreferenceFromLegacy();
385 
386                 // If the device is in test harness mode, we need to disable emergency alert by
387                 // default.
388                 if (ActivityManager.isRunningInUserTestHarness()) {
389                     Log.d(TAG, "In test harness mode. Turn off emergency alert by default.");
390                     sp.edit().putBoolean(CellBroadcastSettings.KEY_ENABLE_ALERTS_MASTER_TOGGLE,
391                             false).apply();
392                 }
393             } else {
394                 Log.d(TAG, "Skip setting default values of shared preference.");
395             }
396 
397             adjustReminderInterval();
398         } else {
399             Log.e(TAG, "initializeSharedPreference: Not system user.");
400         }
401     }
402 
403     /**
404      * migrate shared preferences from legacy content provider client
405      */
406     @VisibleForTesting
migrateSharedPreferenceFromLegacy()407     public void migrateSharedPreferenceFromLegacy() {
408         String[] PREF_KEYS = {
409                 CellBroadcasts.Preference.ENABLE_CMAS_AMBER_PREF,
410                 CellBroadcasts.Preference.ENABLE_AREA_UPDATE_INFO_PREF,
411                 CellBroadcasts.Preference.ENABLE_TEST_ALERT_PREF,
412                 CellBroadcasts.Preference.ENABLE_STATE_LOCAL_TEST_PREF,
413                 CellBroadcasts.Preference.ENABLE_PUBLIC_SAFETY_PREF,
414                 CellBroadcasts.Preference.ENABLE_CMAS_SEVERE_THREAT_PREF,
415                 CellBroadcasts.Preference.ENABLE_CMAS_EXTREME_THREAT_PREF,
416                 CellBroadcasts.Preference.ENABLE_CMAS_PRESIDENTIAL_PREF,
417                 CellBroadcasts.Preference.ENABLE_EMERGENCY_PERF,
418                 CellBroadcasts.Preference.ENABLE_ALERT_VIBRATION_PREF,
419                 CellBroadcasts.Preference.ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF,
420                 ENABLE_ALERT_MASTER_PREF,
421                 ALERT_REMINDER_INTERVAL_PREF
422         };
423         try (ContentProviderClient client = mContext.getContentResolver()
424                 .acquireContentProviderClient(Telephony.CellBroadcasts.AUTHORITY_LEGACY)) {
425             if (client == null) {
426                 Log.d(TAG, "No legacy provider available for sharedpreference migration");
427                 return;
428             }
429             SharedPreferences.Editor sp = PreferenceManager
430                     .getDefaultSharedPreferences(mContext).edit();
431             for (String key : PREF_KEYS) {
432                 try {
433                     Bundle pref = client.call(
434                             CellBroadcasts.AUTHORITY_LEGACY,
435                             CellBroadcasts.CALL_METHOD_GET_PREFERENCE,
436                             key, null);
437                     if (pref != null && pref.containsKey(key)) {
438                         Object val = pref.get(key);
439                         if (val == null) {
440                             // noop - no value to set.
441                             // Only support Boolean and String as preference types for now.
442                         } else if (val instanceof Boolean) {
443                             Log.d(TAG, "migrateSharedPreferenceFromLegacy: " + key + "val: "
444                                     + pref.getBoolean(key));
445                             sp.putBoolean(key, pref.getBoolean(key));
446                         } else if (val instanceof String) {
447                             Log.d(TAG, "migrateSharedPreferenceFromLegacy: " + key + "val: "
448                                     + pref.getString(key));
449                             sp.putString(key, pref.getString(key));
450                         }
451                     } else {
452                         Log.d(TAG, "migrateSharedPreferenceFromLegacy: unsupported key: " + key);
453                     }
454                 } catch (RemoteException e) {
455                     Log.e(TAG, "fails to get shared preference " + e);
456                 }
457             }
458             sp.apply();
459         } catch (Exception e) {
460             // We have to guard ourselves against any weird behavior of the
461             // legacy provider by trying to catch everything
462             loge("Failed migration from legacy provider: " + e);
463         }
464     }
465 
466     /**
467      * Handle Service Category Program Data message.
468      * TODO: Send Service Category Program Results response message to sender
469      *
470      * @param programDataList
471      */
472     @VisibleForTesting
handleCdmaSmsCbProgramData(ArrayList<CdmaSmsCbProgramData> programDataList)473     public void handleCdmaSmsCbProgramData(ArrayList<CdmaSmsCbProgramData> programDataList) {
474         for (CdmaSmsCbProgramData programData : programDataList) {
475             switch (programData.getOperation()) {
476                 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY:
477                     tryCdmaSetCategory(mContext, programData.getCategory(), true);
478                     break;
479 
480                 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY:
481                     tryCdmaSetCategory(mContext, programData.getCategory(), false);
482                     break;
483 
484                 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES:
485                     tryCdmaSetCategory(mContext,
486                             CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT, false);
487                     tryCdmaSetCategory(mContext,
488                             CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT, false);
489                     tryCdmaSetCategory(mContext,
490                             CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false);
491                     tryCdmaSetCategory(mContext,
492                             CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE, false);
493                     break;
494 
495                 default:
496                     loge("Ignoring unknown SCPD operation " + programData.getOperation());
497             }
498         }
499     }
500 
501     /**
502      * set CDMA category in shared preferences
503      * @param context
504      * @param category CDMA category
505      * @param enable true for add category, false otherwise
506      */
507     @VisibleForTesting
tryCdmaSetCategory(Context context, int category, boolean enable)508     public void tryCdmaSetCategory(Context context, int category, boolean enable) {
509         SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
510 
511         switch (category) {
512             case CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT:
513                 sharedPrefs.edit().putBoolean(
514                         CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable)
515                         .apply();
516                 break;
517 
518             case CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT:
519                 sharedPrefs.edit().putBoolean(
520                         CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable)
521                         .apply();
522                 break;
523 
524             case CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
525                 sharedPrefs.edit().putBoolean(
526                         CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply();
527                 break;
528 
529             case CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE:
530                 sharedPrefs.edit().putBoolean(
531                         CellBroadcastSettings.KEY_ENABLE_TEST_ALERTS, enable).apply();
532                 break;
533 
534             default:
535                 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable")
536                         + " alerts in category " + category);
537         }
538     }
539 
540     /**
541      * This method's purpose if to enable unit testing
542      * @return if the mContext user is a system user
543      */
isSystemUser()544     private boolean isSystemUser() {
545         return isSystemUser(mContext);
546     }
547 
548     /**
549      * This method's purpose if to enable unit testing
550      */
551     @VisibleForTesting
startConfigServiceToEnableChannels()552     public void startConfigServiceToEnableChannels() {
553         startConfigService(mContext, CellBroadcastConfigService.ACTION_ENABLE_CHANNELS);
554     }
555 
556     /**
557      * Check if user from context is system user
558      * @param context
559      * @return whether the user is system user
560      */
isSystemUser(Context context)561     private static boolean isSystemUser(Context context) {
562         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
563         return userManager.isSystemUser();
564     }
565 
566     /**
567      * Tell {@link CellBroadcastConfigService} to enable the CB channels.
568      * @param context the broadcast receiver context
569      */
startConfigService(Context context, String action)570     static void startConfigService(Context context, String action) {
571         if (isSystemUser(context)) {
572             Log.d(TAG, "Start Cell Broadcast configuration for intent=" + action);
573             context.startService(new Intent(action, null, context,
574                     CellBroadcastConfigService.class));
575         } else {
576             Log.e(TAG, "startConfigService: Not system user.");
577         }
578     }
579 
580     /**
581      * Enable Launcher.
582      */
583     @VisibleForTesting
enableLauncher()584     public void enableLauncher() {
585         boolean enable = getResourcesMethod().getBoolean(R.bool.show_message_history_in_launcher);
586         final PackageManager pm = mContext.getPackageManager();
587         // This alias presents the target activity, CellBroadcastListActivity, as a independent
588         // entity with its own intent filter for android.intent.category.LAUNCHER.
589         // This alias will be enabled/disabled at run-time based on resource overlay. Once enabled,
590         // it will appear in the Launcher as a top-level application
591         String aliasLauncherActivity = null;
592         try {
593             PackageInfo p = pm.getPackageInfo(mContext.getPackageName(),
594                 PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS);
595             if (p != null) {
596                 for (ActivityInfo activityInfo : p.activities) {
597                     String targetActivity = activityInfo.targetActivity;
598                     if (CellBroadcastListActivity.class.getName().equals(targetActivity)) {
599                         aliasLauncherActivity = activityInfo.name;
600                         break;
601                     }
602                 }
603             }
604         } catch (PackageManager.NameNotFoundException e) {
605             Log.e(TAG, e.toString());
606         }
607         if (TextUtils.isEmpty(aliasLauncherActivity)) {
608             Log.e(TAG, "cannot find launcher activity");
609             return;
610         }
611 
612         if (enable) {
613             Log.d(TAG, "enable launcher activity: " + aliasLauncherActivity);
614             pm.setComponentEnabledSetting(
615                 new ComponentName(mContext, aliasLauncherActivity),
616                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
617         } else {
618             Log.d(TAG, "disable launcher activity: " + aliasLauncherActivity);
619             pm.setComponentEnabledSetting(
620                 new ComponentName(mContext, aliasLauncherActivity),
621                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
622         }
623     }
624 
log(String msg)625     private static void log(String msg) {
626         Log.d(TAG, msg);
627     }
628 
loge(String msg)629     private static void loge(String msg) {
630         Log.e(TAG, msg);
631     }
632 }
633