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