1 /*
2  * Copyright (C) 2020 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.settingslib.emergencynumber;
18 
19 import static android.telephony.emergency.EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE;
20 import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE;
21 
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.pm.PackageManager;
25 import android.net.Uri;
26 import android.os.Bundle;
27 import android.os.PersistableBundle;
28 import android.telephony.CarrierConfigManager;
29 import android.telephony.SubscriptionManager;
30 import android.telephony.TelephonyManager;
31 import android.telephony.emergency.EmergencyNumber;
32 import android.text.TextUtils;
33 import android.util.ArrayMap;
34 import android.util.Log;
35 
36 import androidx.annotation.VisibleForTesting;
37 
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.stream.Collectors;
42 
43 /**
44  * Util class to help manage emergency numbers
45  */
46 public class EmergencyNumberUtils {
47     private static final String TAG = "EmergencyNumberUtils";
48 
49     public static final Uri EMERGENCY_NUMBER_OVERRIDE_AUTHORITY = new Uri.Builder().scheme(
50             ContentResolver.SCHEME_CONTENT)
51             .authority("com.android.emergency.gesture")
52             .build();
53     public static final String METHOD_NAME_GET_EMERGENCY_NUMBER_OVERRIDE =
54             "GET_EMERGENCY_NUMBER_OVERRIDE";
55     public static final String METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE =
56             "SET_EMERGENCY_NUMBER_OVERRIDE";
57     public static final String METHOD_NAME_SET_EMERGENCY_GESTURE = "SET_EMERGENCY_GESTURE";
58     public static final String METHOD_NAME_SET_EMERGENCY_SOUND = "SET_EMERGENCY_SOUND";
59     public static final String METHOD_NAME_GET_EMERGENCY_GESTURE_ENABLED = "GET_EMERGENCY_GESTURE";
60     public static final String METHOD_NAME_GET_EMERGENCY_GESTURE_SOUND_ENABLED =
61             "GET_EMERGENCY_SOUND";
62     public static final String EMERGENCY_GESTURE_CALL_NUMBER = "emergency_gesture_call_number";
63     public static final String EMERGENCY_SETTING_VALUE = "emergency_setting_value";
64     public static final int EMERGENCY_SETTING_ON = 1;
65     public static final int EMERGENCY_SETTING_OFF = 0;
66 
67     @VisibleForTesting
68     static final String FALL_BACK_NUMBER = "112";
69 
70     private final Context mContext;
71     private final TelephonyManager mTelephonyManager;
72     private final CarrierConfigManager mCarrierConfigManager;
73 
EmergencyNumberUtils(Context context)74     public EmergencyNumberUtils(Context context) {
75         mContext = context;
76         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
77             mTelephonyManager = context.getSystemService(TelephonyManager.class);
78             mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
79         } else {
80             mTelephonyManager = null;
81             mCarrierConfigManager = null;
82         }
83     }
84 
85     /**
86      * Returns the most appropriate number for police.
87      */
getDefaultPoliceNumber()88     public String getDefaultPoliceNumber() {
89         if (mTelephonyManager == null) {
90             return FALL_BACK_NUMBER;
91         }
92         final List<String> promotedPoliceNumber = getPromotedEmergencyNumbers(
93                 EMERGENCY_SERVICE_CATEGORY_POLICE);
94         if (promotedPoliceNumber == null || promotedPoliceNumber.isEmpty()) {
95             return FALL_BACK_NUMBER;
96         }
97         return promotedPoliceNumber.get(0);
98     }
99 
100     /**
101      * Returns the number chosen by user. If user has not provided any number, use default ({@link
102      * #getDefaultPoliceNumber()}).
103      */
getPoliceNumber()104     public String getPoliceNumber() {
105         final String userProvidedNumber = getEmergencyNumberOverride();
106         return TextUtils.isEmpty(userProvidedNumber)
107                 ? getDefaultPoliceNumber() : userProvidedNumber;
108     }
109 
110     /**
111      * Sets device-local emergency number override
112      */
setEmergencyNumberOverride(String number)113     public void setEmergencyNumberOverride(String number) {
114         final Bundle bundle = new Bundle();
115         bundle.putString(EMERGENCY_GESTURE_CALL_NUMBER, number);
116         mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
117                 METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE, null /* args */, bundle);
118     }
119 
120     /**
121      * Enable/disable the emergency gesture setting
122      */
setEmergencyGestureEnabled(boolean enabled)123     public void setEmergencyGestureEnabled(boolean enabled) {
124         final Bundle bundle = new Bundle();
125         bundle.putInt(EMERGENCY_SETTING_VALUE,
126                 enabled ? EMERGENCY_SETTING_ON : EMERGENCY_SETTING_OFF);
127         mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
128                 METHOD_NAME_SET_EMERGENCY_GESTURE, null /* args */, bundle);
129     }
130 
131     /**
132      * Enable/disable the emergency gesture sound setting
133      */
setEmergencySoundEnabled(boolean enabled)134     public void setEmergencySoundEnabled(boolean enabled) {
135         final Bundle bundle = new Bundle();
136         bundle.putInt(EMERGENCY_SETTING_VALUE,
137                 enabled ? EMERGENCY_SETTING_ON : EMERGENCY_SETTING_OFF);
138         mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
139                 METHOD_NAME_SET_EMERGENCY_SOUND, null /* args */, bundle);
140     }
141 
142     /**
143      * Whether or not emergency gesture is enabled.
144      */
getEmergencyGestureEnabled()145     public boolean getEmergencyGestureEnabled() {
146         final Bundle bundle = mContext.getContentResolver().call(
147                 EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
148                 METHOD_NAME_GET_EMERGENCY_GESTURE_ENABLED, null /* args */, null /* bundle */);
149         return bundle == null ? true : bundle.getInt(EMERGENCY_SETTING_VALUE, EMERGENCY_SETTING_ON)
150                 == EMERGENCY_SETTING_ON;
151     }
152 
153     /**
154      * Whether or not emergency gesture sound is enabled.
155      */
getEmergencyGestureSoundEnabled()156     public boolean getEmergencyGestureSoundEnabled() {
157         final Bundle bundle = mContext.getContentResolver().call(
158                 EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
159                 METHOD_NAME_GET_EMERGENCY_GESTURE_SOUND_ENABLED, null /* args */,
160                 null /* bundle */);
161         return bundle == null ? true : bundle.getInt(EMERGENCY_SETTING_VALUE, EMERGENCY_SETTING_OFF)
162                 == EMERGENCY_SETTING_ON;
163     }
164 
getEmergencyNumberOverride()165     private String getEmergencyNumberOverride() {
166         final Bundle bundle = mContext.getContentResolver().call(
167                 EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
168                 METHOD_NAME_GET_EMERGENCY_NUMBER_OVERRIDE, null /* args */, null /* bundle */);
169         return bundle == null ? null : bundle.getString(EMERGENCY_GESTURE_CALL_NUMBER);
170     }
171 
getPromotedEmergencyNumbers(int categories)172     private List<String> getPromotedEmergencyNumbers(int categories) {
173         Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(
174                 categories);
175         if (allLists == null || allLists.isEmpty()) {
176             Log.w(TAG, "Unable to retrieve emergency number lists!");
177             return new ArrayList<>();
178         }
179         Map<Integer, List<String>> promotedEmergencyNumberLists = new ArrayMap<>();
180         for (Map.Entry<Integer, List<EmergencyNumber>> entry : allLists.entrySet()) {
181             if (entry.getKey() == null || entry.getValue() == null) {
182                 continue;
183             }
184             int subId = entry.getKey();
185             List<EmergencyNumber> emergencyNumberList = entry.getValue();
186             Log.d(TAG, "Emergency numbers for subscription id " + entry.getKey());
187 
188             // The list of promoted emergency numbers which will be visible on shortcut view.
189             List<EmergencyNumber> promotedList = new ArrayList<>();
190             // A temporary list for non-prioritized emergency numbers.
191             List<EmergencyNumber> tempList = new ArrayList<>();
192 
193             for (EmergencyNumber emergencyNumber : emergencyNumberList) {
194                 // Emergency numbers in DATABASE are prioritized since they were well-categorized.
195                 boolean isFromPrioritizedSource =
196                         emergencyNumber.getEmergencyNumberSources().contains(
197                                 EMERGENCY_NUMBER_SOURCE_DATABASE);
198 
199                 Log.d(TAG, String.format("Number %s, isFromPrioritizedSource %b",
200                         emergencyNumber, isFromPrioritizedSource));
201                 if (isFromPrioritizedSource) {
202                     promotedList.add(emergencyNumber);
203                 } else {
204                     tempList.add(emergencyNumber);
205                 }
206             }
207             // Puts numbers in temp list after prioritized numbers.
208             promotedList.addAll(tempList);
209 
210             if (!promotedList.isEmpty()) {
211                 List<String> sanitizedNumbers = sanitizeEmergencyNumbers(promotedList, subId);
212                 promotedEmergencyNumberLists.put(subId, sanitizedNumbers);
213             }
214         }
215 
216         if (promotedEmergencyNumberLists.isEmpty()) {
217             Log.w(TAG, "No promoted emergency number found!");
218         }
219         return promotedEmergencyNumberLists.get(SubscriptionManager.getDefaultSubscriptionId());
220     }
221 
sanitizeEmergencyNumbers( List<EmergencyNumber> input, int subscriptionId)222     private List<String> sanitizeEmergencyNumbers(
223             List<EmergencyNumber> input, int subscriptionId) {
224         // Make a copy of data so we can mutate.
225         List<EmergencyNumber> data = new ArrayList<>(input);
226         String[] carrierPrefixes =
227                 getCarrierEmergencyNumberPrefixes(mCarrierConfigManager, subscriptionId);
228         return data.stream()
229                 .map(d -> removePrefix(d, carrierPrefixes))
230                 .collect(Collectors.toCollection(ArrayList::new));
231     }
232 
removePrefix(EmergencyNumber emergencyNumber, String[] prefixes)233     private String removePrefix(EmergencyNumber emergencyNumber, String[] prefixes) {
234         String number = emergencyNumber.getNumber();
235         if (prefixes == null || prefixes.length == 0) {
236             return number;
237         }
238         for (String prefix : prefixes) {
239             int prefixStartIndex = number.indexOf(prefix);
240             if (prefixStartIndex != 0) {
241                 continue;
242             }
243             Log.d(TAG, "Removing prefix " + prefix + " from " + number);
244             return number.substring(prefix.length());
245         }
246         return number;
247     }
248 
getCarrierEmergencyNumberPrefixes( CarrierConfigManager carrierConfigManager, int subId)249     private static String[] getCarrierEmergencyNumberPrefixes(
250             CarrierConfigManager carrierConfigManager, int subId) {
251         PersistableBundle b = carrierConfigManager.getConfigForSubId(subId);
252         return b == null
253                 ? null
254                 : b.getStringArray(CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY);
255     }
256 }
257