diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 3af4b25fb40c0..64dc2afafd6ae 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -51,6 +51,7 @@ java_defaults { "SettingsLibRadioButtonPreference", "SettingsLibDisplayDensityUtils", "SettingsLibUtils", + "SettingsLibEmergencyNumber", "SettingsLibTopIntroPreference", ], } diff --git a/packages/SettingsLib/EmergencyNumber/Android.bp b/packages/SettingsLib/EmergencyNumber/Android.bp new file mode 100644 index 0000000000000..3c41f7834d6ca --- /dev/null +++ b/packages/SettingsLib/EmergencyNumber/Android.bp @@ -0,0 +1,10 @@ +android_library { + name: "SettingsLibEmergencyNumber", + + srcs: ["src/**/*.java"], + static_libs: [ + "androidx.annotation_annotation", + ], + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/EmergencyNumber/AndroidManifest.xml b/packages/SettingsLib/EmergencyNumber/AndroidManifest.xml new file mode 100644 index 0000000000000..b877fc4ba5310 --- /dev/null +++ b/packages/SettingsLib/EmergencyNumber/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java new file mode 100644 index 0000000000000..12d21cae48d4e --- /dev/null +++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java @@ -0,0 +1,133 @@ +/* + * 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.Context; +import android.content.pm.PackageManager; +import android.provider.Settings; +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; + +/** + * Util class to help manage emergency numbers + */ +public class EmergencyNumberUtils { + private static final String TAG = "EmergencyNumberUtils"; + private static final String EMERGENCY_GESTURE_CALL_NUMBER = "emergency_gesture_call_number"; + @VisibleForTesting + static final String FALL_BACK_NUMBER = "112"; + + private final Context mContext; + private final TelephonyManager mTelephonyManager; + + + public EmergencyNumberUtils(Context context) { + mContext = context; + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + mTelephonyManager = context.getSystemService(TelephonyManager.class); + } else { + mTelephonyManager = null; + } + } + + /** + * Returns the most appropriate number for police. + */ + public String getDefaultPoliceNumber() { + if (mTelephonyManager == null) { + return FALL_BACK_NUMBER; + } + final List promotedPoliceNumber = getPromotedEmergencyNumbers( + EMERGENCY_SERVICE_CATEGORY_POLICE); + if (promotedPoliceNumber == null || promotedPoliceNumber.isEmpty()) { + return FALL_BACK_NUMBER; + } + return promotedPoliceNumber.get(0).getNumber(); + } + + /** + * Returns the number chosen by user. If user has not provided any number, use default ({@link + * #getDefaultPoliceNumber()}). + */ + public String getPoliceNumber() { + final String userProvidedNumber = Settings.Secure.getString(mContext.getContentResolver(), + EMERGENCY_GESTURE_CALL_NUMBER); + return TextUtils.isEmpty(userProvidedNumber) + ? getDefaultPoliceNumber() : userProvidedNumber; + } + + private List getPromotedEmergencyNumbers(int categories) { + Map> allLists = mTelephonyManager.getEmergencyNumberList( + categories); + if (allLists == null || allLists.isEmpty()) { + Log.w(TAG, "Unable to retrieve emergency number lists!"); + return new ArrayList<>(); + } + Map> promotedEmergencyNumberLists = new ArrayMap<>(); + for (Map.Entry> entry : allLists.entrySet()) { + if (entry.getKey() == null || entry.getValue() == null) { + continue; + } + List 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 promotedList = new ArrayList<>(); + // A temporary list for non-prioritized emergency numbers. + List 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()) { + promotedEmergencyNumberLists.put(entry.getKey(), promotedList); + } + } + + if (promotedEmergencyNumberLists.isEmpty()) { + Log.w(TAG, "No promoted emergency number found!"); + } + return promotedEmergencyNumberLists.get(SubscriptionManager.getDefaultSubscriptionId()); + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java new file mode 100644 index 0000000000000..140363127dff4 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java @@ -0,0 +1,124 @@ +/* + * 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_SERVICE_CATEGORY_POLICE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.PackageManager; +import android.provider.Settings; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.telephony.emergency.EmergencyNumber; +import android.util.ArrayMap; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@RunWith(RobolectricTestRunner.class) +public class EmergencyNumberUtilsTest { + private static final String TELEPHONY_EMERGENCY_NUMBER = "1234"; + private static final String USER_OVERRIDE_EMERGENCY_NUMBER = "5678"; + + @Mock + private Context mContext; + @Mock + private PackageManager mPackageManager; + @Mock + private TelephonyManager mTelephonyManager; + private EmergencyNumberUtils mUtils; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + } + + @Test + public void getDefaultPoliceNumber_noTelephony_shouldReturnDefault() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false); + mUtils = new EmergencyNumberUtils(mContext); + + assertThat(mUtils.getDefaultPoliceNumber()).isEqualTo( + EmergencyNumberUtils.FALL_BACK_NUMBER); + } + + @Test + public void getDefaultPoliceNumber_hasTelephony_shouldLoadFromTelephony() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true); + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); + addEmergencyNumberToTelephony(); + mUtils = new EmergencyNumberUtils(mContext); + + + assertThat(mUtils.getDefaultPoliceNumber()).isEqualTo(TELEPHONY_EMERGENCY_NUMBER); + } + + @Test + public void getPoliceNumber_hasUserOverride_shouldLoadFromUserOverride() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true); + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); + addEmergencyNumberToTelephony(); + + ContentResolver resolver = RuntimeEnvironment.application.getContentResolver(); + when(mContext.getContentResolver()).thenReturn(resolver); + Settings.Secure.putString(resolver, Settings.Secure.EMERGENCY_GESTURE_CALL_NUMBER, + USER_OVERRIDE_EMERGENCY_NUMBER); + + mUtils = new EmergencyNumberUtils(mContext); + + assertThat(mUtils.getPoliceNumber()).isEqualTo(USER_OVERRIDE_EMERGENCY_NUMBER); + } + + @Test + public void getPoliceNumber_noUserOverride_shouldLoadFromTelephony() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true); + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); + addEmergencyNumberToTelephony(); + + mUtils = new EmergencyNumberUtils(mContext); + + assertThat(mUtils.getPoliceNumber()).isEqualTo(TELEPHONY_EMERGENCY_NUMBER); + } + + private void addEmergencyNumberToTelephony() { + final int subId = SubscriptionManager.getDefaultSubscriptionId(); + EmergencyNumber emergencyNumber = mock(EmergencyNumber.class); + Map> numbers = new ArrayMap<>(); + List numbersForSubId = new ArrayList<>(); + numbersForSubId.add(emergencyNumber); + numbers.put(subId, numbersForSubId); + when(mTelephonyManager.getEmergencyNumberList( + EMERGENCY_SERVICE_CATEGORY_POLICE)).thenReturn(numbers); + when(emergencyNumber.getNumber()).thenReturn(TELEPHONY_EMERGENCY_NUMBER); + } +}