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