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