| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.settingslib.emergencynumber; |
| |
| import static android.telephony.emergency.EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE; |
| import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE; |
| |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.pm.PackageManager; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.PersistableBundle; |
| import android.telephony.CarrierConfigManager; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| import android.telephony.emergency.EmergencyNumber; |
| import android.text.TextUtils; |
| import android.util.ArrayMap; |
| import android.util.Log; |
| |
| import androidx.annotation.VisibleForTesting; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.stream.Collectors; |
| |
| /** |
| * Util class to help manage emergency numbers |
| */ |
| public class EmergencyNumberUtils { |
| private static final String TAG = "EmergencyNumberUtils"; |
| |
| public static final Uri EMERGENCY_NUMBER_OVERRIDE_AUTHORITY = new Uri.Builder().scheme( |
| ContentResolver.SCHEME_CONTENT) |
| .authority("com.android.emergency.gesture") |
| .build(); |
| public static final String METHOD_NAME_GET_EMERGENCY_NUMBER_OVERRIDE = |
| "GET_EMERGENCY_NUMBER_OVERRIDE"; |
| public static final String METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE = |
| "SET_EMERGENCY_NUMBER_OVERRIDE"; |
| public static final String METHOD_NAME_SET_EMERGENCY_GESTURE = "SET_EMERGENCY_GESTURE"; |
| public static final String METHOD_NAME_SET_EMERGENCY_SOUND = "SET_EMERGENCY_SOUND"; |
| public static final String METHOD_NAME_GET_EMERGENCY_GESTURE_ENABLED = "GET_EMERGENCY_GESTURE"; |
| public static final String METHOD_NAME_GET_EMERGENCY_GESTURE_SOUND_ENABLED = |
| "GET_EMERGENCY_SOUND"; |
| public static final String EMERGENCY_GESTURE_CALL_NUMBER = "emergency_gesture_call_number"; |
| public static final String EMERGENCY_SETTING_VALUE = "emergency_setting_value"; |
| public static final int EMERGENCY_SETTING_ON = 1; |
| public static final int EMERGENCY_SETTING_OFF = 0; |
| |
| @VisibleForTesting |
| static final String FALL_BACK_NUMBER = "112"; |
| |
| private final Context mContext; |
| private final TelephonyManager mTelephonyManager; |
| private final CarrierConfigManager mCarrierConfigManager; |
| |
| public EmergencyNumberUtils(Context context) { |
| mContext = context; |
| if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| mTelephonyManager = context.getSystemService(TelephonyManager.class); |
| mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class); |
| } else { |
| mTelephonyManager = null; |
| mCarrierConfigManager = null; |
| } |
| } |
| |
| /** |
| * Returns the most appropriate number for police. |
| */ |
| public String getDefaultPoliceNumber() { |
| if (mTelephonyManager == null) { |
| return FALL_BACK_NUMBER; |
| } |
| final List<String> promotedPoliceNumber = getPromotedEmergencyNumbers( |
| EMERGENCY_SERVICE_CATEGORY_POLICE); |
| if (promotedPoliceNumber == null || promotedPoliceNumber.isEmpty()) { |
| return FALL_BACK_NUMBER; |
| } |
| return promotedPoliceNumber.get(0); |
| } |
| |
| /** |
| * Returns the number chosen by user. If user has not provided any number, use default ({@link |
| * #getDefaultPoliceNumber()}). |
| */ |
| public String getPoliceNumber() { |
| final String userProvidedNumber = getEmergencyNumberOverride(); |
| return TextUtils.isEmpty(userProvidedNumber) |
| ? getDefaultPoliceNumber() : userProvidedNumber; |
| } |
| |
| /** |
| * Sets device-local emergency number override |
| */ |
| public void setEmergencyNumberOverride(String number) { |
| final Bundle bundle = new Bundle(); |
| bundle.putString(EMERGENCY_GESTURE_CALL_NUMBER, number); |
| mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, |
| METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE, null /* args */, bundle); |
| } |
| |
| /** |
| * Enable/disable the emergency gesture setting |
| */ |
| public void setEmergencyGestureEnabled(boolean enabled) { |
| final Bundle bundle = new Bundle(); |
| bundle.putInt(EMERGENCY_SETTING_VALUE, |
| enabled ? EMERGENCY_SETTING_ON : EMERGENCY_SETTING_OFF); |
| mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, |
| METHOD_NAME_SET_EMERGENCY_GESTURE, null /* args */, bundle); |
| } |
| |
| /** |
| * Enable/disable the emergency gesture sound setting |
| */ |
| public void setEmergencySoundEnabled(boolean enabled) { |
| final Bundle bundle = new Bundle(); |
| bundle.putInt(EMERGENCY_SETTING_VALUE, |
| enabled ? EMERGENCY_SETTING_ON : EMERGENCY_SETTING_OFF); |
| mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, |
| METHOD_NAME_SET_EMERGENCY_SOUND, null /* args */, bundle); |
| } |
| |
| /** |
| * Whether or not emergency gesture is enabled. |
| */ |
| public boolean getEmergencyGestureEnabled() { |
| final Bundle bundle = mContext.getContentResolver().call( |
| EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, |
| METHOD_NAME_GET_EMERGENCY_GESTURE_ENABLED, null /* args */, null /* bundle */); |
| return bundle == null ? true : bundle.getInt(EMERGENCY_SETTING_VALUE, EMERGENCY_SETTING_ON) |
| == EMERGENCY_SETTING_ON; |
| } |
| |
| /** |
| * Whether or not emergency gesture sound is enabled. |
| */ |
| public boolean getEmergencyGestureSoundEnabled() { |
| final Bundle bundle = mContext.getContentResolver().call( |
| EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, |
| METHOD_NAME_GET_EMERGENCY_GESTURE_SOUND_ENABLED, null /* args */, |
| null /* bundle */); |
| return bundle == null ? true : bundle.getInt(EMERGENCY_SETTING_VALUE, EMERGENCY_SETTING_OFF) |
| == EMERGENCY_SETTING_ON; |
| } |
| |
| private String getEmergencyNumberOverride() { |
| final Bundle bundle = mContext.getContentResolver().call( |
| EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, |
| METHOD_NAME_GET_EMERGENCY_NUMBER_OVERRIDE, null /* args */, null /* bundle */); |
| return bundle == null ? null : bundle.getString(EMERGENCY_GESTURE_CALL_NUMBER); |
| } |
| |
| private List<String> getPromotedEmergencyNumbers(int categories) { |
| Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList( |
| categories); |
| if (allLists == null || allLists.isEmpty()) { |
| Log.w(TAG, "Unable to retrieve emergency number lists!"); |
| return new ArrayList<>(); |
| } |
| Map<Integer, List<String>> promotedEmergencyNumberLists = new ArrayMap<>(); |
| for (Map.Entry<Integer, List<EmergencyNumber>> entry : allLists.entrySet()) { |
| if (entry.getKey() == null || entry.getValue() == null) { |
| continue; |
| } |
| int subId = entry.getKey(); |
| List<EmergencyNumber> emergencyNumberList = entry.getValue(); |
| Log.d(TAG, "Emergency numbers for subscription id " + entry.getKey()); |
| |
| // The list of promoted emergency numbers which will be visible on shortcut view. |
| List<EmergencyNumber> promotedList = new ArrayList<>(); |
| // A temporary list for non-prioritized emergency numbers. |
| List<EmergencyNumber> tempList = new ArrayList<>(); |
| |
| for (EmergencyNumber emergencyNumber : emergencyNumberList) { |
| // Emergency numbers in DATABASE are prioritized since they were well-categorized. |
| boolean isFromPrioritizedSource = |
| emergencyNumber.getEmergencyNumberSources().contains( |
| EMERGENCY_NUMBER_SOURCE_DATABASE); |
| |
| Log.d(TAG, String.format("Number %s, isFromPrioritizedSource %b", |
| emergencyNumber, isFromPrioritizedSource)); |
| if (isFromPrioritizedSource) { |
| promotedList.add(emergencyNumber); |
| } else { |
| tempList.add(emergencyNumber); |
| } |
| } |
| // Puts numbers in temp list after prioritized numbers. |
| promotedList.addAll(tempList); |
| |
| if (!promotedList.isEmpty()) { |
| List<String> sanitizedNumbers = sanitizeEmergencyNumbers(promotedList, subId); |
| promotedEmergencyNumberLists.put(subId, sanitizedNumbers); |
| } |
| } |
| |
| if (promotedEmergencyNumberLists.isEmpty()) { |
| Log.w(TAG, "No promoted emergency number found!"); |
| } |
| return promotedEmergencyNumberLists.get(SubscriptionManager.getDefaultSubscriptionId()); |
| } |
| |
| private List<String> sanitizeEmergencyNumbers( |
| List<EmergencyNumber> input, int subscriptionId) { |
| // Make a copy of data so we can mutate. |
| List<EmergencyNumber> data = new ArrayList<>(input); |
| String[] carrierPrefixes = |
| getCarrierEmergencyNumberPrefixes(mCarrierConfigManager, subscriptionId); |
| return data.stream() |
| .map(d -> removePrefix(d, carrierPrefixes)) |
| .collect(Collectors.toCollection(ArrayList::new)); |
| } |
| |
| private String removePrefix(EmergencyNumber emergencyNumber, String[] prefixes) { |
| String number = emergencyNumber.getNumber(); |
| if (prefixes == null || prefixes.length == 0) { |
| return number; |
| } |
| for (String prefix : prefixes) { |
| int prefixStartIndex = number.indexOf(prefix); |
| if (prefixStartIndex != 0) { |
| continue; |
| } |
| Log.d(TAG, "Removing prefix " + prefix + " from " + number); |
| return number.substring(prefix.length()); |
| } |
| return number; |
| } |
| |
| private static String[] getCarrierEmergencyNumberPrefixes( |
| CarrierConfigManager carrierConfigManager, int subId) { |
| PersistableBundle b = carrierConfigManager.getConfigForSubId(subId); |
| return b == null |
| ? null |
| : b.getStringArray(CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY); |
| } |
| } |