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