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