1 /* 2 * Copyright (C) 2018 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.internal.telephony.emergency; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.os.AsyncResult; 24 import android.os.Environment; 25 import android.os.Handler; 26 import android.os.Message; 27 import android.os.ParcelFileDescriptor; 28 import android.os.PersistableBundle; 29 import android.os.SystemProperties; 30 import android.telephony.CarrierConfigManager; 31 import android.telephony.PhoneNumberUtils; 32 import android.telephony.ServiceState; 33 import android.telephony.SubscriptionManager; 34 import android.telephony.TelephonyManager; 35 import android.telephony.emergency.EmergencyNumber; 36 import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting; 37 import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories; 38 import android.text.TextUtils; 39 import android.util.LocalLog; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.telephony.CommandsInterface; 43 import com.android.internal.telephony.HalVersion; 44 import com.android.internal.telephony.LocaleTracker; 45 import com.android.internal.telephony.Phone; 46 import com.android.internal.telephony.PhoneConstants; 47 import com.android.internal.telephony.PhoneFactory; 48 import com.android.internal.telephony.ServiceStateTracker; 49 import com.android.internal.telephony.SubscriptionController; 50 import com.android.internal.telephony.metrics.TelephonyMetrics; 51 import com.android.internal.util.IndentingPrintWriter; 52 import com.android.phone.ecc.nano.ProtobufEccData; 53 import com.android.phone.ecc.nano.ProtobufEccData.EccInfo; 54 import com.android.telephony.Rlog; 55 56 import com.google.i18n.phonenumbers.ShortNumberInfo; 57 58 import java.io.BufferedInputStream; 59 import java.io.ByteArrayOutputStream; 60 import java.io.File; 61 import java.io.FileDescriptor; 62 import java.io.FileInputStream; 63 import java.io.IOException; 64 import java.io.InputStream; 65 import java.io.PrintWriter; 66 import java.util.ArrayList; 67 import java.util.Arrays; 68 import java.util.Collections; 69 import java.util.List; 70 import java.util.zip.GZIPInputStream; 71 72 /** 73 * Emergency Number Tracker that handles update of emergency number list from RIL and emergency 74 * number database. This is multi-sim based and each Phone has a EmergencyNumberTracker. 75 */ 76 public class EmergencyNumberTracker extends Handler { 77 private static final String TAG = EmergencyNumberTracker.class.getSimpleName(); 78 79 private static final int INVALID_DATABASE_VERSION = -1; 80 private static final String EMERGENCY_NUMBER_DB_OTA_FILE_NAME = "emergency_number_db"; 81 private static final String EMERGENCY_NUMBER_DB_OTA_FILE_PATH = 82 "misc/emergencynumberdb/" + EMERGENCY_NUMBER_DB_OTA_FILE_NAME; 83 84 /** Used for storing overrided (non-default) OTA database file path */ 85 private ParcelFileDescriptor mOverridedOtaDbParcelFileDescriptor = null; 86 87 /** @hide */ 88 public static boolean DBG = false; 89 /** @hide */ 90 public static final int ADD_EMERGENCY_NUMBER_TEST_MODE = 1; 91 /** @hide */ 92 public static final int REMOVE_EMERGENCY_NUMBER_TEST_MODE = 2; 93 /** @hide */ 94 public static final int RESET_EMERGENCY_NUMBER_TEST_MODE = 3; 95 96 private final CommandsInterface mCi; 97 private final Phone mPhone; 98 private String mCountryIso; 99 private String mLastKnownEmergencyCountryIso = ""; 100 private int mCurrentDatabaseVersion = INVALID_DATABASE_VERSION; 101 /** 102 * Indicates if the country iso is set by another subscription. 103 * @hide 104 */ 105 public boolean mIsCountrySetByAnotherSub = false; 106 private String[] mEmergencyNumberPrefix = new String[0]; 107 108 private static final String EMERGENCY_NUMBER_DB_ASSETS_FILE = "eccdata"; 109 110 private List<EmergencyNumber> mEmergencyNumberListFromDatabase = new ArrayList<>(); 111 private List<EmergencyNumber> mEmergencyNumberListFromRadio = new ArrayList<>(); 112 private List<EmergencyNumber> mEmergencyNumberListWithPrefix = new ArrayList<>(); 113 private List<EmergencyNumber> mEmergencyNumberListFromTestMode = new ArrayList<>(); 114 private List<EmergencyNumber> mEmergencyNumberList = new ArrayList<>(); 115 116 private final LocalLog mEmergencyNumberListDatabaseLocalLog = new LocalLog(16); 117 private final LocalLog mEmergencyNumberListRadioLocalLog = new LocalLog(16); 118 private final LocalLog mEmergencyNumberListPrefixLocalLog = new LocalLog(16); 119 private final LocalLog mEmergencyNumberListTestModeLocalLog = new LocalLog(16); 120 private final LocalLog mEmergencyNumberListLocalLog = new LocalLog(16); 121 122 /** Event indicating the update for the emergency number list from the radio. */ 123 private static final int EVENT_UNSOL_EMERGENCY_NUMBER_LIST = 1; 124 /** 125 * Event indicating the update for the emergency number list from the database due to the 126 * change of country code. 127 **/ 128 private static final int EVENT_UPDATE_DB_COUNTRY_ISO_CHANGED = 2; 129 /** Event indicating the update for the emergency number list in the testing mode. */ 130 private static final int EVENT_UPDATE_EMERGENCY_NUMBER_TEST_MODE = 3; 131 /** Event indicating the update for the emergency number prefix from carrier config. */ 132 private static final int EVENT_UPDATE_EMERGENCY_NUMBER_PREFIX = 4; 133 /** Event indicating the update for the OTA emergency number database. */ 134 @VisibleForTesting 135 public static final int EVENT_UPDATE_OTA_EMERGENCY_NUMBER_DB = 5; 136 /** Event indicating the override for the test OTA emergency number database. */ 137 @VisibleForTesting 138 public static final int EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH = 6; 139 140 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 141 @Override 142 public void onReceive(Context context, Intent intent) { 143 if (intent.getAction().equals( 144 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { 145 onCarrierConfigChanged(); 146 return; 147 } else if (intent.getAction().equals( 148 TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED)) { 149 int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, -1); 150 if (phoneId == mPhone.getPhoneId()) { 151 String countryIso = intent.getStringExtra( 152 TelephonyManager.EXTRA_NETWORK_COUNTRY); 153 logd("ACTION_NETWORK_COUNTRY_CHANGED: PhoneId: " + phoneId + " CountryIso: " 154 + countryIso); 155 156 // Update country iso change for available Phones 157 updateEmergencyCountryIsoAllPhones(countryIso == null ? "" : countryIso); 158 } 159 return; 160 } 161 } 162 }; 163 EmergencyNumberTracker(Phone phone, CommandsInterface ci)164 public EmergencyNumberTracker(Phone phone, CommandsInterface ci) { 165 mPhone = phone; 166 mCi = ci; 167 168 if (mPhone != null) { 169 CarrierConfigManager configMgr = (CarrierConfigManager) 170 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 171 if (configMgr != null) { 172 PersistableBundle b = configMgr.getConfigForSubId(mPhone.getSubId()); 173 if (b != null) { 174 mEmergencyNumberPrefix = b.getStringArray( 175 CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY); 176 } 177 } else { 178 loge("CarrierConfigManager is null."); 179 } 180 181 // Receive Carrier Config Changes 182 IntentFilter filter = new IntentFilter( 183 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); 184 // Receive Telephony Network Country Changes 185 filter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED); 186 187 mPhone.getContext().registerReceiver(mIntentReceiver, filter); 188 } else { 189 loge("mPhone is null."); 190 } 191 192 initializeDatabaseEmergencyNumberList(); 193 mCi.registerForEmergencyNumberList(this, EVENT_UNSOL_EMERGENCY_NUMBER_LIST, null); 194 } 195 196 /** 197 * Message handler for updating emergency number list from RIL, updating emergency number list 198 * from database if the country ISO is changed, and notifying the change of emergency number 199 * list. 200 * 201 * @param msg The message 202 */ 203 @Override handleMessage(Message msg)204 public void handleMessage(Message msg) { 205 switch (msg.what) { 206 case EVENT_UNSOL_EMERGENCY_NUMBER_LIST: 207 AsyncResult ar = (AsyncResult) msg.obj; 208 if (ar.result == null) { 209 loge("EVENT_UNSOL_EMERGENCY_NUMBER_LIST: Result from RIL is null."); 210 } else if ((ar.result != null) && (ar.exception == null)) { 211 updateRadioEmergencyNumberListAndNotify((List<EmergencyNumber>) ar.result); 212 } else { 213 loge("EVENT_UNSOL_EMERGENCY_NUMBER_LIST: Exception from RIL : " 214 + ar.exception); 215 } 216 break; 217 case EVENT_UPDATE_DB_COUNTRY_ISO_CHANGED: 218 if (msg.obj == null) { 219 loge("EVENT_UPDATE_DB_COUNTRY_ISO_CHANGED: Result from UpdateCountryIso is" 220 + " null."); 221 } else { 222 updateEmergencyNumberListDatabaseAndNotify((String) msg.obj); 223 } 224 break; 225 case EVENT_UPDATE_EMERGENCY_NUMBER_TEST_MODE: 226 if (msg.obj == null && msg.arg1 != RESET_EMERGENCY_NUMBER_TEST_MODE) { 227 loge("EVENT_UPDATE_EMERGENCY_NUMBER_TEST_MODE: Result from" 228 + " executeEmergencyNumberTestModeCommand is null."); 229 } else { 230 updateEmergencyNumberListTestModeAndNotify( 231 msg.arg1, (EmergencyNumber) msg.obj); 232 } 233 break; 234 case EVENT_UPDATE_EMERGENCY_NUMBER_PREFIX: 235 if (msg.obj == null) { 236 loge("EVENT_UPDATE_EMERGENCY_NUMBER_PREFIX: Result from" 237 + " onCarrierConfigChanged is null."); 238 } else { 239 updateEmergencyNumberPrefixAndNotify((String[]) msg.obj); 240 } 241 break; 242 case EVENT_UPDATE_OTA_EMERGENCY_NUMBER_DB: 243 updateOtaEmergencyNumberListDatabaseAndNotify(); 244 break; 245 case EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH: 246 if (msg.obj == null) { 247 overrideOtaEmergencyNumberDbFilePath(null); 248 } else { 249 overrideOtaEmergencyNumberDbFilePath((ParcelFileDescriptor) msg.obj); 250 } 251 break; 252 } 253 } 254 isAirplaneModeEnabled()255 private boolean isAirplaneModeEnabled() { 256 ServiceStateTracker serviceStateTracker = mPhone.getServiceStateTracker(); 257 if (serviceStateTracker != null) { 258 if (serviceStateTracker.getServiceState().getState() 259 == ServiceState.STATE_POWER_OFF) { 260 return true; 261 } 262 } 263 return false; 264 } 265 266 /** 267 * Checks if it's sim absent to decide whether to apply sim-absent emergency numbers from 3gpp 268 */ 269 @VisibleForTesting isSimAbsent()270 public boolean isSimAbsent() { 271 for (Phone phone: PhoneFactory.getPhones()) { 272 int slotId = SubscriptionController.getInstance().getSlotIndex(phone.getSubId()); 273 // If slot id is invalid, it means that there is no sim card. 274 if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 275 // If there is at least one sim active, sim is not absent; it returns false 276 logd("found sim in slotId: " + slotId + " subid: " + phone.getSubId()); 277 return false; 278 } 279 } 280 return true; 281 } 282 initializeDatabaseEmergencyNumberList()283 private void initializeDatabaseEmergencyNumberList() { 284 // If country iso has been cached when listener is set, don't need to cache the initial 285 // country iso and initial database. 286 if (mCountryIso == null) { 287 String countryForDatabaseCache = getInitialCountryIso().toLowerCase(); 288 updateEmergencyCountryIso(countryForDatabaseCache); 289 // Use the last known country to cache the database in APM 290 if (TextUtils.isEmpty(countryForDatabaseCache) 291 && isAirplaneModeEnabled()) { 292 countryForDatabaseCache = getCountryIsoForCachingDatabase(); 293 } 294 cacheEmergencyDatabaseByCountry(countryForDatabaseCache); 295 } 296 } 297 298 /** 299 * Update Emergency country iso for all the Phones 300 */ 301 @VisibleForTesting updateEmergencyCountryIsoAllPhones(String countryIso)302 public void updateEmergencyCountryIsoAllPhones(String countryIso) { 303 // Notify country iso change for current Phone 304 mIsCountrySetByAnotherSub = false; 305 updateEmergencyNumberDatabaseCountryChange(countryIso); 306 307 // Share and notify country iso change for other Phones if the country 308 // iso in their emergency number tracker is not available or the country 309 // iso there is set by another active subscription. 310 for (Phone phone: PhoneFactory.getPhones()) { 311 if (phone.getPhoneId() == mPhone.getPhoneId()) { 312 continue; 313 } 314 EmergencyNumberTracker emergencyNumberTracker; 315 if (phone != null && phone.getEmergencyNumberTracker() != null) { 316 emergencyNumberTracker = phone.getEmergencyNumberTracker(); 317 // If signal is lost, do not update the empty country iso for other slots. 318 if (!TextUtils.isEmpty(countryIso)) { 319 if (TextUtils.isEmpty(emergencyNumberTracker.getEmergencyCountryIso()) 320 || emergencyNumberTracker.mIsCountrySetByAnotherSub) { 321 emergencyNumberTracker.mIsCountrySetByAnotherSub = true; 322 emergencyNumberTracker.updateEmergencyNumberDatabaseCountryChange( 323 countryIso); 324 } 325 } 326 } 327 } 328 } 329 onCarrierConfigChanged()330 private void onCarrierConfigChanged() { 331 if (mPhone != null) { 332 CarrierConfigManager configMgr = (CarrierConfigManager) 333 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 334 if (configMgr != null) { 335 PersistableBundle b = configMgr.getConfigForSubId(mPhone.getSubId()); 336 if (b != null) { 337 String[] emergencyNumberPrefix = b.getStringArray( 338 CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY); 339 if (!Arrays.equals(mEmergencyNumberPrefix, emergencyNumberPrefix)) { 340 this.obtainMessage(EVENT_UPDATE_EMERGENCY_NUMBER_PREFIX, 341 emergencyNumberPrefix).sendToTarget(); 342 } 343 } 344 } 345 } else { 346 loge("onCarrierConfigChanged mPhone is null."); 347 } 348 } 349 getInitialCountryIso()350 private String getInitialCountryIso() { 351 if (mPhone != null) { 352 ServiceStateTracker sst = mPhone.getServiceStateTracker(); 353 if (sst != null) { 354 LocaleTracker lt = sst.getLocaleTracker(); 355 if (lt != null) { 356 return lt.getCurrentCountry(); 357 } 358 } 359 } else { 360 loge("getInitialCountryIso mPhone is null."); 361 362 } 363 return ""; 364 } 365 366 /** 367 * Update Emergency Number database based on changed Country ISO. 368 * 369 * @param countryIso 370 * 371 * @hide 372 */ updateEmergencyNumberDatabaseCountryChange(String countryIso)373 public void updateEmergencyNumberDatabaseCountryChange(String countryIso) { 374 this.obtainMessage(EVENT_UPDATE_DB_COUNTRY_ISO_CHANGED, countryIso).sendToTarget(); 375 } 376 377 /** 378 * Update changed OTA Emergency Number database. 379 * 380 * @hide 381 */ updateOtaEmergencyNumberDatabase()382 public void updateOtaEmergencyNumberDatabase() { 383 this.obtainMessage(EVENT_UPDATE_OTA_EMERGENCY_NUMBER_DB).sendToTarget(); 384 } 385 386 /** 387 * Override the OTA Emergency Number database file path. 388 * 389 * @hide 390 */ updateOtaEmergencyNumberDbFilePath(ParcelFileDescriptor otaParcelFileDescriptor)391 public void updateOtaEmergencyNumberDbFilePath(ParcelFileDescriptor otaParcelFileDescriptor) { 392 this.obtainMessage( 393 EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH, 394 otaParcelFileDescriptor).sendToTarget(); 395 } 396 397 /** 398 * Override the OTA Emergency Number database file path. 399 * 400 * @hide 401 */ resetOtaEmergencyNumberDbFilePath()402 public void resetOtaEmergencyNumberDbFilePath() { 403 this.obtainMessage( 404 EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH, null).sendToTarget(); 405 } 406 convertEmergencyNumberFromEccInfo(EccInfo eccInfo, String countryIso)407 private EmergencyNumber convertEmergencyNumberFromEccInfo(EccInfo eccInfo, String countryIso) { 408 String phoneNumber = eccInfo.phoneNumber.trim(); 409 if (phoneNumber.isEmpty()) { 410 loge("EccInfo has empty phone number."); 411 return null; 412 } 413 int emergencyServiceCategoryBitmask = 0; 414 for (int typeData : eccInfo.types) { 415 switch (typeData) { 416 case EccInfo.Type.POLICE: 417 emergencyServiceCategoryBitmask |= 418 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE; 419 break; 420 case EccInfo.Type.AMBULANCE: 421 emergencyServiceCategoryBitmask |= 422 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE; 423 break; 424 case EccInfo.Type.FIRE: 425 emergencyServiceCategoryBitmask |= 426 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE; 427 break; 428 case EccInfo.Type.MARINE_GUARD: 429 emergencyServiceCategoryBitmask |= 430 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD; 431 break; 432 case EccInfo.Type.MOUNTAIN_RESCUE: 433 emergencyServiceCategoryBitmask |= 434 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE; 435 break; 436 case EccInfo.Type.MIEC: 437 emergencyServiceCategoryBitmask |= 438 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC; 439 break; 440 case EccInfo.Type.AIEC: 441 emergencyServiceCategoryBitmask |= 442 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC; 443 break; 444 default: 445 // Ignores unknown types. 446 } 447 } 448 return new EmergencyNumber(phoneNumber, countryIso, "", emergencyServiceCategoryBitmask, 449 new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE, 450 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN); 451 } 452 cacheEmergencyDatabaseByCountry(String countryIso)453 private void cacheEmergencyDatabaseByCountry(String countryIso) { 454 int assetsDatabaseVersion; 455 456 // Read the Asset emergency number database 457 List<EmergencyNumber> updatedAssetEmergencyNumberList = new ArrayList<>(); 458 // try-with-resource. The 2 streams are auto closeable. 459 try (BufferedInputStream inputStream = new BufferedInputStream( 460 mPhone.getContext().getAssets().open(EMERGENCY_NUMBER_DB_ASSETS_FILE)); 461 GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) { 462 ProtobufEccData.AllInfo allEccMessages = ProtobufEccData.AllInfo.parseFrom( 463 readInputStreamToByteArray(gzipInputStream)); 464 assetsDatabaseVersion = allEccMessages.revision; 465 logd(countryIso + " asset emergency database is loaded. Ver: " + assetsDatabaseVersion 466 + " Phone Id: " + mPhone.getPhoneId()); 467 for (ProtobufEccData.CountryInfo countryEccInfo : allEccMessages.countries) { 468 if (countryEccInfo.isoCode.equals(countryIso.toUpperCase())) { 469 for (ProtobufEccData.EccInfo eccInfo : countryEccInfo.eccs) { 470 updatedAssetEmergencyNumberList.add(convertEmergencyNumberFromEccInfo( 471 eccInfo, countryIso)); 472 } 473 } 474 } 475 EmergencyNumber.mergeSameNumbersInEmergencyNumberList(updatedAssetEmergencyNumberList); 476 } catch (IOException ex) { 477 logw("Cache asset emergency database failure: " + ex); 478 return; 479 } 480 481 // Cache OTA emergency number database 482 int otaDatabaseVersion = cacheOtaEmergencyNumberDatabase(); 483 484 // Use a valid database that has higher version. 485 if (otaDatabaseVersion == INVALID_DATABASE_VERSION 486 && assetsDatabaseVersion == INVALID_DATABASE_VERSION) { 487 loge("No database available. Phone Id: " + mPhone.getPhoneId()); 488 } else if (assetsDatabaseVersion > otaDatabaseVersion) { 489 logd("Using Asset Emergency database. Version: " + assetsDatabaseVersion); 490 mCurrentDatabaseVersion = assetsDatabaseVersion; 491 mEmergencyNumberListFromDatabase = updatedAssetEmergencyNumberList; 492 } else { 493 logd("Using Ota Emergency database. Version: " + otaDatabaseVersion); 494 } 495 } 496 cacheOtaEmergencyNumberDatabase()497 private int cacheOtaEmergencyNumberDatabase() { 498 ProtobufEccData.AllInfo allEccMessages = null; 499 int otaDatabaseVersion = INVALID_DATABASE_VERSION; 500 501 // Read the OTA emergency number database 502 List<EmergencyNumber> updatedOtaEmergencyNumberList = new ArrayList<>(); 503 504 File file; 505 // If OTA File partition is not available, try to reload the default one. 506 if (mOverridedOtaDbParcelFileDescriptor == null) { 507 file = new File(Environment.getDataDirectory(), EMERGENCY_NUMBER_DB_OTA_FILE_PATH); 508 } else { 509 try { 510 file = ParcelFileDescriptor.getFile(mOverridedOtaDbParcelFileDescriptor 511 .getFileDescriptor()).getAbsoluteFile(); 512 } catch (IOException ex) { 513 loge("Cache ota emergency database IOException: " + ex); 514 return INVALID_DATABASE_VERSION; 515 } 516 } 517 518 // try-with-resource. Those 3 streams are all auto closeable. 519 try (FileInputStream fileInputStream = new FileInputStream(file); 520 BufferedInputStream inputStream = new BufferedInputStream(fileInputStream); 521 GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) { 522 allEccMessages = ProtobufEccData.AllInfo.parseFrom( 523 readInputStreamToByteArray(gzipInputStream)); 524 String countryIso = getLastKnownEmergencyCountryIso(); 525 logd(countryIso + " ota emergency database is loaded. Ver: " + otaDatabaseVersion); 526 otaDatabaseVersion = allEccMessages.revision; 527 for (ProtobufEccData.CountryInfo countryEccInfo : allEccMessages.countries) { 528 if (countryEccInfo.isoCode.equals(countryIso.toUpperCase())) { 529 for (ProtobufEccData.EccInfo eccInfo : countryEccInfo.eccs) { 530 updatedOtaEmergencyNumberList.add(convertEmergencyNumberFromEccInfo( 531 eccInfo, countryIso)); 532 } 533 } 534 } 535 EmergencyNumber.mergeSameNumbersInEmergencyNumberList(updatedOtaEmergencyNumberList); 536 } catch (IOException ex) { 537 loge("Cache ota emergency database IOException: " + ex); 538 return INVALID_DATABASE_VERSION; 539 } 540 541 // Use a valid database that has higher version. 542 if (otaDatabaseVersion != INVALID_DATABASE_VERSION 543 && mCurrentDatabaseVersion < otaDatabaseVersion) { 544 mCurrentDatabaseVersion = otaDatabaseVersion; 545 mEmergencyNumberListFromDatabase = updatedOtaEmergencyNumberList; 546 } 547 return otaDatabaseVersion; 548 } 549 550 /** 551 * Util function to convert inputStream to byte array before parsing proto data. 552 */ readInputStreamToByteArray(InputStream inputStream)553 private static byte[] readInputStreamToByteArray(InputStream inputStream) throws IOException { 554 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 555 int nRead; 556 int size = 16 * 1024; // Read 16k chunks 557 byte[] data = new byte[size]; 558 while ((nRead = inputStream.read(data, 0, data.length)) != -1) { 559 buffer.write(data, 0, nRead); 560 } 561 buffer.flush(); 562 return buffer.toByteArray(); 563 } 564 updateRadioEmergencyNumberListAndNotify( List<EmergencyNumber> emergencyNumberListRadio)565 private void updateRadioEmergencyNumberListAndNotify( 566 List<EmergencyNumber> emergencyNumberListRadio) { 567 Collections.sort(emergencyNumberListRadio); 568 logd("updateRadioEmergencyNumberListAndNotify(): receiving " + emergencyNumberListRadio); 569 if (!emergencyNumberListRadio.equals(mEmergencyNumberListFromRadio)) { 570 try { 571 EmergencyNumber.mergeSameNumbersInEmergencyNumberList(emergencyNumberListRadio); 572 writeUpdatedEmergencyNumberListMetrics(emergencyNumberListRadio); 573 mEmergencyNumberListFromRadio = emergencyNumberListRadio; 574 if (!DBG) { 575 mEmergencyNumberListRadioLocalLog.log("updateRadioEmergencyNumberList:" 576 + emergencyNumberListRadio); 577 } 578 updateEmergencyNumberList(); 579 if (!DBG) { 580 mEmergencyNumberListLocalLog.log("updateRadioEmergencyNumberListAndNotify:" 581 + mEmergencyNumberList); 582 } 583 notifyEmergencyNumberList(); 584 } catch (NullPointerException ex) { 585 loge("updateRadioEmergencyNumberListAndNotify() Phone already destroyed: " + ex 586 + " EmergencyNumberList not notified"); 587 } 588 } 589 } 590 updateEmergencyNumberListDatabaseAndNotify(String countryIso)591 private void updateEmergencyNumberListDatabaseAndNotify(String countryIso) { 592 logd("updateEmergencyNumberListDatabaseAndNotify(): receiving countryIso: " 593 + countryIso); 594 updateEmergencyCountryIso(countryIso.toLowerCase()); 595 // Use cached country iso in APM to load emergency number database. 596 if (TextUtils.isEmpty(countryIso) && isAirplaneModeEnabled()) { 597 countryIso = getCountryIsoForCachingDatabase(); 598 logd("updateEmergencyNumberListDatabaseAndNotify(): using cached APM country " 599 + countryIso); 600 } 601 cacheEmergencyDatabaseByCountry(countryIso); 602 writeUpdatedEmergencyNumberListMetrics(mEmergencyNumberListFromDatabase); 603 if (!DBG) { 604 mEmergencyNumberListDatabaseLocalLog.log( 605 "updateEmergencyNumberListDatabaseAndNotify:" 606 + mEmergencyNumberListFromDatabase); 607 } 608 updateEmergencyNumberList(); 609 if (!DBG) { 610 mEmergencyNumberListLocalLog.log("updateEmergencyNumberListDatabaseAndNotify:" 611 + mEmergencyNumberList); 612 } 613 notifyEmergencyNumberList(); 614 } 615 overrideOtaEmergencyNumberDbFilePath( ParcelFileDescriptor otaParcelableFileDescriptor)616 private void overrideOtaEmergencyNumberDbFilePath( 617 ParcelFileDescriptor otaParcelableFileDescriptor) { 618 logd("overrideOtaEmergencyNumberDbFilePath:" + otaParcelableFileDescriptor); 619 mOverridedOtaDbParcelFileDescriptor = otaParcelableFileDescriptor; 620 } 621 updateOtaEmergencyNumberListDatabaseAndNotify()622 private void updateOtaEmergencyNumberListDatabaseAndNotify() { 623 logd("updateOtaEmergencyNumberListDatabaseAndNotify():" 624 + " receiving Emegency Number database OTA update"); 625 if (cacheOtaEmergencyNumberDatabase() != INVALID_DATABASE_VERSION) { 626 writeUpdatedEmergencyNumberListMetrics(mEmergencyNumberListFromDatabase); 627 if (!DBG) { 628 mEmergencyNumberListDatabaseLocalLog.log( 629 "updateOtaEmergencyNumberListDatabaseAndNotify:" 630 + mEmergencyNumberListFromDatabase); 631 } 632 updateEmergencyNumberList(); 633 if (!DBG) { 634 mEmergencyNumberListLocalLog.log("updateOtaEmergencyNumberListDatabaseAndNotify:" 635 + mEmergencyNumberList); 636 } 637 notifyEmergencyNumberList(); 638 } 639 } 640 updateEmergencyNumberPrefixAndNotify(String[] emergencyNumberPrefix)641 private void updateEmergencyNumberPrefixAndNotify(String[] emergencyNumberPrefix) { 642 logd("updateEmergencyNumberPrefixAndNotify(): receiving emergencyNumberPrefix: " 643 + Arrays.toString(emergencyNumberPrefix)); 644 mEmergencyNumberPrefix = emergencyNumberPrefix; 645 updateEmergencyNumberList(); 646 if (!DBG) { 647 mEmergencyNumberListLocalLog.log("updateEmergencyNumberPrefixAndNotify:" 648 + mEmergencyNumberList); 649 } 650 notifyEmergencyNumberList(); 651 } 652 notifyEmergencyNumberList()653 private void notifyEmergencyNumberList() { 654 try { 655 if (getEmergencyNumberList() != null) { 656 mPhone.notifyEmergencyNumberList(); 657 logd("notifyEmergencyNumberList(): notified"); 658 } 659 } catch (NullPointerException ex) { 660 loge("notifyEmergencyNumberList(): failure: Phone already destroyed: " + ex); 661 } 662 } 663 664 /** 665 * Update emergency numbers based on the radio, database, and test mode, if they are the same 666 * emergency numbers. 667 */ updateEmergencyNumberList()668 private void updateEmergencyNumberList() { 669 List<EmergencyNumber> mergedEmergencyNumberList = 670 new ArrayList<>(mEmergencyNumberListFromDatabase); 671 mergedEmergencyNumberList.addAll(mEmergencyNumberListFromRadio); 672 // 'updateEmergencyNumberList' is called every time there is a change for emergency numbers 673 // from radio indication, emergency numbers from database, emergency number prefix from 674 // carrier config, or test mode emergency numbers, the emergency number prefix is changed 675 // by carrier config, the emergency number list with prefix needs to be clear, and re-apply 676 // the new prefix for the current emergency numbers. 677 mEmergencyNumberListWithPrefix.clear(); 678 if (mEmergencyNumberPrefix.length != 0) { 679 mEmergencyNumberListWithPrefix.addAll(getEmergencyNumberListWithPrefix( 680 mEmergencyNumberListFromRadio)); 681 mEmergencyNumberListWithPrefix.addAll(getEmergencyNumberListWithPrefix( 682 mEmergencyNumberListFromDatabase)); 683 } 684 if (!DBG) { 685 mEmergencyNumberListPrefixLocalLog.log("updateEmergencyNumberList:" 686 + mEmergencyNumberListWithPrefix); 687 } 688 mergedEmergencyNumberList.addAll(mEmergencyNumberListWithPrefix); 689 mergedEmergencyNumberList.addAll(mEmergencyNumberListFromTestMode); 690 EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList); 691 mEmergencyNumberList = mergedEmergencyNumberList; 692 } 693 694 /** 695 * Get the emergency number list. 696 * 697 * @return the emergency number list based on radio indication or ril.ecclist if radio 698 * indication not support from the HAL. 699 */ getEmergencyNumberList()700 public List<EmergencyNumber> getEmergencyNumberList() { 701 if (!mEmergencyNumberListFromRadio.isEmpty()) { 702 return Collections.unmodifiableList(mEmergencyNumberList); 703 } else { 704 return getEmergencyNumberListFromEccListDatabaseAndTest(); 705 } 706 } 707 708 /** 709 * Checks if the number is an emergency number in the current Phone. 710 * 711 * @return {@code true} if it is; {@code false} otherwise. 712 */ isEmergencyNumber(String number, boolean exactMatch)713 public boolean isEmergencyNumber(String number, boolean exactMatch) { 714 if (number == null) { 715 return false; 716 } 717 718 // Do not treat SIP address as emergency number 719 if (PhoneNumberUtils.isUriNumber(number)) { 720 return false; 721 } 722 723 // Strip the separators from the number before comparing it 724 // to the list. 725 number = PhoneNumberUtils.extractNetworkPortionAlt(number); 726 727 if (!mEmergencyNumberListFromRadio.isEmpty()) { 728 for (EmergencyNumber num : mEmergencyNumberList) { 729 // According to com.android.i18n.phonenumbers.ShortNumberInfo, in 730 // these countries, if extra digits are added to an emergency number, 731 // it no longer connects to the emergency service. 732 String countryIso = getLastKnownEmergencyCountryIso(); 733 if (countryIso.equals("br") || countryIso.equals("cl") 734 || countryIso.equals("ni")) { 735 exactMatch = true; 736 } else { 737 exactMatch = false || exactMatch; 738 } 739 if (exactMatch) { 740 if (num.getNumber().equals(number)) { 741 logd("Found in mEmergencyNumberList [exact match] "); 742 return true; 743 } 744 } else { 745 if (number.startsWith(num.getNumber())) { 746 logd("Found in mEmergencyNumberList [not exact match] "); 747 return true; 748 } 749 } 750 } 751 return false; 752 } else { 753 boolean inEccList = isEmergencyNumberFromEccList(number, exactMatch); 754 boolean inEmergencyNumberDb = isEmergencyNumberFromDatabase(number); 755 boolean inEmergencyNumberTestList = isEmergencyNumberForTest(number); 756 logd("Search results - inRilEccList:" + inEccList 757 + " inEmergencyNumberDb:" + inEmergencyNumberDb + " inEmergencyNumberTestList: " 758 + inEmergencyNumberTestList); 759 return inEccList || inEmergencyNumberDb || inEmergencyNumberTestList; 760 } 761 } 762 763 /** 764 * Get the {@link EmergencyNumber} for the corresponding emergency number address. 765 * 766 * @param emergencyNumber - the supplied emergency number. 767 * @return the {@link EmergencyNumber} for the corresponding emergency number address. 768 */ getEmergencyNumber(String emergencyNumber)769 public EmergencyNumber getEmergencyNumber(String emergencyNumber) { 770 emergencyNumber = PhoneNumberUtils.stripSeparators(emergencyNumber); 771 for (EmergencyNumber num : getEmergencyNumberList()) { 772 if (num.getNumber().equals(emergencyNumber)) { 773 return num; 774 } 775 } 776 return null; 777 } 778 779 /** 780 * Get the emergency service categories for the corresponding emergency number. The only 781 * trusted sources for the categories are the 782 * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} and 783 * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM}. 784 * 785 * @param emergencyNumber - the supplied emergency number. 786 * @return the emergency service categories for the corresponding emergency number. 787 */ getEmergencyServiceCategories(String emergencyNumber)788 public @EmergencyServiceCategories int getEmergencyServiceCategories(String emergencyNumber) { 789 emergencyNumber = PhoneNumberUtils.stripSeparators(emergencyNumber); 790 for (EmergencyNumber num : getEmergencyNumberList()) { 791 if (num.getNumber().equals(emergencyNumber)) { 792 if (num.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING) 793 || num.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM)) { 794 return num.getEmergencyServiceCategoryBitmask(); 795 } 796 } 797 } 798 return EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED; 799 } 800 801 /** 802 * Get the emergency call routing for the corresponding emergency number. The only trusted 803 * source for the routing is {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE}. 804 * 805 * @param emergencyNumber - the supplied emergency number. 806 * @return the emergency call routing for the corresponding emergency number. 807 */ getEmergencyCallRouting(String emergencyNumber)808 public @EmergencyCallRouting int getEmergencyCallRouting(String emergencyNumber) { 809 emergencyNumber = PhoneNumberUtils.stripSeparators(emergencyNumber); 810 for (EmergencyNumber num : getEmergencyNumberList()) { 811 if (num.getNumber().equals(emergencyNumber)) { 812 if (num.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE)) { 813 return num.getEmergencyCallRouting(); 814 } 815 } 816 } 817 return EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN; 818 } 819 getEmergencyCountryIso()820 public String getEmergencyCountryIso() { 821 return mCountryIso; 822 } 823 getLastKnownEmergencyCountryIso()824 public String getLastKnownEmergencyCountryIso() { 825 return mLastKnownEmergencyCountryIso; 826 } 827 getCountryIsoForCachingDatabase()828 private String getCountryIsoForCachingDatabase() { 829 ServiceStateTracker sst = mPhone.getServiceStateTracker(); 830 if (sst != null) { 831 LocaleTracker lt = sst.getLocaleTracker(); 832 if (lt != null) { 833 return lt.getLastKnownCountryIso(); 834 } 835 } 836 return ""; 837 } 838 getEmergencyNumberDbVersion()839 public int getEmergencyNumberDbVersion() { 840 return mCurrentDatabaseVersion; 841 } 842 updateEmergencyCountryIso(String countryIso)843 private synchronized void updateEmergencyCountryIso(String countryIso) { 844 mCountryIso = countryIso; 845 if (!TextUtils.isEmpty(mCountryIso)) { 846 mLastKnownEmergencyCountryIso = mCountryIso; 847 } 848 mCurrentDatabaseVersion = INVALID_DATABASE_VERSION; 849 } 850 851 /** 852 * Get Emergency number list based on EccList. This util is used for solving backward 853 * compatibility if device does not support the 1.4 IRadioIndication HAL that reports 854 * emergency number list. 855 */ getEmergencyNumberListFromEccList()856 private List<EmergencyNumber> getEmergencyNumberListFromEccList() { 857 List<EmergencyNumber> emergencyNumberList = new ArrayList<>(); 858 int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId()); 859 860 String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId); 861 String emergencyNumbers = SystemProperties.get(ecclist, ""); 862 if (TextUtils.isEmpty(emergencyNumbers)) { 863 // then read-only ecclist property since old RIL only uses this 864 emergencyNumbers = SystemProperties.get("ro.ril.ecclist"); 865 } 866 if (!TextUtils.isEmpty(emergencyNumbers)) { 867 // searches through the comma-separated list for a match, 868 // return true if one is found. 869 for (String emergencyNum : emergencyNumbers.split(",")) { 870 emergencyNumberList.add(getLabeledEmergencyNumberForEcclist(emergencyNum)); 871 } 872 } 873 emergencyNumbers = ((isSimAbsent()) ? "112,911,000,08,110,118,119,999" : "112,911"); 874 for (String emergencyNum : emergencyNumbers.split(",")) { 875 emergencyNumberList.add(getLabeledEmergencyNumberForEcclist(emergencyNum)); 876 } 877 if (mEmergencyNumberPrefix.length != 0) { 878 emergencyNumberList.addAll(getEmergencyNumberListWithPrefix(emergencyNumberList)); 879 } 880 EmergencyNumber.mergeSameNumbersInEmergencyNumberList(emergencyNumberList); 881 return emergencyNumberList; 882 } 883 getEmergencyNumberListWithPrefix( List<EmergencyNumber> emergencyNumberList)884 private List<EmergencyNumber> getEmergencyNumberListWithPrefix( 885 List<EmergencyNumber> emergencyNumberList) { 886 List<EmergencyNumber> emergencyNumberListWithPrefix = new ArrayList<>(); 887 if (emergencyNumberList != null) { 888 for (EmergencyNumber num : emergencyNumberList) { 889 for (String prefix : mEmergencyNumberPrefix) { 890 // If an emergency number has started with the prefix, 891 // no need to apply the prefix. 892 if (!num.getNumber().startsWith(prefix)) { 893 emergencyNumberListWithPrefix.add(new EmergencyNumber( 894 prefix + num.getNumber(), num.getCountryIso(), 895 num.getMnc(), num.getEmergencyServiceCategoryBitmask(), 896 num.getEmergencyUrns(), num.getEmergencyNumberSourceBitmask(), 897 num.getEmergencyCallRouting())); 898 } 899 } 900 } 901 } 902 return emergencyNumberListWithPrefix; 903 } 904 isEmergencyNumberForTest(String number)905 private boolean isEmergencyNumberForTest(String number) { 906 number = PhoneNumberUtils.stripSeparators(number); 907 for (EmergencyNumber num : mEmergencyNumberListFromTestMode) { 908 if (num.getNumber().equals(number)) { 909 return true; 910 } 911 } 912 return false; 913 } 914 isEmergencyNumberFromDatabase(String number)915 private boolean isEmergencyNumberFromDatabase(String number) { 916 if (!mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) { 917 return false; 918 } 919 number = PhoneNumberUtils.stripSeparators(number); 920 for (EmergencyNumber num : mEmergencyNumberListFromDatabase) { 921 if (num.getNumber().equals(number)) { 922 return true; 923 } 924 } 925 List<EmergencyNumber> emergencyNumberListFromDatabaseWithPrefix = 926 getEmergencyNumberListWithPrefix(mEmergencyNumberListFromDatabase); 927 for (EmergencyNumber num : emergencyNumberListFromDatabaseWithPrefix) { 928 if (num.getNumber().equals(number)) { 929 return true; 930 } 931 } 932 return false; 933 } 934 getLabeledEmergencyNumberForEcclist(String number)935 private EmergencyNumber getLabeledEmergencyNumberForEcclist(String number) { 936 number = PhoneNumberUtils.stripSeparators(number); 937 for (EmergencyNumber num : mEmergencyNumberListFromDatabase) { 938 if (num.getNumber().equals(number)) { 939 return new EmergencyNumber(number, getLastKnownEmergencyCountryIso().toLowerCase(), 940 "", num.getEmergencyServiceCategoryBitmask(), 941 new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE, 942 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN); 943 } 944 } 945 return new EmergencyNumber(number, "", "", 946 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED, 947 new ArrayList<String>(), 0, 948 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN); 949 } 950 951 /** 952 * Back-up old logics for {@link PhoneNumberUtils#isEmergencyNumberInternal} for legacy 953 * and deprecate purpose. 954 */ isEmergencyNumberFromEccList(String number, boolean useExactMatch)955 private boolean isEmergencyNumberFromEccList(String number, boolean useExactMatch) { 956 // If the number passed in is null, just return false: 957 if (number == null) return false; 958 959 /// M: preprocess number for emergency check @{ 960 // Move following logic to isEmergencyNumber() 961 962 // If the number passed in is a SIP address, return false, since the 963 // concept of "emergency numbers" is only meaningful for calls placed 964 // over the cell network. 965 // (Be sure to do this check *before* calling extractNetworkPortionAlt(), 966 // since the whole point of extractNetworkPortionAlt() is to filter out 967 // any non-dialable characters (which would turn 'abc911def@example.com' 968 // into '911', for example.)) 969 //if (PhoneNumberUtils.isUriNumber(number)) { 970 // return false; 971 //} 972 973 // Strip the separators from the number before comparing it 974 // to the list. 975 //number = PhoneNumberUtils.extractNetworkPortionAlt(number); 976 /// @} 977 978 String emergencyNumbers = ""; 979 int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId()); 980 981 String ecclist = null; 982 String countryIso = getLastKnownEmergencyCountryIso(); 983 984 if (!mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) { 985 //only use ril ecc list for older devices with HAL < 1.4 986 // check read-write ecclist property first 987 ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId); 988 emergencyNumbers = SystemProperties.get(ecclist, ""); 989 990 logd("slotId:" + slotId + " country:" + countryIso + " emergencyNumbers: " 991 + emergencyNumbers); 992 993 if (TextUtils.isEmpty(emergencyNumbers)) { 994 // then read-only ecclist property since old RIL only uses this 995 emergencyNumbers = SystemProperties.get("ro.ril.ecclist"); 996 } 997 998 if (!TextUtils.isEmpty(emergencyNumbers)) { 999 // searches through the comma-separated list for a match, 1000 // return true if one is found. 1001 for (String emergencyNum : emergencyNumbers.split(",")) { 1002 // According to com.android.i18n.phonenumbers.ShortNumberInfo, in 1003 // these countries, if extra digits are added to an emergency number, 1004 // it no longer connects to the emergency service. 1005 if (useExactMatch || countryIso.equals("br") || countryIso.equals("cl") 1006 || countryIso.equals("ni")) { 1007 if (number.equals(emergencyNum)) { 1008 return true; 1009 } else { 1010 for (String prefix : mEmergencyNumberPrefix) { 1011 if (number.equals(prefix + emergencyNum)) { 1012 return true; 1013 } 1014 } 1015 } 1016 } else { 1017 if (number.startsWith(emergencyNum)) { 1018 return true; 1019 } else { 1020 for (String prefix : mEmergencyNumberPrefix) { 1021 if (number.startsWith(prefix + emergencyNum)) { 1022 return true; 1023 } 1024 } 1025 } 1026 } 1027 } 1028 // no matches found against the list! 1029 return false; 1030 } 1031 } 1032 1033 logd("System property doesn't provide any emergency numbers." 1034 + " Use embedded logic for determining ones."); 1035 1036 // According spec 3GPP TS22.101, the following numbers should be 1037 // ECC numbers when SIM/USIM is not present. 1038 emergencyNumbers = ((isSimAbsent()) ? "112,911,000,08,110,118,119,999" : "112,911"); 1039 1040 for (String emergencyNum : emergencyNumbers.split(",")) { 1041 if (useExactMatch) { 1042 if (number.equals(emergencyNum)) { 1043 return true; 1044 } else { 1045 for (String prefix : mEmergencyNumberPrefix) { 1046 if (number.equals(prefix + emergencyNum)) { 1047 return true; 1048 } 1049 } 1050 } 1051 } else { 1052 if (number.startsWith(emergencyNum)) { 1053 return true; 1054 } else { 1055 for (String prefix : mEmergencyNumberPrefix) { 1056 if (number.equals(prefix + emergencyNum)) { 1057 return true; 1058 } 1059 } 1060 } 1061 } 1062 } 1063 1064 if(isSimAbsent()) { 1065 // No ecclist system property, so use our own list. 1066 if (countryIso != null) { 1067 ShortNumberInfo info = ShortNumberInfo.getInstance(); 1068 if (useExactMatch) { 1069 if (info.isEmergencyNumber(number, countryIso.toUpperCase())) { 1070 return true; 1071 } else { 1072 for (String prefix : mEmergencyNumberPrefix) { 1073 if (info.isEmergencyNumber(prefix + number, countryIso.toUpperCase())) { 1074 return true; 1075 } 1076 } 1077 } 1078 return false; 1079 } else { 1080 if (info.connectsToEmergencyNumber(number, countryIso.toUpperCase())) { 1081 return true; 1082 } else { 1083 for (String prefix : mEmergencyNumberPrefix) { 1084 if (info.connectsToEmergencyNumber(prefix + number, 1085 countryIso.toUpperCase())) { 1086 return true; 1087 } 1088 } 1089 } 1090 return false; 1091 } 1092 } 1093 } 1094 1095 return false; 1096 } 1097 1098 /** 1099 * Execute command for updating emergency number for test mode. 1100 */ executeEmergencyNumberTestModeCommand(int action, EmergencyNumber num)1101 public void executeEmergencyNumberTestModeCommand(int action, EmergencyNumber num) { 1102 this.obtainMessage(EVENT_UPDATE_EMERGENCY_NUMBER_TEST_MODE, action, 0, num).sendToTarget(); 1103 } 1104 1105 /** 1106 * Update emergency number list for test mode. 1107 */ updateEmergencyNumberListTestModeAndNotify(int action, EmergencyNumber num)1108 private void updateEmergencyNumberListTestModeAndNotify(int action, EmergencyNumber num) { 1109 if (action == ADD_EMERGENCY_NUMBER_TEST_MODE) { 1110 if (!isEmergencyNumber(num.getNumber(), true)) { 1111 mEmergencyNumberListFromTestMode.add(num); 1112 } 1113 } else if (action == RESET_EMERGENCY_NUMBER_TEST_MODE) { 1114 mEmergencyNumberListFromTestMode.clear(); 1115 } else if (action == REMOVE_EMERGENCY_NUMBER_TEST_MODE) { 1116 mEmergencyNumberListFromTestMode.remove(num); 1117 } else { 1118 loge("updateEmergencyNumberListTestModeAndNotify: Unexpected action in test mode."); 1119 return; 1120 } 1121 if (!DBG) { 1122 mEmergencyNumberListTestModeLocalLog.log( 1123 "updateEmergencyNumberListTestModeAndNotify:" 1124 + mEmergencyNumberListFromTestMode); 1125 } 1126 updateEmergencyNumberList(); 1127 if (!DBG) { 1128 mEmergencyNumberListLocalLog.log( 1129 "updateEmergencyNumberListTestModeAndNotify:" 1130 + mEmergencyNumberList); 1131 } 1132 notifyEmergencyNumberList(); 1133 } 1134 getEmergencyNumberListFromEccListDatabaseAndTest()1135 private List<EmergencyNumber> getEmergencyNumberListFromEccListDatabaseAndTest() { 1136 List<EmergencyNumber> mergedEmergencyNumberList = getEmergencyNumberListFromEccList(); 1137 if (mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) { 1138 loge("getEmergencyNumberListFromEccListDatabaseAndTest: radio indication is" 1139 + " unavailable in 1.4 HAL."); 1140 mergedEmergencyNumberList.addAll(mEmergencyNumberListFromDatabase); 1141 mergedEmergencyNumberList.addAll(getEmergencyNumberListWithPrefix( 1142 mEmergencyNumberListFromDatabase)); 1143 } 1144 mergedEmergencyNumberList.addAll(getEmergencyNumberListTestMode()); 1145 EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList); 1146 return mergedEmergencyNumberList; 1147 } 1148 1149 /** 1150 * Get emergency number list for test. 1151 */ getEmergencyNumberListTestMode()1152 public List<EmergencyNumber> getEmergencyNumberListTestMode() { 1153 return Collections.unmodifiableList(mEmergencyNumberListFromTestMode); 1154 } 1155 1156 @VisibleForTesting getRadioEmergencyNumberList()1157 public List<EmergencyNumber> getRadioEmergencyNumberList() { 1158 return new ArrayList<>(mEmergencyNumberListFromRadio); 1159 } 1160 logd(String str)1161 private static void logd(String str) { 1162 Rlog.d(TAG, str); 1163 } 1164 logw(String str)1165 private static void logw(String str) { 1166 Rlog.w(TAG, str); 1167 } 1168 loge(String str)1169 private static void loge(String str) { 1170 Rlog.e(TAG, str); 1171 } 1172 writeUpdatedEmergencyNumberListMetrics( List<EmergencyNumber> updatedEmergencyNumberList)1173 private void writeUpdatedEmergencyNumberListMetrics( 1174 List<EmergencyNumber> updatedEmergencyNumberList) { 1175 if (updatedEmergencyNumberList == null) { 1176 return; 1177 } 1178 for (EmergencyNumber num : updatedEmergencyNumberList) { 1179 TelephonyMetrics.getInstance().writeEmergencyNumberUpdateEvent( 1180 mPhone.getPhoneId(), num, getEmergencyNumberDbVersion()); 1181 } 1182 } 1183 1184 /** 1185 * Dump Emergency Number List info in the tracking 1186 * 1187 * @param fd FileDescriptor 1188 * @param pw PrintWriter 1189 * @param args args 1190 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)1191 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1192 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 1193 ipw.println(" Hal Version:" + mPhone.getHalVersion()); 1194 ipw.println(" ========================================= "); 1195 1196 ipw.println(" Country Iso:" + getEmergencyCountryIso()); 1197 ipw.println(" ========================================= "); 1198 1199 ipw.println(" Database Version:" + getEmergencyNumberDbVersion()); 1200 ipw.println(" ========================================= "); 1201 1202 ipw.println("mEmergencyNumberListDatabaseLocalLog:"); 1203 ipw.increaseIndent(); 1204 mEmergencyNumberListDatabaseLocalLog.dump(fd, pw, args); 1205 ipw.decreaseIndent(); 1206 ipw.println(" ========================================= "); 1207 1208 ipw.println("mEmergencyNumberListRadioLocalLog:"); 1209 ipw.increaseIndent(); 1210 mEmergencyNumberListRadioLocalLog.dump(fd, pw, args); 1211 ipw.decreaseIndent(); 1212 ipw.println(" ========================================= "); 1213 1214 ipw.println("mEmergencyNumberListPrefixLocalLog:"); 1215 ipw.increaseIndent(); 1216 mEmergencyNumberListPrefixLocalLog.dump(fd, pw, args); 1217 ipw.decreaseIndent(); 1218 ipw.println(" ========================================= "); 1219 1220 ipw.println("mEmergencyNumberListTestModeLocalLog:"); 1221 ipw.increaseIndent(); 1222 mEmergencyNumberListTestModeLocalLog.dump(fd, pw, args); 1223 ipw.decreaseIndent(); 1224 ipw.println(" ========================================= "); 1225 1226 ipw.println("mEmergencyNumberListLocalLog (valid >= 1.4 HAL):"); 1227 ipw.increaseIndent(); 1228 mEmergencyNumberListLocalLog.dump(fd, pw, args); 1229 ipw.decreaseIndent(); 1230 ipw.println(" ========================================= "); 1231 1232 int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId()); 1233 String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId); 1234 ipw.println(" ril.ecclist: " + SystemProperties.get(ecclist, "")); 1235 ipw.println(" ========================================= "); 1236 1237 ipw.println("Emergency Number List for Phone" + "(" + mPhone.getPhoneId() + ")"); 1238 ipw.increaseIndent(); 1239 ipw.println(getEmergencyNumberList()); 1240 ipw.decreaseIndent(); 1241 ipw.println(" ========================================= "); 1242 1243 ipw.flush(); 1244 } 1245 } 1246