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