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