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