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