• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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