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