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