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