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