Merge "Fix unlock sim thread leak" into main am: 71c1c0a525 am: decd41773b
Original change: https://android-review.googlesource.com/c/platform/packages/services/Telephony/+/2714253
Change-Id: Ie992353bf213f6901c1fd6f685aeb7adde2153a5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index dad0e6c..dc35c5d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -43,6 +43,7 @@
"PlatformProperties",
"modules-utils-os",
"nist-sip",
+ "service-entitlement"
],
srcs: [
@@ -79,6 +80,16 @@
},
}
+// Allow other applications to use public constants from SlicePurchaseController
+java_library {
+ name: "SlicePurchaseController",
+ srcs: ["src/com/android/phone/slice/*.java",],
+ libs: [
+ "telephony-common",
+ "service-entitlement"
+ ],
+}
+
platform_compat_config {
name: "TeleService-platform-compat-config",
src: ":TeleService",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a427bac..8d03ed7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -19,8 +19,11 @@
package="com.android.phone"
coreApp="true"
android:sharedUserId="android.uid.phone"
- android:sharedUserLabel="@string/phoneAppLabel"
->
+ android:sharedUserLabel="@string/phoneAppLabel">
+
+ <!-- Allows broadcasting for SlicePurchaseController events. -->
+ <protected-broadcast android:name="com.android.phone.slice.action.START_SLICE_PURCHASE_APP" />
+ <protected-broadcast android:name="com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_TIMEOUT" />
<original-package android:name="com.android.phone" />
<!-- Allows granting runtime permissions to telephony related components. -->
@@ -132,6 +135,8 @@
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
<uses-permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE" />
+ <uses-permission android:name="android.permission.BIND_SATELLITE_GATEWAY_SERVICE" />
+ <uses-permission android:name="android.permission.BIND_SATELLITE_SERVICE" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
<uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
@@ -153,13 +158,19 @@
<uses-permission android:name="android.permission.ACCESS_TELEPHONY_SIMINFO_DB"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+ <!-- Needed to block unwanted malicious pop up overlays -->
+ <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
+
+ <!-- Needed to set user association to a certain sim -->
+ <uses-permission android:name="android.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION"/>
+
+ <!-- Needed to register for UWB state changes for satellite communication -->
+ <uses-permission android:name="android.permission.UWB_PRIVILEGED"/>
+
<permission android:name="com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID"
android:label="Access last known cell identity."
android:protectionLevel="signature"/>
- <!-- Needed to block unwanted malicious pop up overlays -->
- <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
-
<application android:name="PhoneApp"
android:persistent="true"
android:label="@string/phoneAppLabel"
@@ -418,6 +429,10 @@
"android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS" />
<action android:name=
"android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION" />
+ <action android:name=
+ "android.telephony.euicc.action.TRANSFER_EMBEDDED_SUBSCRIPTIONS" />
+ <action android:name=
+ "android.telephony.euicc.action.CONVERT_TO_EMBEDDED_SUBSCRIPTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
@@ -567,6 +582,14 @@
android:configChanges="orientation|screenSize|keyboardHidden"
android:theme="@style/Theme.Transparent"/>
+ <activity
+ android:name="com.android.phone.ErrorDialogActivity"
+ android:exported="false"
+ android:excludeFromRecents="true"
+ android:launchMode="singleTop"
+ android:configChanges="orientation|screenSize|keyboardHidden"
+ android:theme="@style/Theme.Telephony.Transparent"/>
+
<service
android:name="com.android.phone.vvm.RemoteVvmTaskManager"
android:exported="false"/>
diff --git a/ecc/conversion_toolset_v1/proto/protobuf_ecc_data.proto b/ecc/conversion_toolset_v1/proto/protobuf_ecc_data.proto
index c1a826a..088b5b7 100644
--- a/ecc/conversion_toolset_v1/proto/protobuf_ecc_data.proto
+++ b/ecc/conversion_toolset_v1/proto/protobuf_ecc_data.proto
@@ -19,11 +19,27 @@
AIEC = 7;
}
+ enum Routing {
+ UNKNOWN = 0;
+ EMERGENCY = 1;
+ NORMAL= 2;
+ }
+
// Required: Every EccInfo shall contain a phone number.
optional string phone_number = 1;
// Extra rules: Every Ecc should have at least 1 valid type.
repeated Type types = 2 [packed=true];
+
+
+ //Optional: By default, routing is assumed to be 'UNKNOWN'
+ optional Routing routing = 3 [default = UNKNOWN];
+
+ //Optional: This field is evaluated only if routing is set to NORMAL
+ //If the field is empty, NORMAL routing is used for all MNCs
+ //Else normal routing is used only for list of MNCs specified
+ repeated string normal_routing_mncs = 4;
+
}
// CountryInfo represents available ECCs of a country/region, recognized
@@ -44,6 +60,13 @@
// Per http://www.etsi.org/deliver/etsi_ts/122100_122199/122101/09.01.00_60/ts_122101v090100p.pdf,
// 112 and 911 shall always be available.
optional string ecc_fallback = 3;
+
+ // Required: Every CountryInfo shall specify whether emergency numbers sourced from modem config
+ // should be ignored.
+ //
+ // If this value is set to true, we have a pretty good authority of emergency numbers in the
+ // android emergency number db for this country.
+ optional bool ignore_modem_config = 4 [default = false];
}
message AllInfo {
diff --git a/ecc/input/eccdata.txt b/ecc/input/eccdata.txt
index a8b021e..7252480 100644
--- a/ecc/input/eccdata.txt
+++ b/ecc/input/eccdata.txt
@@ -114,6 +114,18 @@
types: AMBULANCE
types: FIRE
}
+ eccs {
+ phone_number: "100"
+ types: FIRE
+ }
+ eccs {
+ phone_number: "101"
+ types: POLICE
+ }
+ eccs {
+ phone_number: "107"
+ types: AMBULANCE
+ }
ecc_fallback: "911"
}
countries {
@@ -134,6 +146,31 @@
types: AMBULANCE
types: FIRE
}
+ eccs {
+ phone_number: "122"
+ types: FIRE
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "133"
+ types: POLICE
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "144"
+ types: AMBULANCE
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "128"
+ types: MOUNTAIN_RESCUE
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "140"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
ecc_fallback: "112"
}
countries {
@@ -215,6 +252,19 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "100"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "101"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
}
ecc_fallback: "112"
}
@@ -320,14 +370,22 @@
eccs {
phone_number: "190"
types: POLICE
+ routing: EMERGENCY
}
eccs {
phone_number: "192"
types: AMBULANCE
+ routing: NORMAL
}
eccs {
phone_number: "193"
types: FIRE
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "185"
+ types: MARINE_GUARD
+ routing: NORMAL
}
ecc_fallback: "911"
}
@@ -407,6 +465,12 @@
types: AMBULANCE
types: FIRE
}
+ eccs {
+ phone_number: "112"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ }
ecc_fallback: "911"
}
countries {
@@ -438,16 +502,57 @@
countries {
iso_code: "CH"
eccs {
+ phone_number: "112"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "911"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
phone_number: "117"
types: POLICE
+ routing: EMERGENCY
}
eccs {
phone_number: "144"
types: AMBULANCE
+ routing: NORMAL
}
eccs {
phone_number: "118"
types: FIRE
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "143"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "145"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "147"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "1414"
+ types: TYPE_UNSPECIFIED
+ }
+ eccs {
+ phone_number: "0800117117"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
}
ecc_fallback: "112"
}
@@ -482,6 +587,7 @@
eccs {
phone_number: "133"
types: POLICE
+ routing: EMERGENCY
}
eccs {
phone_number: "131"
@@ -627,6 +733,7 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -647,6 +754,7 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "911"
}
@@ -667,6 +775,12 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "115"
+ types: POLICE
+ routing: NORMAL
}
ecc_fallback: "911"
}
@@ -677,6 +791,7 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -719,6 +834,77 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "088"
+ types: POLICE
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "085"
+ types: FIRE
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "1006"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "061"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "062"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "080"
+ types: FIRE
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "091"
+ types: POLICE
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "092"
+ types: POLICE
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "024"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "016"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "017"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "021"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "116111"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "900018018"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
}
ecc_fallback: "112"
}
@@ -739,6 +925,7 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -792,26 +979,32 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
eccs {
phone_number: "15"
types: AMBULANCE
+ routing: NORMAL
}
eccs {
phone_number: "17"
types: POLICE
+ routing: NORMAL
}
eccs {
phone_number: "18"
types: FIRE
+ routing: NORMAL
}
eccs {
phone_number: "115"
types: TYPE_UNSPECIFIED
+ routing: NORMAL
}
eccs {
phone_number: "116000"
types: TYPE_UNSPECIFIED
+ routing: NORMAL
}
eccs {
phone_number: "114"
@@ -819,27 +1012,33 @@
}
eccs {
phone_number: "191"
- types: TYPE_UNSPECIFIED
+ types: MOUNTAIN_RESCUE
+ routing: NORMAL
}
eccs {
phone_number: "196"
types: MARINE_GUARD
+ routing: NORMAL
}
eccs {
phone_number: "197"
types: TYPE_UNSPECIFIED
+ routing: NORMAL
}
eccs {
phone_number: "116117"
types: TYPE_UNSPECIFIED
- }
- eccs {
- phone_number: "116111"
- types: TYPE_UNSPECIFIED
+ routing: NORMAL
}
eccs {
phone_number: "119"
types: TYPE_UNSPECIFIED
+ routing: NORMAL
+ }
+ eccs {
+ phone_number: "116111"
+ types: TYPE_UNSPECIFIED
+ routing: NORMAL
}
ecc_fallback: "112"
}
@@ -860,6 +1059,7 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -975,14 +1175,17 @@
eccs {
phone_number: "17"
types: POLICE
+ routing: NORMAL
}
eccs {
phone_number: "15"
types: AMBULANCE
+ routing: NORMAL
}
eccs {
phone_number: "18"
types: FIRE
+ routing: NORMAL
}
ecc_fallback: "112"
}
@@ -1225,6 +1428,18 @@
types: AMBULANCE
types: FIRE
}
+ eccs {
+ phone_number: "113"
+ types: POLICE
+ }
+ eccs {
+ phone_number: "115"
+ types: FIRE
+ }
+ eccs {
+ phone_number: "118"
+ types: AMBULANCE
+ }
ecc_fallback: "112"
}
countries {
@@ -1252,11 +1467,18 @@
eccs {
phone_number: "110"
types: POLICE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "118"
+ types: MARINE_GUARD
+ routing: EMERGENCY
}
eccs {
phone_number: "119"
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -1358,11 +1580,42 @@
eccs {
phone_number: "112"
types: POLICE
+ routing: EMERGENCY
}
eccs {
phone_number: "119"
- types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "122"
+ types: MARINE_GUARD
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "111"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "113"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "117"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "118"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "125"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -1497,6 +1750,7 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -1786,6 +2040,7 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "911"
}
@@ -1901,8 +2156,17 @@
eccs {
phone_number: "112"
types: POLICE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "113"
types: AMBULANCE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "110"
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -1955,6 +2219,21 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "112"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "911"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -1986,6 +2265,7 @@
eccs {
phone_number: "105"
types: POLICE
+ routing: EMERGENCY
}
eccs {
phone_number: "110"
@@ -2062,6 +2342,77 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "984"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "985"
+ types: MOUNTAIN_RESCUE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "986"
+ types: POLICE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "997"
+ types: POLICE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "998"
+ types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "999"
+ types: AMBULANCE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "991"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "992"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "993"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "994"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "995"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "996"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "987"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "989"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -2114,6 +2465,14 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "911"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -2134,6 +2493,7 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "911"
}
@@ -2152,14 +2512,17 @@
eccs {
phone_number: "17"
types: POLICE
+ routing: NORMAL
}
eccs {
phone_number: "15"
types: AMBULANCE
+ routing: NORMAL
}
eccs {
phone_number: "18"
types: FIRE
+ routing: NORMAL
}
ecc_fallback: "112"
}
@@ -2196,6 +2559,37 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "101"
+ types: FIRE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "102"
+ types: POLICE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "103"
+ types: AMBULANCE
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "104"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "121"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
+ }
+ eccs {
+ phone_number: "123"
+ types: TYPE_UNSPECIFIED
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -2274,6 +2668,7 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "112"
}
@@ -2694,6 +3089,7 @@
types: POLICE
types: AMBULANCE
types: FIRE
+ routing: EMERGENCY
}
ecc_fallback: "911"
}
@@ -2705,6 +3101,18 @@
types: AMBULANCE
types: FIRE
}
+ eccs {
+ phone_number: "104"
+ types: FIRE
+ }
+ eccs {
+ phone_number: "105"
+ types: AMBULANCE
+ }
+ eccs {
+ phone_number: "106"
+ types: MARINE_GUARD
+ }
ecc_fallback: "911"
}
countries {
diff --git a/ecc/output/eccdata b/ecc/output/eccdata
index 8427603..fc86961 100644
--- a/ecc/output/eccdata
+++ b/ecc/output/eccdata
Binary files differ
diff --git a/res/layout/band_mode.xml b/res/layout/band_mode.xml
deleted file mode 100644
index b43dd1d..0000000
--- a/res/layout/band_mode.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:padding="4dip"
- android:gravity="center_horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <ListView android:id="@+id/band"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:textSize="7sp">
- </ListView>
-
-</LinearLayout>
diff --git a/res/layout/radio_info.xml b/res/layout/radio_info.xml
index 2a2ad89..ffe63de 100644
--- a/res/layout/radio_info.xml
+++ b/res/layout/radio_info.xml
@@ -4,22 +4,24 @@
**
** Copyright 2006, 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
+** 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
+** 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
+** 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.
*/
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:layoutDirection="locale"
+ android:textDirection="locale">
<LinearLayout style="@style/info_layout"
android:descendantFocusability="beforeDescendants"
@@ -92,6 +94,12 @@
<TextView android:id="@+id/data_network" style="@style/info_value" />
</LinearLayout>
+ <!-- Data Raw Registration State -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_data_raw_registration_state_label" style="@style/info_label" />
+ <TextView android:id="@+id/data_raw_registration_state" style="@style/info_value" />
+ </LinearLayout>
+
<!-- Override Network Type -->
<LinearLayout style="@style/RadioInfo_entry_layout">
<TextView android:text="@string/radio_info_override_network_type_label" style="@style/info_label" />
@@ -110,6 +118,18 @@
<TextView android:id="@+id/voice_network" style="@style/info_value" />
</LinearLayout>
+ <!-- Voice Raw Registration State -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_voice_raw_registration_state_label" style="@style/info_label" />
+ <TextView android:id="@+id/voice_raw_registration_state" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- PS IWLAN Raw Registration State -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_wlan_data_raw_registration_state_label" style="@style/info_label" />
+ <TextView android:id="@+id/wlan_data_raw_registration_state" style="@style/info_value" />
+ </LinearLayout>
+
<!-- Signal Strength -->
<LinearLayout style="@style/RadioInfo_entry_layout">
<TextView android:text="@string/radio_info_signal_strength_label" style="@style/info_label" />
@@ -165,12 +185,6 @@
<TextView android:id="@+id/network_slicing_config" style="@style/info_value" />
</LinearLayout>
- <!-- Physical Channel Config -->
- <LinearLayout style="@style/RadioInfo_entry_layout">
- <TextView android:text="@string/radio_info_phy_chan_config" style="@style/info_label" />
- <TextView android:id="@+id/phy_chan_config" style="@style/info_value" />
- </LinearLayout>
-
<!-- Horizontal Rule -->
<View
android:layout_width="fill_parent"
@@ -204,6 +218,14 @@
android:layout_height="wrap_content"
android:text="@string/radio_info_radio_power"/>
+ <!-- Simulate out of service -->
+ <Switch android:id="@+id/simulate_out_of_service"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/simulate_out_of_service_string"/>
+
<!-- VoLTE provisioned -->
<Switch android:id="@+id/volte_provisioned_switch"
android:textSize="14sp"
@@ -281,7 +303,7 @@
android:layout_height="wrap_content"
android:text="@string/ping_test_label"
/>
-
+
<LinearLayout style="@style/RadioInfo_entry_layout">
<TextView android:text="@string/radio_info_ping_hostname_v4" style="@style/info_label" />
<TextView android:id="@+id/pingHostnameV4" style="@style/info_value" />
@@ -368,6 +390,12 @@
android:layout_height="wrap_content"
/>
+ <!-- Physical Channel Config -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_phy_chan_config" style="@style/info_label" />
+ <TextView android:id="@+id/phy_chan_config" style="@style/info_value" />
+ </LinearLayout>
+
<!-- CellInfo -->
<LinearLayout style="@style/RadioInfo_entry_layout">
<TextView android:text="@string/radio_info_cellinfo_label"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index bf10c79..2c8117b 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Kanselleer"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Stel Verwyderbare-e-SIM as Verstek"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobieleradiokrag"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simuleer is nie beskikbaar nie (Slegs ontfoutingbou)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Bekyk SIM-adresboek"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Kyk na vaste skakelnommers"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Bekyk skakeldiensnommers"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Gekoppel"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Opgeskort"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Onbekend"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primêr"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"grepe"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data gestuur:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Boodskap wag:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Foonnommer:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Kies radioband"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Stemnetwerktipe:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Datanetwerktipe:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Ignoreer Netwerktipe:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Onverwerkte stem se registrasiestatus:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Onverwerkte data se registrasiestatus:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Onverwerkte WLAN-data se registrasiestatus:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Kies foonindeks"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Stel voorkeurnetwerktipe:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Pieng gasheernaam(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Jou bluetooth-sein is swak. Probeer om na luidsprekerfoon oor te skakel."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Kennisgewing oor oproepgehalte"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Opgeskorte SIP-rekeninge"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Kan nie boodskap van hierdie profiel af stuur nie"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Jou werkbeleid laat jou toe om boodskappe slegs van die werkprofiel af te stuur"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Kanselleer"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Skakel oor na werkprofiel"</string>
</resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index d0e0701..b3b063e 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -171,7 +171,7 @@
<string name="vm_change_pin_error_mismatch" msgid="5364847280026257331">"የቀድሞ ፒን አልተዛመደም።"</string>
<string name="vm_change_pin_error_invalid" msgid="5230002671175580674">"አዲሱ ፒን ልክ ያልሆኑ ቁምፊዎችን ይዟል።"</string>
<string name="vm_change_pin_error_system_error" msgid="9116483527909681791">"ፒን መቀየር አልተቻለም።"</string>
- <string name="vvm_unsupported_message_format" msgid="4206402558577739713">"የማይደገፍ የመልዕክት ዓይነት፤ ለማዳመጥ ወደ <xliff:g id="NUMBER">%s</xliff:g> ይደውሉ።"</string>
+ <string name="vvm_unsupported_message_format" msgid="4206402558577739713">"የማይደገፍ የመልዕክት አይነት፤ ለማዳመጥ ወደ <xliff:g id="NUMBER">%s</xliff:g> ይደውሉ።"</string>
<string name="network_settings_title" msgid="7560807107123171541">"የተንቀሳቃሽ ስልክ አውታረ መረብ"</string>
<string name="label_available" msgid="1316084116670821258">"የሚገኙ አውታረመረቦች"</string>
<string name="load_networks_progress" msgid="4051433047717401683">"በመፈለግ ላይ…"</string>
@@ -186,9 +186,9 @@
<string name="manual_mode_disallowed_summary" msgid="3970048592179890197">"ከ %1$s ጋር በተገናኘ ጊዜ አይገኝም"</string>
<string name="network_select_title" msgid="4117305053881611988">"አውታረ መረብ"</string>
<string name="register_automatically" msgid="3907580547590554834">"ራስ ሰር ምዝገባ...."</string>
- <string name="preferred_network_mode_title" msgid="5253395265169539830">"የሚመረጠው የአውታረ መረብ ዓይነት"</string>
+ <string name="preferred_network_mode_title" msgid="5253395265169539830">"የሚመረጠው የአውታረ መረብ አይነት"</string>
<string name="preferred_network_mode_summary" msgid="3787989000044330064">"የአውታረመረቡንመከወኛ ሁነታ ለውጥ"</string>
- <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"የሚመረጠው የአውታረ መረብ ዓይነት"</string>
+ <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"የሚመረጠው የአውታረ መረብ አይነት"</string>
<string name="forbidden_network" msgid="5081729819561333023">"(የተከለከለ)"</string>
<string name="choose_network_title" msgid="5335832663422653082">"አውታረ መረብ ይምረጡ"</string>
<string name="network_disconnected" msgid="8844141106841160825">"ግንኙነት ተቋርጧል"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"ይቅር"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"የሚወገድን ኢሲም ነባሪ በሚል አቀናብር"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"የሞባይል ሬዲዮ ኃይል"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"ከአገልግሎት ውጭን አስመስል (የስህተት ማረሚያ ግንብ ብቻ)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"የሲም አድራሻ ደብተር አሳይ"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"በቋሚነት የሚደወልባቸው ቁጥሮች"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"የአገልግሎት መደወያ ቁጥሮችን ተመልከት"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"ተገናኝቷል"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"ታግዷል"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"ያልታወቀ"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"ቀዳሚ"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"ባይቶች"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,12 +889,14 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"የተላከ ውሂብ፦"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"መልዕክት በመጠበቅ ላይ፡"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ስልክ ቁጥር:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"የሬዲዮ ባንድ ይምረጡ"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"የድምፅ የአውታረ መረብ ዓይነት፡"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"የውሂብ አውታረ መረብ ዓይነት፡"</string>
- <string name="radio_info_override_network_type_label" msgid="4176280017221092005">"የአውታረ መረብ ዓይነት ሻር፦"</string>
+ <string name="radio_info_override_network_type_label" msgid="4176280017221092005">"የአውታረ መረብ አይነት ሻር፦"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"የጥሬ ድምጽ ምዝገባ ሁነታ፦"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"የጥሬ ውሂብ ምዝገባ ሁነታ፦"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"የWLAN ጥሬ ውሂብ ምዝገባ ሁነታ፦"</string>
<string name="phone_index_label" msgid="6222406512768964268">"የስልክ መረጃ ጠቋሚ ይምረጡ"</string>
- <string name="radio_info_set_perferred_label" msgid="7408131389363136210">"ተመራጭ የአውታረ መረብ ዓይነት ያዋቅሩ፡"</string>
+ <string name="radio_info_set_perferred_label" msgid="7408131389363136210">"ተመራጭ የአውታረ መረብ አይነት ያዋቅሩ፡"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"ፒንግ ስመ ከዳም(www.google.com) IPv4፦"</string>
<string name="radio_info_ping_hostname_v6" msgid="2748637889486554603">"ፒንግ ስመ ካዳም(www.google.com) IPv6፡"</string>
<string name="radio_info_http_client_test" msgid="1329583721088428238">"የኤችቲቲፒ ደንበኛ ሙከራ:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"የእርስዎ የብሉቱዝ ሲግናል ደካማ ነው። ወደ የስልክ ድምፅ ማጉያ ለመቀየር ይሞክሩ።"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"የጥሪ ጥራት ማሳወቂያ"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"የተቋረጡ የSIP መለያዎች"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"ከዚህ መገለጫ መልዕክት መላክ አይቻልም"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"የሥራ መመሪያዎ እርስዎ ከሥራ መገለጫው ብቻ መልዕክት እንዲልኩ ይፈቅድልዎታል"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"ይቅር"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"ወደ የሥራ መገለጫ ቀይር"</string>
</resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 866411c..1ee5e44 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"إلغاء"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"ضبط شريحة eSIM القابلة للإزالة كشريحة تلقائية"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"طاقة اللاسلكي للجوّال"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"محاكاة الخطأ \"خارج الخدمة\" (الإصدار المخصص لتصحيح الأخطاء فقط)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"عرض دفتر عناوين SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"عرض أرقام الطلب الثابت"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"عرض أرقام طلب الخدمة"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"متّصل بالإنترنت"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"معلّق"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"غير معروف"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"أساسي"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"الحزم"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"بايت"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"بالديسيبل"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"البيانات المُرسَلة:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"انتظار الرسائل:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"رقم الهاتف:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"تحديد النطاق اللاسلكي"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"نوع الشبكة الصوتية:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"نوع شبكة البيانات:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"تجاهل نوع الشبكة:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"حالة التسجيل الأولية للصوت:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"حالة التسجيل الأولية للبيانات:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"حالة التسجيل الأولية لبيانات WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"اختيار فهرس الهاتف"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"تحديد نوع الشبكة المفضّل:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"فحص اتصال اسم المضيف (www.google.com) عبر IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"إشارة البلوتوث ضعيفة. حاوِل التبديل إلى مكبّر الصوت."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"إشعار بشأن جودة المكالمة"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"حسابات SIP المتوقّفة"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"لا يمكن إرسال رسالة من هذا الملف الشخصي"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"تسمح لك سياسة العمل بإرسال الرسائل من الملف الشخصي للعمل فقط."</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"إلغاء"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"التبديل إلى الملف الشخصي للعمل"</string>
</resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index b864691..f66b63c 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"বাতিল কৰক"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"আঁতৰাব পৰা ই-ছিম ডিফ’ল্ট হিচাপে ছেট কৰক"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"ম’বাইলৰ ৰেডিঅ’ পাৱাৰ"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"সেৱাত নাই ছিমুলে’ট কৰক (কেৱল ডিবাগ বিল্ড)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"ছিম ঠিকনা সূচী চাওক"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"ফিক্সড্ ডায়েলিং নম্বৰসমূহ চাওক"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"সেৱা ডায়েলিং নম্বৰসমূহ চাওক"</string>
@@ -849,7 +850,7 @@
<string name="radio_info_ims_reg_status" msgid="25582845222446390">"IMS পঞ্জীয়ন: <xliff:g id="STATUS">%1$s</xliff:g>\nভইচ অভাৰ LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nভইচ অভাৰ ৱাই-ফাই: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nভিডিঅ’ কলিং: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nUT ইণ্টাৰফে’চ: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
<string name="radioInfo_service_in" msgid="45753418231446400">"সেৱাত আছে"</string>
<string name="radioInfo_service_out" msgid="287972405416142312">"সেৱাত নাই"</string>
- <string name="radioInfo_service_emergency" msgid="4763879891415016848">"কেৱল জৰুৰীকালীন কল"</string>
+ <string name="radioInfo_service_emergency" msgid="4763879891415016848">"কেৱল জৰুৰীকালীন কলসমূহ"</string>
<string name="radioInfo_service_off" msgid="3456583511226783064">"ৰেডিঅ’ অফ হৈ আছে"</string>
<string name="radioInfo_roaming_in" msgid="3156335577793145965">"ৰ’মিং"</string>
<string name="radioInfo_roaming_not" msgid="1904547918725478110">"ৰ’মিঙত নাই"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"সংযোগ কৰা হ’ল"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"নিলম্বন কৰা হৈছে"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"অজ্ঞাত"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"প্ৰাথমিক"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"পিকেটিএছ"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"বাইটসমূহ"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"ডিবিএম"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"প্ৰেৰণ কৰা ডেটা:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"বাৰ্তা অপেক্ষাৰত:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ফ’ন নম্বৰ:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"ৰেডিঅ’ বেণ্ড বাছনি কৰক"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"ভইচ নেটৱৰ্কৰ প্ৰকাৰ:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ডেটা নেটৱৰ্কৰ প্ৰকাৰ:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"নেটৱৰ্ক প্ৰকাৰ অ’ভাৰৰাইড কৰক:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"কণ্ঠস্বৰৰ অসম্পাদিত পঞ্জীয়নৰ স্থিতি:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"ডেটাৰ অসম্পাদিত পঞ্জীয়নৰ স্থিতি:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN ডেটাৰ অসম্পাদিত পঞ্জীয়নৰ স্থিতি:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ফ’নৰ ইনডেক্স বাছনি কৰক"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"পচন্দৰ নেটৱৰ্কৰ প্ৰকাৰ ছেট কৰক:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"হ\'ষ্টনাম(www.google.com) IPv4 পিং কৰক:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"আপোনাৰ ব্লুটুথৰ ছিগনেল দুৰ্বল। স্পীকাৰফ’নলৈ সলনি কৰি চাওক।"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"কলৰ গুণগত মানৰ জাননী"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"বন্ধ হৈ যোৱা SIP একাউণ্ট"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"এই প্ৰ’ফাইলৰ পৰা বাৰ্তা পঠিয়াব নোৱাৰি"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"আপোনাৰ কৰ্মস্থানৰ নীতিয়ে আপোনাক কেৱল কৰ্মস্থানৰ প্ৰ’ফাইলৰ পৰা বাৰ্তা পঠিয়াবলৈ দিয়ে"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"বাতিল কৰক"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"কৰ্মস্থানৰ প্ৰ’ফাইললৈ সলনি কৰক"</string>
</resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 46f8873..f7cf162 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Ləğv edin"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Çıxarıla bilən eSIM\'i Defolt olaraq təyin edin"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobil Radio Enerjisi"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"\"Xidmətdənkənar\" Simulyasiyası (yalnız Debaq Versiyası)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM Ünvan Kitabçasına Baxın"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Sabit Yığım Nömrələrinə Baxın"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Xidmət Yığım Nömrələrinə Baxın"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Qoşuldu"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Dayandırılıb"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Naməlum"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"İlkin"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkt"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bayt"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data göndərildi:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Mesaj Gözlənilir:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefon nömrəsi:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Radio Diapazon Seçin"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Səs Şəbəkə Növü:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Data Şəbəkə Növü:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Şəbəkə Növünü Əvəzləyin:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"İşlənməmiş Səs üzrə Qeydiyyat Statusu:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"İşlənməmiş Data üzrə Qeydiyyat Statusu:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"İşlənməmiş WLAN Datası üzrə Qeydiyyat Statusu:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Telefon indeksini seçin"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Tərcih Olunmuş Şəbəkə Növü Seçin:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping Hostname(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth siqnalı zəifdir. Telefon spikerinə keçin."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Zəng keyfiyyəti bildirişi"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Ləğv edilmiş SIP hesabları"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Bu profildən mesaj göndərmək mümkün deyil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"İş siyasətiniz yalnız iş profilindən mesaj göndərməyə icazə verir"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Ləğv edin"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"İş profilinə keçin"</string>
</resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 027f588..ce1b9a3 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Otkaži"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Podesi prenosivi eSIM kao podrazumevani"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Napajanje za radio na mobilnim uređajima"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulacija ne funkcioniše (samo verzija sa otklonjenim greškama)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Prikaži adresar SIM-a"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Prikaži brojeve za fiksno biranje"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Prikaži brojeve za servisno biranje"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Povezano"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspendovano"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Nepoznato"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primarno"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pak."</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bajt(ov)a"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Poslati podaci:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Poruka na čekanju:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Broj telefona:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Izaberite radijski opseg"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Tip glasovne mreže:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Tip mreže za prenos podataka:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Zameni tip mreže:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Stanje registracije neobrađenih glasovnih podataka:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Stanje registracije neobrađenih podataka:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Stanje registracije neobrađenih WLAN podataka:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Izaberite indeks telefona"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Podesite željeni tip mreže:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"IPv4 imena hosta za pingovanje (www.google.com):"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth signal je slab. Probajte da pređete na spikerfon."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Obaveštenje o kvalitetu poziva"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Zastareli SIP nalozi"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Ne možete da pošaljete poruku sa ovog profila"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Smernice za posao vam omogućavaju da šaljete poruke samo sa poslovnog profila"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Otkaži"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Pređi na poslovni profil"</string>
</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 3d25a4d..e2e48ad 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Скасаваць"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Зрабіць здымную eSIM-карту стандартнай"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Магутнасць радыёсігналу"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Мадэляванне знаходжання па-за сеткай (толькі ў зборцы для адладкі)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Праглядзець адрасную кнігу на SIM-карце"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Прагляд фіксаваных нумароў"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Паглядзець сэрвісныя нумары"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Падключана"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Прыпынена"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Невядома"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Асноўны"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"пак."</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"Б"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"дБм"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Адпраўленыя даныя:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Непрачытанае паведамленне:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Нумар тэлефона:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Выбраць радыёдыяпазон"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Тып галасавой сеткі:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Тып сеткі перадачы даных:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Перавызначэнне тыпу сеткі:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Стан пачатковай рэгістрацыі голасу:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Стан пачатковай рэгістрацыі даных:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Стан пачатковай рэгістрацыі даных WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Выбраць тэлефонны код"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Задаць прыярытэтны тып сеткі:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Каманда ping для хоста www.google.com (IPv4):"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Сігнал Bluetooth слабы. Паспрабуйце пераключыцца на гучную сувязь."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Апавяшчэнне пра якасць выкліку"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Састарэлыя ўліковыя запісы SIP"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Адпраўляць паведамленні з гэтага профілю нельга"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Згодна з палітыкай вашай арганізацыі адпраўляць паведамленні дазваляецца толькі з працоўнага профілю"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Скасаваць"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Пераключыцца на працоўны профіль"</string>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 871438d..5c6ea93 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Отказ"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Задаване на електронната SIM карта с изваждащ се чип като основна"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Мощност на мобилното радио"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Симулиране на липса на услуга (само в компилацията за отстраняване на грешки)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Преглед на указателя на SIM картата"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Преглед на номера за фиксирано набиране"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Преглед на номера за набиране на услуги"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Установена е връзка"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Спрени"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Неизвестно"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Основна"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"пакета"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"байта"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Изпратени данни:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Изчакващо съобщение:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Телефонен номер:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Избиране на радиодиапазон"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Тип на гласовата мрежа:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Тип на мрежата за данни:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Замяна на типа на мрежата:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Необработено състояние на регистрация за глас:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Необработено състояние на регистрация за данни:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Необработено състояние на регистрация за данни за WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Избиране на индекс за телефон"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Задаване на предпочитан тип мрежа:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Име на хост за позив (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Сигналът ви за Bluetooth е слаб. Опитайте да превключите на високоговорител."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Известия за качеството на обаждането"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Оттеглени профили за SIP"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Съобщението не може да се изпрати от този потребителски профил"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Служебните ви правила позволяват да изпращате съобщения само от служебния потребителски профил"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Отказ"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Превключване към служебния потребителски профил"</string>
</resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 1605181..cb98b0d 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"বাদ দিন"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"সরিয়ে দেওয়া যায় এমন eSIM ডিফল্ট হিসেবে সেট করুন"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"মোবাইল রেডিওর গুণমান"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"আউট-অফ-সার্ভিস সিমুলেট করা (শুধুমাত্র ডিবাগ বিল্ডের জন্য)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"সিম অ্যাড্রেস বুক দেখুন"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"স্থায়ী ডায়াল নম্বরগুলি দেখুন"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"সার্ভিস ডায়াল নম্বরগুলি দেখুন"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"কানেক্ট করা হয়েছে"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"সাসপেন্ড করা হয়েছে"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"অজানা"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"প্রাথমিক"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"বাইট"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"ডেটা পাঠানো হয়েছে:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"মেসেজের জন্য অপেক্ষা করা হচ্ছে:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ফোন নম্বর:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"রেডিও ব্যান্ড বেছে নিন"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"ভয়েস নেটওয়ার্কের প্রকার:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ডেটা নেটওয়ার্কের প্রকার:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"ওভাররাইড নেটওয়ার্কের ধরন:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"ভয়েস Raw রেজিস্ট্রেশনের স্ট্যাটাস:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"ডেটা Raw রেজিস্ট্রেশনের স্ট্যাটাস:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN ডেটা Raw রেজিস্ট্রেশনের স্ট্যাটাস:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ফোনের ইন্ডেক্স বেছে নিন"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"পছন্দের নেটওয়ার্ক সেট করুন:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"হোস্টনামে(www.google.com) IPv4 পিং করুন:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"আপনার ডিভাইসের ব্লুটুথ সিগনাল ভাল না। বদল করে স্পিকারফোন বেছে নিন।"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"ফোন কলের কোয়ালিটি সংক্রান্ত বিজ্ঞপ্তি"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"পুরনো SIP অ্যাকাউন্ট"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"এই প্রোফাইল থেকে মেসেজ পাঠানো যাচ্ছে না"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"কাজ সংক্রান্ত নীতি, আপনাকে শুধুমাত্র অফিস প্রোফাইল থেকে মেসেজ করার অনুমতি দেয়"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"বাতিল করুন"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"অফিস প্রোফাইলে পাল্টে নিন"</string>
</resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index fec68b1..1a6d50f 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Otkaži"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Postavljanje uklonjive eSim kartice kao zadane"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Snaga mobilnog radija"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulacija ne radi (samo verzija za otklanjanje grešaka)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Prikaži SIM adresar"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Prikaži brojeve fiksnog biranja"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Prikaži brojeve biranja usluga"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Povezano"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Obustavljeno"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Nepoznato"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primarno"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"paketi"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bajtova"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Poslani podaci:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Poruka na čekanju:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Broj telefona:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Odaberi radijski opseg"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Vrsta glasovne mreže:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Vrsta mreže za prijenos podataka:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Zaobilaženje vrste mreže:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Stanje registracije sirovog glasa:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Stanje registracije sirovih podataka:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Stanje registracije sirovih podataka WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Odaberi indeks telefona"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Postavi preferiranu vrstu mreže:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Pinguj ime računara (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Vaš Bluetooth signal je slab. Pokušajte prebaciti na zvučnik."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Obavještenje o kvalitetu poziva"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Zastarjeli SIP računi"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Slanje poruke s ovog profila nije moguće"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Vaša poslovna pravila dopuštaju vam da šaljete poruke samo s poslovnog profila"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Odustani"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Prelazak na poslovni profil"</string>
</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index ff2ecf8..f7effe7 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Cancel·la"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Defineix l\'eSIM extraïble com a opció predeterminada"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Potència del senyal mòbil"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simula que està fora de servei (només per a la compilació de depuració)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Mostra la llibreta d\'adreces de la SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Mostra els números de marcatge fix"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Mostra els números de marcatge de serveis"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Amb connexió"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"En suspensió"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Desconegut"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Principal"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"paquets"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Dades enviades:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Missatge en espera:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Número de telèfon:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Selecciona la banda de senyal mòbil"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Tipus de xarxa de veu:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Tipus de xarxa de dades:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Substitució de tipus de xarxa:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Estat de registre de les dades sense tractar de Voice:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Estat de registre de les dades sense tractar:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Estat de registre de les dades sense tractar de la WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Selecciona la guia telefònica"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Defineix el tipus de xarxa preferit:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Fer ping a IPv4 de nom d\'amfitrió (www.google.com):"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"El senyal del Bluetooth és feble. Fes servir l\'altaveu."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notificació sobre la qualitat de la trucada"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Comptes SIP obsolets"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"No es pot enviar el missatge des d\'aquest perfil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"La teva política de treball et permet enviar missatges només des del perfil de treball"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Cancel·la"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Canvia al perfil de treball"</string>
</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index f7ea827..6c945ca 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -596,7 +596,7 @@
<string name="hac_mode_summary" msgid="7774989500136009881">"Zapnout režim kompatibility s naslouchátkem"</string>
<string name="rtt_mode_title" msgid="3075948111362818043">"Hovor, během něhož můžete posílat zprávy (RTT)"</string>
<string name="rtt_mode_summary" msgid="8631541375609989562">"Povolit posílání zpráv během hlasového hovoru"</string>
- <string name="rtt_mode_more_information" msgid="587500128658756318">"Funkce Okamžitý text (RTT) pomáhá volajícím, kteří neslyší, jsou nedoslýchaví, mají řečovou vadu, případně potřebují víc než jen hlas z jiného důvodu.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Další informace</a>\n <br><br> – Text poslaný během hovoru se uloží jako přepis zpráv\n <br> – Tato funkce není dostupná při videohovorech"</string>
+ <string name="rtt_mode_more_information" msgid="587500128658756318">"Funkce SMS během hovoru (RTT) pomáhá volajícím, kteří neslyší, jsou nedoslýchaví, mají řečovou vadu, případně potřebují víc než jen hlas z jiného důvodu.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Další informace</a>\n <br><br> – Text poslaný během hovoru se uloží jako přepis zpráv\n <br> – Tato funkce není dostupná při videohovorech"</string>
<string name="no_rtt_when_roaming" msgid="5268008247378355389">"Použít: Funkce SMS během hovoru není dostupná při roamingu"</string>
<string-array name="tty_mode_entries">
<item msgid="3238070884803849303">"TTY vypnuto"</item>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Zrušit"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Nastavit vyjímatelnou eSIM jako výchozí"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Výkon mobilního přijímače"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulovat provoz mimo službu (pouze ladicí sestavení)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Zobrazit adresář SIM karty"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Zobrazit povolená telefonní čísla"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Zobrazit čísla volání služeb"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Připojeno"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Pozastaveno"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Neznámé"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primární"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pakety"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"B"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Odeslaná data:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Čekající zpráva:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefonní číslo:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Zvolit pásmo bezdrátového modulu"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Typ hlasové sítě:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Typ datové sítě:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Přepsat typ sítě:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice – stav registrace nezpracovaných dat:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Stav registrace nezpracovaných dat:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN – stav registrace nezpracovaných dat:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Vybrat telefonní seznam"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Nastavit upřednostňovaný typ sítě:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping názvu hostitele (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Signál sítě Bluetooth je slabý. Zkuste přepnout na hlasitý odposlech."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Oznámení o kvalitě hovoru"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Zastaralé účty SIP"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Z tohoto profilu nemůžete odeslat zprávu"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Vaše pracovní zásady vám umožňují odesílat zprávy jen z pracovního profilu"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Zrušit"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Přepnout na pracovní profil"</string>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 6f2a42b..a489077 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Annuller"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Konfigurer eSIM, der kan fjernes, som standard"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobilsendestyrke"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulering af enhed, der er ude af drift (kun i fejlretningsbuild)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Vis adressebog på SIM-kortet"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Vis numre til begrænset opkald"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Vis tjenestens faste opkaldsnumre"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Tilsluttet"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspenderet"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Ukendt"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primær"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pk."</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data sendt:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Besked venter:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefonnummer:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Vælg radiobånd"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Talenetværkstype:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Datanetværkstype:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Overskriv netværkstype:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"RAW-registreringsstatus for Voice:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"RAW-registreringsstatus for data:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"RAW-registreringsstatus for WLAN-data:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Vælg telefonindeks"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Angiv den foretrukne netværkstype:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping værtsnavn (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Dit Bluetooth-signal er svagt. Prøv at skifte til medhør."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notifikation om opkaldskvalitet"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Udfasede SIP-konti"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Du kan ikke sende beskeder fra denne profil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Din arbejdspolitik giver dig mulighed for kun at sende beskeder fra arbejdsprofilen"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Annuller"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Skift til arbejdsprofil"</string>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 1831979..3613464 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -592,7 +592,7 @@
<string name="importToFDNfromContacts" msgid="5068664870738407341">"Aus Kontakten importieren"</string>
<string name="singleContactImportedMsg" msgid="3619804066300998934">"Kontakt wird importiert."</string>
<string name="failedToImportSingleContactMsg" msgid="228095510489830266">"Fehler beim Importieren des Kontakts"</string>
- <string name="hac_mode_title" msgid="4127986689621125468">"Hörgeräte"</string>
+ <string name="hac_mode_title" msgid="4127986689621125468">"Hörhilfen"</string>
<string name="hac_mode_summary" msgid="7774989500136009881">"Hörhilfekompatibilität aktivieren"</string>
<string name="rtt_mode_title" msgid="3075948111362818043">"RTT-Anruf"</string>
<string name="rtt_mode_summary" msgid="8631541375609989562">"Nachrichten in Sprachanrufen erlauben"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Abbrechen"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Wechsel-eSIM als Standard festlegen"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobilfunkstärke"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"„Außer Betrieb“ simulieren (nur Debug-Build)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM-Adressbuch anzeigen"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Rufnummernbeschränkung ansehen"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Servicerufnummern anzeigen"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Verbunden"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Gesperrt"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Unbekannt"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primär"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"Pakete"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"Bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Gesendete Daten:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Nachricht liegt vor:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefonnummer:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Frequenzbereich auswählen"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Sprachnetzwerktyp:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Datennetzwerktyp:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Netzwerktyp überschreiben:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Registrierungsstatus von Voice:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Registrierungsstatus der Daten:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Registrierungsstatus der WLAN-Daten:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Smartphone-Index auswählen"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Bevorzugten Netzwerktyp festlegen:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Hostname (www.google.com) pingen, IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Das Bluetooth-Signal ist schwach. Verwende die Freisprechfunktion."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Benachrichtigung zu Anrufqualität"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Eingestellte SIP-Konten"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Du kannst über dieses Profil keine Nachrichten senden"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Gemäß den Arbeitsrichtlinien darfst du nur über dein Arbeitsprofil Nachrichten senden"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Abbrechen"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Zum Arbeitsprofil wechseln"</string>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 40a39c2..035e947 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Ακύρωση"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Ορισμός αφαιρούμενης eSIM ως προεπιλεγμένης"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Ισχύς πομπού κινητής τηλεφωνίας"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Η προσομοίωση δεν λειτουργεί (μόνο έκδοση εντοπισμού σφαλμάτων)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Προβολή βιβλίου διευθύνσεων κάρτας SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Προβολή προκαθορισμένων αριθμών κλήσης"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Προβολή αριθμών κλήσης υπηρεσίας"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Σε σύνδεση"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Σε αναστολή"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Άγνωστο"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Κύρια"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"byte"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Δεδομένα που στάλθηκαν:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Αναμονή μηνύματος:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Αριθμός τηλεφώνου:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Επιλογή ζώνης συχνοτήτων πομπού"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Τύπος δικτύου φωνής:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Τύπος δικτύου δεδομένων:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Παράκαμψη τύπου δικτύου:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Κατάσταση μη επεξεργασμένης εγγραφής φωνής:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Κατάσταση μη επεξεργασμένης εγγραφής δεδομένων:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Κατάσταση μη επεξεργασμένης εγγραφής δεδομένων WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Επιλογή ευρετηρίου τηλεφώνου"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Ορισμός προτιμώμενου τύπου δικτύου:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Όνομα κεντρικού υπολογιστή Ping (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Το σήμα bluetooth είναι ασθενές. Δοκιμάστε να αλλάξετε σε ανοιχτή ακρόαση."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Ειδοποίηση ποιότητας κλήσης"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Καταργημένοι λογαριασμοί SIP"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Δεν είναι δυνατή η αποστολή μηνύματος από αυτό το προφίλ"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Η πολιτική εργασίας σας επιτρέπει την αποστολή μηνυμάτων μόνο από το προφίλ εργασίας"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Ακύρωση"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Εναλλαγή σε προφίλ εργασίας"</string>
</resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index eebbbb3..c9a7765 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Cancel"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Set removable eSIM as default"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobile radio power"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulate out of service (debug build only)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"View SIM address book"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"View fixed dialling numbers"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"View service dialling numbers"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Connected"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspended"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Unknown"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primary"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data sent"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Message waiting:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Phone number:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Select radio band"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Voice network type:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Data network type:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Override network type:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice raw registration state:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Data raw registration state:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN data raw registration state:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Select phone index"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Set preferred network type:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping hostname(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Your Bluetooth signal is weak. Try switching to speakerphone."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Call quality notification"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Deprecated SIP accounts"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Can\'t send message from this profile"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Your work policy allows you to send messages only from the work profile"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Cancel"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Switch to work profile"</string>
</resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index f8b01ea..b65452e 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Cancel"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Set Removable eSIM as Default"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobile Radio Power"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulate Out of Service (Debug Build only)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"View SIM Address Book"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"View Fixed Dialing Numbers"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"View Service Dialing Numbers"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Connected"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspended"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Unknown"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primary"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data Sent:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Message Waiting:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Phone Number:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Select Radio Band"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Voice Network Type:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Data Network Type:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Override Network Type:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice Raw Registration State:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Data Raw Registration State:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN Data Raw Registration State:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Select phone index"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Set Preferred Network Type:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping Hostname(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Your bluetooth signal is weak. Try switching to speakerphone."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Call Quality Notification"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Deprecated SIP accounts"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Can\'t send message from this profile"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Your work policy allows you to send message only from the work profile"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Cancel"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Switch to work profile"</string>
</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index eebbbb3..c9a7765 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Cancel"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Set removable eSIM as default"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobile radio power"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulate out of service (debug build only)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"View SIM address book"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"View fixed dialling numbers"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"View service dialling numbers"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Connected"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspended"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Unknown"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primary"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data sent"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Message waiting:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Phone number:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Select radio band"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Voice network type:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Data network type:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Override network type:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice raw registration state:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Data raw registration state:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN data raw registration state:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Select phone index"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Set preferred network type:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping hostname(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Your Bluetooth signal is weak. Try switching to speakerphone."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Call quality notification"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Deprecated SIP accounts"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Can\'t send message from this profile"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Your work policy allows you to send messages only from the work profile"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Cancel"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Switch to work profile"</string>
</resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index eebbbb3..c9a7765 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Cancel"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Set removable eSIM as default"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobile radio power"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulate out of service (debug build only)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"View SIM address book"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"View fixed dialling numbers"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"View service dialling numbers"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Connected"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspended"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Unknown"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primary"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data sent"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Message waiting:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Phone number:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Select radio band"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Voice network type:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Data network type:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Override network type:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice raw registration state:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Data raw registration state:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN data raw registration state:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Select phone index"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Set preferred network type:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping hostname(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Your Bluetooth signal is weak. Try switching to speakerphone."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Call quality notification"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Deprecated SIP accounts"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Can\'t send message from this profile"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Your work policy allows you to send messages only from the work profile"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Cancel"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Switch to work profile"</string>
</resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 84474ef..7c9c6ee 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Cancel"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Set Removable eSIM as Default"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobile Radio Power"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulate Out of Service (Debug Build only)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"View SIM Address Book"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"View Fixed Dialing Numbers"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"View Service Dialing Numbers"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Connected"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspended"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Unknown"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primary"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data Sent:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Message Waiting:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Phone Number:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Select Radio Band"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Voice Network Type:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Data Network Type:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Override Network Type:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice Raw Registration State:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Data Raw Registration State:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN Data Raw Registration State:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Select phone index"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Set Preferred Network Type:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping Hostname(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Your bluetooth signal is weak. Try switching to speakerphone."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Call Quality Notification"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Deprecated SIP accounts"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Can\'t send message from this profile"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Your work policy allows you to send message only from the work profile"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Cancel"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Switch to work profile"</string>
</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index ccdab6c..22e0e4d 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Cancelar"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Establecer eSIM extraíble como predeterminada"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Potencia de la señal móvil"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simular fuera de servicio (solo para la compilación de depuración)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Ver libreta de direcciones de SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Ver números de marcación fija"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Ver números de marcación de servicio"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Conectado"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspendido"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Desconocido"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Principal"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"paquetes"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Datos enviados:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Mensaje en espera:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Número de teléfono:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Seleccionar banda de radio"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Tipo de red de voz:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Tipo de red de datos:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Anular tipo de red:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Estado del registro de Voice sin procesar:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Estado del registro de los datos sin procesar:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Estado del registro de los datos de WLAN sin procesar:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Seleccionar guía telefónica"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Establecer el tipo de red preferida:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Hacer ping a IPv4 de nombre de host (www.google.com):"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Tu señal de Bluetooth es débil. Intenta cambiar a la bocina."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notificación de calidad de llamada"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Cuentas SIP obsoletas"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"No se puede enviar un mensaje desde este perfil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Tu política de trabajo te permite enviar mensajes solo desde el perfil de trabajo."</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Cancelar"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Cambiar al perfil de trabajo"</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 94b8703..8fa9445 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -186,9 +186,9 @@
<string name="manual_mode_disallowed_summary" msgid="3970048592179890197">"No disponible cuando se ha conectado con %1$s"</string>
<string name="network_select_title" msgid="4117305053881611988">"Red"</string>
<string name="register_automatically" msgid="3907580547590554834">"Registro automático..."</string>
- <string name="preferred_network_mode_title" msgid="5253395265169539830">"Tipo de red preferido"</string>
+ <string name="preferred_network_mode_title" msgid="5253395265169539830">"Tipo de red preferida"</string>
<string name="preferred_network_mode_summary" msgid="3787989000044330064">"Cambiar el modo operativo de la red"</string>
- <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"Tipo de red preferido"</string>
+ <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"Tipo de red preferida"</string>
<string name="forbidden_network" msgid="5081729819561333023">"(prohibida)"</string>
<string name="choose_network_title" msgid="5335832663422653082">"Elegir red"</string>
<string name="network_disconnected" msgid="8844141106841160825">"Desconectado"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Cancelar"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Establecer eSIM extraíble como predeterminada"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Potencia de la señal móvil"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simular fuera del servicio (solo versión de depuración)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Ver libreta de direcciones de tarjeta SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Ver números de marcación fija"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Ver números de marcación de servicio"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Conectado"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspendido"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Desconocido"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Principal"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pqts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Datos enviados:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Mensaje en espera:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Número de teléfono:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Seleccionar banda de señal móvil"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Tipo de red de voz:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Tipo de red de datos:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Anular tipo de red:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Estado de registro sin procesar de Voice:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Estado de registro sin procesar de datos:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Estado de registro sin procesar de los datos de WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Seleccionar guía telefónica"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Establecer tipo de red preferido:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Hacer ping a IPv4 de nombre de host (www.google.com):"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Tu señal de Bluetooth es débil. Prueba a cambiar al altavoz."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notificación de calidad de la llamada"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Cuentas SIP obsoletas"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"No se puede enviar el mensaje desde este perfil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Tu política de trabajo te permite enviar mensajes solo desde el perfil de trabajo"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Cancelar"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Cambiar al perfil de trabajo"</string>
</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 5495ffb..6ec88dc 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Tühista"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Eemaldatava eSIM-i määramine vaikevalikuks"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobiiliraadio toide"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simuleerimine ei tööta (ainult silumisjärgus)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Kuva SIM-i aadressiraamat"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Kuva fikseeritud valimisnumbrid"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Kuva teenuse valimise numbrid"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Ühendatud"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Peatatud"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Teadmata"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Peamine"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"baiti"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Saadetud andmed:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Ootel sõnum:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefoninumber:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Raadioriba valimine"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Häälvõrgu tüüp:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Andmesidevõrgu tüüp:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Võrgu tüübi alistamine:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Hääle toorandmete registreerimise olek:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Toorandmete registreerimise olek:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN-i toorandmete registreerimise olek:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Telefoni registri valimine"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Eelistatud võrgutüübi määramine:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Pingi hosti nimi (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Teie Bluetoothi signaal on nõrk. Lülitage valjuhääldile."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Kõnekvaliteedi märguanne"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Katkestatud toega SIP-kontod"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Te ei saa sellelt profiililt sõnumeid saata"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Teie töökoja eeskirjad lubavad teil ainult tööprofiililt sõnumeid saata"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Tühista"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Lülitu tööprofiilile"</string>
</resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index a9ac5f8..12c1a3c 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -96,10 +96,10 @@
<string name="sum_loading_settings" msgid="434063780286688775">"Ezarpenak kargatzen…"</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"Ezkutatu zenbakia irteerako deietan"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"Erakutsi zenbakia irteerako deietan"</string>
- <string name="sum_default_caller_id" msgid="1767070797135682959">"Erabili operadorearen ezarpen lehenetsiak, irteerako deietan nire zenbakia erakusteko"</string>
+ <string name="sum_default_caller_id" msgid="1767070797135682959">"Erabili operadorearen ezarpen lehenetsiak, egiten ditudan deietan nire zenbakia erakusteko"</string>
<string name="labelCW" msgid="8449327023861428622">"Deiak zain"</string>
- <string name="sum_cw_enabled" msgid="3977308526187139996">"Deiak abian dauden bitartean, eman sarrerako deien berri"</string>
- <string name="sum_cw_disabled" msgid="3658094589461768637">"Deiak abian dauden bitartean, eman sarrerako deien berri"</string>
+ <string name="sum_cw_enabled" msgid="3977308526187139996">"Deiak abian dauden bitartean, eman jasotzen ditudan deien berri"</string>
+ <string name="sum_cw_disabled" msgid="3658094589461768637">"Deiak abian dauden bitartean, eman jasotzen ditudan deien berri"</string>
<string name="call_forwarding_settings" msgid="8937130467468257671">"Dei-desbideratzearen ezarpenak"</string>
<string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"Deiak desbideratzeko ezarpenak (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="labelCF" msgid="3578719437928476078">"Dei-desbideratzea"</string>
@@ -126,7 +126,7 @@
<string name="disable_cfnrc_forbidden" msgid="775348748084726890">"Operadoreak ez du dei-desbideratzeak desgaitzea onartzen telefonoa eskuragarri ez dagoen bitartean."</string>
<string name="registration_cf_forbidden" msgid="4386482610771190420">"Operadoreak ez du onartzen dei-desbideratzea."</string>
<string name="cdma_call_waiting" msgid="4565070960879673216">"Deia zain uzteko aukera aktibatu nahi duzu?"</string>
- <string name="enable_cdma_call_waiting_setting" msgid="5906811747921744307">"Dei batean zaudenean, jakinarazpen bat jasoko duzu sarrerako deiak jasotzen badituzu"</string>
+ <string name="enable_cdma_call_waiting_setting" msgid="5906811747921744307">"Dei batean zaudenean, jakinarazpen bat jasoko duzu deiak jasotzen badituzu"</string>
<string name="enable_cdma_cw" msgid="811047045863422232">"Aktibatu"</string>
<string name="disable_cdma_cw" msgid="7119290446496301734">"Utzi"</string>
<string name="cdma_call_waiting_in_ims_on" msgid="6390979414188659218">"IMS zerbitzupean CDMA deiak zain uzteko aukera aktibatuta dago"</string>
@@ -156,8 +156,8 @@
<item msgid="9150034130629852635">"Erakutsi zenbakia"</item>
</string-array>
<string name="vm_changed" msgid="4739599044379692505">"Erantzungailuaren zenbakia aldatu da."</string>
- <string name="vm_change_failed" msgid="7877733929455763566">"Ezin izan da erantzungailuaren zenbakia aldatu.\nArazoak bere horretan badirau, jarri operadorearekin harremanetan."</string>
- <string name="fw_change_failed" msgid="9179241823460192148">"Ezin izan da desbideratze-zenbakia aldatu.\nArazoak bere horretan jarraitzen badu, jarri operadorearekin harremanetan."</string>
+ <string name="vm_change_failed" msgid="7877733929455763566">"Ezin izan da erantzungailuaren zenbakia aldatu.\nArazoak bere horretan badirau, jarri harremanetan operadorearekin."</string>
+ <string name="fw_change_failed" msgid="9179241823460192148">"Ezin izan da desbideratze-zenbakia aldatu.\nArazoak bere horretan jarraitzen badu, jarri harremanetan operadorearekin."</string>
<string name="fw_get_in_vm_failed" msgid="2432678237218183844">"Ezin izan dira desbideratze-zenbakiaren ezarpenak eskuratu eta gorde.\nHala ere, hornitzaile berrira aldatu nahi duzu?"</string>
<string name="no_change" msgid="3737264882821031892">"Ez da aldaketarik egin."</string>
<string name="sum_voicemail_choose_provider" msgid="6750824719081403773">"Aukeratu erantzungailu-zerbitzua"</string>
@@ -766,8 +766,8 @@
<string name="sum_call_barring_enabled" msgid="5184331188926370824">"Aktibatuta"</string>
<string name="sum_call_barring_disabled" msgid="5699448000600153096">"Desaktibatuta"</string>
<string name="call_barring_baoc" msgid="7400892586336429326">"Egindako guztiak"</string>
- <string name="call_barring_baoc_enabled" msgid="3131509193386668182">"Irteerako dei guztiak blokeatzeko aukera desgaitu nahi duzu?"</string>
- <string name="call_barring_baoc_disabled" msgid="8534224684091141509">"Irteerako dei guztiak blokeatu nahi dituzu?"</string>
+ <string name="call_barring_baoc_enabled" msgid="3131509193386668182">"Egindako dei guztiak blokeatzeko aukera desgaitu nahi duzu?"</string>
+ <string name="call_barring_baoc_disabled" msgid="8534224684091141509">"Egindako dei guztiak blokeatu nahi dituzu?"</string>
<string name="call_barring_baoic" msgid="8668125428666851665">"Nazioartean egindakoak"</string>
<string name="call_barring_baoic_enabled" msgid="1203758092657630123">"Nazioartean egindako deiak blokeatzeko aukera desgaitu nahi duzu?"</string>
<string name="call_barring_baoic_disabled" msgid="5656889339002997449">"Nazioartean egindako deiak blokeatu nahi dituzu?"</string>
@@ -775,8 +775,8 @@
<string name="call_barring_baoicr_enabled" msgid="1615324165512798478">"Nazioarteko ibiltaritzan egindako deiak blokeatzeko aukera desgaitu nahi duzu?"</string>
<string name="call_barring_baoicr_disabled" msgid="172010175248142831">"Nazioarteko ibiltaritzan egindako deiak blokeatu nahi dituzu?"</string>
<string name="call_barring_baic" msgid="7941393541678658566">"Jasotako guztiak"</string>
- <string name="call_barring_baic_enabled" msgid="4357332358020337470">"Sarrerako dei guztiak blokeatzeko aukera desgaitu nahi duzu?"</string>
- <string name="call_barring_baic_disabled" msgid="2355945245938240958">"Sarrerako dei guztiak blokeatu nahi dituzu?"</string>
+ <string name="call_barring_baic_enabled" msgid="4357332358020337470">"Jasotako dei guztiak blokeatzeko aukera desgaitu nahi duzu?"</string>
+ <string name="call_barring_baic_disabled" msgid="2355945245938240958">"Jasotako dei guztiak blokeatu nahi dituzu?"</string>
<string name="call_barring_baicr" msgid="8712249337313034226">"Nazioarteko ibiltaritzan jasotakoak"</string>
<string name="call_barring_baicr_enabled" msgid="64774270234828175">"Nazioarteko ibiltaritzan jasotako dei guztiak blokeatzeko aukera desgaitu nahi duzu?"</string>
<string name="call_barring_baicr_disabled" msgid="3488129262744027262">"Nazioarteko ibiltaritzan jasotako deiak blokeatu nahi dituzu?"</string>
@@ -801,8 +801,8 @@
<string name="supp_service_notification_call_waiting" msgid="4577403881609445324">"Deia zain dago."</string>
<string name="supp_service_clir_suppression_rejected" msgid="6105737020194776121">"Sareak zenbakia blokeatzeko aukera baztertu du."</string>
<string name="supp_service_closed_user_group_call" msgid="2811636666505250689">"Erabiltzaile talde itxi baten deia."</string>
- <string name="supp_service_incoming_calls_barred" msgid="2034627421274447674">"Debekatu dira sarrerako deiak."</string>
- <string name="supp_service_outgoing_calls_barred" msgid="5205725332394087112">"Debekatu dira irteerako deiak."</string>
+ <string name="supp_service_incoming_calls_barred" msgid="2034627421274447674">"Debekatu da deiak jasotzea."</string>
+ <string name="supp_service_outgoing_calls_barred" msgid="5205725332394087112">"Debekatu da deiak egitea."</string>
<string name="supp_service_call_forwarding_active" msgid="7910162960395132464">"Aktibatu da dei-desbideratzea."</string>
<string name="supp_service_additional_call_forwarded" msgid="8772753260008398632">"Desbideratu da jasotako beste deia."</string>
<string name="supp_service_additional_ect_connected" msgid="8525934162945220237">"Transferitu da deia."</string>
@@ -814,8 +814,8 @@
<string name="supp_service_conference_call" msgid="4004193534408317148">"Konferentzia-deian sartzen."</string>
<string name="supp_service_held_call_released" msgid="2847835124639112410">"Askatu da zain utzitako deia."</string>
<string name="callFailed_otasp_provisioning_in_process" msgid="3345666183602879326">"Ezin da egin deia, une honetan gailua hornitzen ari delako."</string>
- <string name="callFailed_already_dialing" msgid="7250591188960691086">"Ezin da egin deia, irteerako beste dei bat abian delako."</string>
- <string name="callFailed_already_ringing" msgid="2376603543544289303">"Ezin da egin deia, oraindik erantzun ez diozun sarrerako dei bat baitago. Beste dei bat egin aurretik, erantzun sarrerako deiari edo bazter ezazu."</string>
+ <string name="callFailed_already_dialing" msgid="7250591188960691086">"Ezin da egin deia, beste dei bat abian delako."</string>
+ <string name="callFailed_already_ringing" msgid="2376603543544289303">"Ezin da egin deia, oraindik erantzun ez diozun dei bat jasotzen ari zarelako. Beste dei bat egin aurretik, erantzun deiari edo bazter ezazu."</string>
<string name="callFailed_calling_disabled" msgid="5010992739401206283">"Ezin da egin deia, deiak egiteko aukera desgaitu egin delako sistemaren ro.telephony.disable-call propietatea erabilita."</string>
<string name="callFailed_too_many_calls" msgid="2761754044990799580">"Ezin da egin deia, dagoeneko bi dei daudelako abian. Beste dei bat egin aurretik, eten deietako bat edo bateratu deiak konferentzia-dei bakarrean."</string>
<string name="supp_service_over_ut_precautions" msgid="2145018231396701311">"<xliff:g id="SUPP_SERVICE">%s</xliff:g> erabiltzeko, ziurtatu datu-konexioa aktibatuta dagoela. Sare mugikorren ezarpenetan alda dezakezu aukera hau."</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Utzi"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Ezarri eSIM aldagarria lehenetsi gisa"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Sare mugikor bidezko irratiaren indarra"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulatu gailua ez dabilela (arazketa-konpilazioa soilik)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Ikusi SIMeko kontaktuak"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Ikusi markatze finkoko zenbakiak"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Ikusi zerbitzuaren markatze-zenbakiak"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Konektatuta"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Behin-behinean itxitakoak"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Ezezaguna"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Nagusia"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"byte"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Bidalitako datuak:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Mezua zain:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefono-zenbakia:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Hautatu irrati-banda"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Ahots-deien sare mota:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Datu-sare mota:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Ordeztu sare mota:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice-ko prozesatu gabeko datuen erregistroaren egoera:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Prozesatu gabeko datuen erregistroaren egoera:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN bidezko deien prozesatu gabeko datuen erregistroaren egoera:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Hautatu telefonoaren indizea"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Ezarri sare mota hobetsia:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping egiteko ostalari-izena (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth seinalea ahula da. Erabili telefonoko bozgorailua."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Deien kalitateari buruzko jakinarazpena"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"SIP-eko kontu zaharkituak"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Ezin duzu bidali mezurik profil honetatik"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Mezuak laneko profiletik soilik bidaltzeko baimena ematen dizute laneko gidalerroek"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Utzi"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Aldatu laneko profilera"</string>
</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index a2dce41..dce81ab 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"لغو"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"تنظیم سیمکارت داخلی جداشدنی بهعنوان پیشفرض"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"توان رادیوی تلفن همراه"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"شبیهسازی از کار افتادن (فقط ساخت اشکالزدایی)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"مشاهده دفترچه نشانی سیمکارت"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"مشاهده شمارههای شمارهگیری ثابت"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"مشاهده شمارههای شمارهگیری سرویس"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"متصل"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"تعلیقشده"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"نامشخص"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"اصلی"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"بایت"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"داده ارسالشده:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"پیام در انتظار:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"شماره تلفن:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"انتخاب باند رادیو"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"نوع شبکه صوتی:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"نوع شبکه داده:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"ملغی کردن نوع شبکه:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"وضعیت ثبت خام صدا:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"وضعیت ثبت خام داده:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"وضعیت ثبت خام داده WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"انتخاب نمایه تلفن"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"تنظیم نوع شبکه ترجیحی:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"پینگ کردن نام میزبان (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"سیگنال بلوتوث شما ضعیف است. از بلندگوی تلفن استفاده کنید."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"اعلان کیفیت تماس"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"حسابهای SIP منسوخشده"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"نمیتوانید ازطریق این نمایه پیام ارسال کنید"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"طبق خطمشی کاریتان فقط میتوانید ازطریق نمایه کاری پیام ارسال کنید"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"لغو کردن"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"رفتن به نمایه کاری"</string>
</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 7c1f1f0..430afe0 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Peru"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Aseta poistettava eSIM oletukseksi"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobiiliradion voimakkuus"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Epäkunnossa-simulaatio (vain virheenkorjauksen koontiversio)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Näytä SIM-kortin osoitekirja"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Näytä sallitut numerot"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Näytä sallitut palvelunumerot"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Yhdistetty"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Jäädytetty"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Tuntematon"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Ensisijainen"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pakettia"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"tavua"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Lähetetyt tiedot:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Viesti odottaa:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Puhelinnumero:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Valitse radiotaajuus"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Äänipuhelujen verkon tyyppi:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Tietoverkon tyyppi:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Ohita verkon tyyppi:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice-raakadatan rekisteröinnin tila:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Raakadatan rekisteröinnin tila:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN-raakadatan rekisteröinnin tila:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Valitse puhelimen hakemisto"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Aseta ensisijainen verkon tyyppi:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping isäntänimelle (www.google.com), IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth-signaali on heikko. Kokeile vaihtaa kaiutinpuhelimeen."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Puhelun laatua koskeva ilmoitus"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Käytöstä poistetut SIP-tilit"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Viestiä ei voi lähettää tästä profiilista"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Työkäytäntö sallii sinun lähettää viestejä vain työprofiilista"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Peru"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Vaihda työprofiiliin"</string>
</resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index e891759..b5c2014 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Annuler"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Définir la carte eSIM amovible comme carte SIM par défaut"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Alimentation de radio cellulaire"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulation de l\'appareil hors service (version de débogage uniquement)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Afficher le carnet d\'adresses de la carte SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Afficher les numéros d\'appel fixes"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Afficher les numéros de service"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Connecté"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspendu"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Inconnu"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Principal"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"paquets"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"octets"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Données envoyées :"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Message en attente :"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Numéro de téléphone :"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Sélectionner la bande radio"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Type de réseau vocal :"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Type de réseau de données :"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Remplacer le type de réseau :"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"État d\'inscription brut de Voice :"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"État d\'inscription brut des données :"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"État d\'inscription brut des données WLAN :"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Sélectionner l\'indice du téléphone"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Définir le type de réseau préféré :"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Faire un ping de l\'IPv4 du nom d\'hôte (www.google.com) :"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Votre signal Bluetooth est faible. Essayez de passer au haut-parleur mains libres."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notification de qualité d\'appel"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Comptes SIP obsolètes"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Impossible d\'envoyer un message à partir de ce profil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Votre politique de l\'entreprise vous autorise à envoyer des messages uniquement à partir de votre profil professionnel"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Annuler"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Passer au profil professionnel"</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 8558b09..7e3ca20 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Annuler"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Définir l\'eSIM amovible comme SIM par défaut"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Alimentation radio mobile"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simuler une panne (version de débogage uniquement)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Afficher le carnet d\'adresses de la carte SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Afficher les numéros autorisés"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Afficher les numéros de service"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Connecté"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspendu"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Inconnu"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Principal"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"paquets"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"octets"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Données envoyées :"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Message en attente :"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Numéro de téléphone :"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Sélectionner une bande radio"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Type de réseau vocal :"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Type de réseau de données :"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Ignorer le type de réseau :"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"État d\'enregistrement brut de Voice :"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"État d\'enregistrement brut des données :"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"État d\'enregistrement brut des données WLAN :"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Sélectionner l\'identifiant du téléphone"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Définir le type de réseau préféré :"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Pinguer l\'IPv4 du nom d\'hôte (www.google.com) :"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Votre signal Bluetooth est faible. Essayez d\'utiliser le haut-parleur."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notification concernant la qualité de l\'appel"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Comptes SIP obsolètes"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Impossible d\'envoyer un message depuis ce profil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Votre règle professionnelle ne vous permet d\'envoyer des messages que depuis le profil professionnel"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Annuler"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Passer au profil professionnel"</string>
</resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 6e30618..6d92dd2 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Cancelar"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Establecer eSIM extraíble como predeterminada"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Alimentación da radio móbil"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simular Fóra de servizo (só compilación de depuración)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Ver axenda de enderezos da SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Ver números de marcación fixa"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Ver números de marcación de servizo"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Conectada"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspendido"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Descoñecido"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Principal"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Datos enviados:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Mensaxe en espera:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Número de teléfono:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Seleccionar banda de radio"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Tipo de rede de voz:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Tipo de rede de datos:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Ignorar tipo de rede:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Estado do rexistro básico dos datos de Voice:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Estado do rexistro básico dos datos:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Estado do rexistro básico dos datos de WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Seleccionar unha guía telefónica"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Definir o tipo de rede preferido:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Facer ping ao IPv4 do nome do servidor (www.google.com):"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"O teu sinal de Bluetooth é feble. Proba a cambiar ao altofalante."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notificación sobre a calidade da chamada"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Contas SIP obsoletas"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Non se poden enviar mensaxes desde este perfil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"A política do teu traballo só che permite enviar mensaxes desde o perfil de traballo"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Cancelar"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Cambiar ao perfil de traballo"</string>
</resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 5d8548e..0bc9c21 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"રદ કરો"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"કાઢી નાખી શકાય એવા ઇ-સિમ કાર્ડને ડિફૉલ્ટ તરીકે સેટ કરો"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"મોબાઇલ રેડિયો પાવર"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"\'સેવા ઉપલબ્ધ નથી\' મોડ સિમ્યુલેટ કરો (માત્ર ડિબગ બિલ્ડ માટે)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"સિમમાં સરનામા પુસ્તિકા જુઓ"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"ફિક્સ્ડ ડાયલિંગ નંબર જુઓ"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"સર્વિસ ડાયલિંગ નંબર જુઓ"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"કનેક્ટેડ"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"સસ્પેન્ડ કરી"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"અજાણ"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"પ્રાથમિક"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"બાઇટ"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"ડેટા મોકલ્યો:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"સંદેશ ઉપલબ્ધ છે:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ફોન નંબર:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"રેડિયો બૅન્ડ પસંદ કરો"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"વૉઇસ નેટવર્ક પ્રકાર:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ડેટા નેટવર્કનો પ્રકાર:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"નેટવર્કનો પ્રકાર ઓવરરાઇડ કરો:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice ડેટાનું મૂળભૂત રજિસ્ટ્રેશન સ્ટેટસ:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"ડેટાનું મૂળભૂત રજિસ્ટ્રેશન સ્ટેટસ:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN ડેટાનું મૂળભૂત રજિસ્ટ્રેશન સ્ટેટસ:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ફોનની અનુક્રમણિકા પસંદ કરો"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"પસંદગીનો નેટવર્ક પ્રકાર સેટ કરો:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"હોસ્ટનું નામ પિંગ કરો(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"તમારા બ્લૂટૂથનું સિગ્નલ નબળું છે. સ્પીકરફોન પર સ્વિચ કરવાનો પ્રયાસ કરો."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"કૉલની ક્વૉલિટી માટે નોટિફિકેશન"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"ટાળવામાં આવેલા SIP એકાઉન્ટ"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"આ પ્રોફાઇલ પરથી મેસેજ મોકલી શકતા નથી"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"તમારી ઑફિસની પૉલિસી તમને માત્ર ઑફિસની પ્રોફાઇલ પરથી જ મેસેજ મોકલવાની મંજૂરી આપે છે"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"રદ કરો"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"ઑફિસની પ્રોફાઇલ પર સ્વિચ કરો"</string>
</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 255063c..6ba73b9 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"रद्द करें"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"हटाए जा सकने वाले ई-सिम को डिफ़ॉल्ट के तौर पर सेट करें"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"मोबाइल रेडियो पावर"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"सिम्युलेट किया गया डिवाइस काम नहीं कर रहा है (सिर्फ़ डीबग के लिए बिल्ड)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"सिम में संपर्कों के पते की सूची देखें"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"फ़िक्स्ड डायलिंग नंबर देखें"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"सेवा के डायलिंग नंबर देखें"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"कनेक्ट किया गया"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"थोड़ी देर के लिए रोक लगी है"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"अज्ञात"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"प्राइमरी"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"बाइट"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"भेजा गया डेटा :"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"मैसेज वेटिंग:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"फ़ोन नंबर:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"रेडियो का बैंड चुनें"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"वॉइस नेटवर्क टाइप:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"डेटा नेटवर्क प्रकार:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"नेटवर्क टाइप बदलें:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice के रॉ डेटा के रजिस्ट्रेशन का स्टेटस:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"रॉ डेटा के रजिस्ट्रेशन का स्टेटस:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN के रॉ डेटा के रजिस्ट्रेशन का स्टेटस:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"फ़ोन इंडेक्स चुनें"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"पसंदीदा नेटवर्क प्रकार सेट करें:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"होस्टनाम(www.google.com) IPv4 पिंग करें:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"आपका ब्लूटूथ सिग्नल कमज़ोर है. स्पीकरफ़ोन की सुविधा का इस्तेमाल करें."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"कॉल की क्वालिटी की सूचना"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"ऐसे SIP खाते जिनका समर्थन रोक दिया गया है"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"इस प्रोफ़ाइल से मैसेज नहीं भेजा जा सकता"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"ऑफ़िस की नीति के तहत, वर्क प्रोफ़ाइल से ही मैसेज भेजा जा सकता है"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"रद्द करें"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"वर्क प्रोफ़ाइल पर स्विच करें"</string>
</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 89c6846..ffe6c69 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Odustani"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Postavljanje uklonjivog eSIM-a kao zadanog"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Snaga mobilnog radija"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulacija stanja \"izvan upotrebe\" (samo međuverzija programa za otklanjanje pogrešaka)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Prikaži imenik SIM-a"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Prikaži brojeve za fiksno biranje"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Prikaži brojeve za servisno biranje"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Povezano"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Obustavljeno"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Nepoznato"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primarno"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bajtovi"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Podaci poslani:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Poruka na čekanju:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefonski broj:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Odaberite radijsku frekvenciju"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Vrsta glasovne mreže:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Vrsta podatkovne mreže:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Nadjačavanje vrste mreže:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Stanje registracije neobrađenih glasovnih podataka:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Stanje registracije neobrađenih podatka:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Stanje registracije neobrađenih podataka WLAN-a:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Odaberite telefonski indeks"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Postavite željenu vrstu mreže:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Pinganje naziva hosta (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Signal Bluetootha je slab. Pokušajte se prebaciti na zvučnik."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Obavijest o kvaliteti poziva"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Obustavljeni SIP računi"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Slanje poruke s ovog profila nije moguće"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Vaša poslovna pravila dopuštaju vam da šaljete poruke samo s poslovnog profila"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Odustani"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Prelazak na poslovni profil"</string>
</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 10415e0..a7c1b4d 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Mégse"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Cserélhető eSIM beállítása alapértelmezettként"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobil rádióadó teljesítménye"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Szolgáltatáskiesés szimulációja (csak hibaelhárító build)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM-kártya telefonkönyvének megtekintése"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Fix hívószámok megtekintése"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Szolgáltatásszámok megtekintése"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Csatlakoztatva"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Felfüggesztve"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Ismeretlen"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Elsődleges"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"csomag"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bájt"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Elküldött adatok:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Üzenetek várakozása:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefonszám:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Rádióhullámsáv kiválasztása"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Hanghálózat típusa:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Adathálózat típusa:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Hálózattípus felülbírálata:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Beszédhang nyers regisztrációs állapota:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Adatok nyers regisztrációs állapota:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN-adatok nyers regisztrációs állapota:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Telefonkönyv kiválasztása"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Preferált hálózattípus beállítása:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Pingelt gazdagépnév (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Gyenge a Bluetooth-jel. Próbáljon kihangosítóra váltani."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Értesítés a hívás minőségéről"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Elavult SIP-fiókok"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Nem lehet üzenetet küldeni ebből a profilból"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"A munkahelyi házirend csak az üzenetek munkaprofilból való küldését engedélyezi"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Mégse"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Váltás munkaprofilra"</string>
</resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index ef5a3d8..b7a570b 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Չեղարկել"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Սահմանել հեռացվելի eSIM քարտը որպես կանխադրված"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Բջջային ռադիոազդանշանի հզորությունը"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Սպասարկման գոտուց դուրս գտնվելու սիմուլյացիա (միայն վրիպազերծման կառուցում)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Դիտել SIM քարտի հասցեագիրքը"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Տեսնել ամրակցված հեռախոսահամարները"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Տեսնել ծառայությունների հեռախոսահամարները"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Միացված է"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Անջատված է"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Անհայտ"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Հիմնական"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"բայթ"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Ուղարկված տվյալները՝"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Հաղորդագրության սպասում՝"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Հեռախոսահամար`"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Ընտրել հաճախությունների շերտը"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Ձայնային ցանցի տեսակը՝"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Տվյալների ցանցի տեսակը՝"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Փոխարինման ցանցի տեսակը՝"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Չմշակված ձայնային տվյալների գրանցման կարգավիճակը՝"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Չմշակված տվյալների գրանցման կարգավիճակը՝"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN-ի չմշակված տվյալների գրանցման կարգավիճակը՝"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Ընտրեք հեռախոսային կոդ"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Կարգավորեք ցանկալի ցանցի տեսակը՝"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Փինգ հանգույցի անուն(www.google.com) IPv4՝"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Ձեր Bluetooth-ի ազդանշանը թույլ է։ Փորձեք միացնել բարձրախոսը։"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Զանգի որակի մասին ծանուցում"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Հնացած SIP հաշիվներ"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Հնարավոր չէ հաղորդագրություն ուղարկել այս պրոֆիլից"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Ձեր աշխատանքային կանոնների համաձայն՝ դուք կարող եք հաղորդագրություն ուղարկել միայն աշխատանքային պրոֆիլից"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Չեղարկել"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Անցնել աշխատանքային պրոֆիլ"</string>
</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 4ccee18..7f31a9c 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Batal"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Tetapkan eSIM yang Dapat Dilepas sebagai Default"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Daya Radio Seluler"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulasi Tidak dapat Digunakan (Khusus Build Debug)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Lihat Buku Alamat SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Lihat Panggilan Terbatas"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Lihat Nomor Panggilan Layanan"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Terhubung"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Ditangguhkan"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Tidak diketahui"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primer"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"byte"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data Terkirim:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Pesan Menunggu:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Nomor Telepon:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Pilih Band Radio"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Jenis Jaringan Suara:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Jenis Jaringan Data:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Ganti Jenis Jaringan:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Status Pendaftaran Mentah Suara:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Status Pendaftaran Mentah Data:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Status Pendaftaran Mentah Data WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Pilih indeks ponsel"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Setel Jenis Jaringan yang Disukai:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping Hostname(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Sinyal bluetooth Anda lemah. Coba beralih ke speaker ponsel."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notifikasi Kualitas Panggilan"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Akun SIP yang tidak digunakan lagi"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Tidak dapat mengirim pesan dari profil ini"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Kebijakan kantor mengizinkan Anda mengirim pesan hanya dari profil kerja"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Batal"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Beralih ke profil kerja"</string>
</resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index ef24bbd..07b362d 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Hætta við"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Stilla laust eSIM sem sjálfgefið"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Loftnetsstyrkur farsíma"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Líkja eftir „Utan þjónustusvæðis“ (aðeins villuleitarsmíði)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Skoða símaskrá SIM-korts"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Skoða læst númeraval"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Skoða þjónustunúmer"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Tengt"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Lokað tímabundið"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Óþekkt"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Aðal"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pk."</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bæti"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Gögn send:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Skilaboð í bið:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Símanúmer:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Velja útvarpstíðni"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Netkerfi raddþjónustu:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Tegund gagnanets:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Hnekkja tegund netkerfis:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Skráningarstaða óunninna raddgagna:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Skráningarstaða óunninna gagna:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Skráningarstaða óunninna WLAN-gagna:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Velja atriðaskrá síma"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Veldu kjörsímkerfi:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping Hostname(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth-tengingin er léleg. Prófaðu að nota hátalara í staðinn."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Tilkynning um símtalsgæði"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Úreldir SIP-reikningar"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Ekki er hægt að senda skilaboð úr þessu sniði"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Vinnureglur gera þér aðeins kleift að senda skilaboð úr vinnusniði"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Hætta við"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Skipta yfir í vinnusnið"</string>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 7d8ba9d..edc2cc3 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Annulla"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Imposta la eSIM rimovibile come predefinita"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Potenza del segnale radio mobile"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulazione non disponibile (solo build di debug)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Visualizza rubrica SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Visualizza numeri consentiti"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Visualizza numeri dell\'elenco dei numeri di servizio"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Connesso"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Sospeso"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Sconosciuto"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Principale"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkt"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"byte"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Dati inviati:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Messaggio in attesa:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Numero di telefono:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Seleziona banda radio"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Tipo di rete vocale:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Tipo di rete di dati:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Override del tipo di rete:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Stato della registrazione dei dati vocali non elaborati:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Stato della registrazione dei dati non elaborati:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Stato della registrazione dei dati WLAN non elaborati:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Seleziona indice telefonico"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Imposta tipo di rete preferito:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping nome host (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Il segnale del Bluetooth è debole. Prova a passare al vivavoce."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notifica sulla qualità della chiamata"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Account SIP deprecati"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Impossibile inviare messaggi da questo profilo"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Le norme di lavoro ti consentono di inviare messaggi solo dal profilo di lavoro"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Annulla"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Passa al profilo di lavoro"</string>
</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index c47bd8b..cb16eb2 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -596,8 +596,7 @@
<string name="hac_mode_summary" msgid="7774989500136009881">"הפעלת תאימות למכשיר שמיעה"</string>
<string name="rtt_mode_title" msgid="3075948111362818043">"שיחת RTT (טקסט בזמן אמת)"</string>
<string name="rtt_mode_summary" msgid="8631541375609989562">"אפשר לשלוח הודעות טקסט בזמן שיחה"</string>
- <!-- syntax error in translation for rtt_mode_more_information (587500128658756318) org.xmlpull.v1.XmlPullParserException: expected: /br read: string (position:END_TAG </string>@1:430 in <string name="rtt_mode_more_information" msgid="587500128658756318">"RTT (טקסט בזמן אמת) היא תכונת נגישות שעוזרת לחרשים, לקויי שמיעה, לקויי דיבור או אנשים שלא יכולים להסתפק רק בשיחות קוליות.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>למידע נוסף</a>\n <br><br> - שיחות RTT מתומללות ונשמרות כהודעות\n <br> - אי אפשר להשתמש ב-RTT לשיחות וידאו"</string>
-) -->
+ <string name="rtt_mode_more_information" msgid="587500128658756318">"RTT (טקסט בזמן אמת) היא תכונת נגישות שעוזרת לחרשים, לקויי שמיעה, לקויי דיבור או אנשים שלא יכולים להסתפק רק בשיחות קוליות.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>למידע נוסף</a>\n <br><br> - שיחות RTT מתומללות ונשמרות כהודעות\n <br> - אי אפשר להשתמש ב-RTT לשיחות וידאו"</string>
<string name="no_rtt_when_roaming" msgid="5268008247378355389">"הערה: אי אפשר להשתמש ב-RTT בזמן נדידה"</string>
<string-array name="tty_mode_entries">
<item msgid="3238070884803849303">"TTY כבוי"</item>
@@ -838,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"ביטול"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"הגדרת eSIM נשלף כברירת המחדל"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"הפעלה של רדיו סלולרי"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"סימולציה של המצב \'לא בשירות\' (גרסת build לניפוי באגים בלבד)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"הצגת פנקס כתובות של SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"הצגת מספרי חיוג קבועים"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"מספרי חיוג לשירות"</string>
@@ -862,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"מקושר"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"בהשעיה"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"לא ידוע"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"ראשי"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"בייטים"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -888,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"הנתונים נשלחו:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"הודעה ממתינה:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"מספר טלפון:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"בחירת תדר רדיו"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"סוג רשת קולית:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"סוג רשת נתונים:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"שינוי מברירת המחדל של סוג הרשת:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"מצב הרישום הגולמי של הקול:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"מצב הרישום הגולמי של הנתונים:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"מצב הרישום הגולמי של נתוני WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"בחירת אינדקס טלפון"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"הגדרת סוג רשת מועדף:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"נדנוד לשם המארח (www.google.com) מסוג IPv4:"</string>
@@ -920,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"הקליטה של ה-Bluetooth חלשה. כדאי לעבור לדיבורית."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"התראה על איכות השיחה"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"חשבונות SIP שהוצאו משימוש"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"אי אפשר לשלוח הודעות מהפרופיל הזה"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"המדיניות של מקום העבודה מאפשרת לך לשלוח הודעות רק מפרופיל העבודה"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"ביטול"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"החלפה לפרופיל עבודה"</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 63470d9..647a54d 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"キャンセル"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"リムーバブル eSIM をデフォルトに設定"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"モバイル無線電力"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"圏外状態のシミュレート(デバッグビルドのみ)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM のアドレス帳を表示"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"発信番号制限を表示"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"サービス電話番号を表示"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"接続済み"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"停止中"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"不明"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"メイン"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"バイト"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"送信データ:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"メッセージ待機中:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"電話番号:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"無線バンドを選択"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"音声ネットワークの種類:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"データ ネットワークの種類:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"優先ネットワークの種類:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"音声の未加工登録の状態:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"データの未加工登録の状態:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN データの未加工登録の状態:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"スマートフォンのインデックスを選択"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"優先ネットワークの種類を設定:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"ホスト名(www.google.com)の ping(IPv4):"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth の信号強度が十分ではありません。スピーカーフォンに切り替えてみてください。"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"通話品質に関するお知らせ"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"サポートが終了した SIP アカウント"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"このプロファイルからはメッセージを送信できません"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"仕事用ポリシーでは、メッセージの送信を仕事用プロファイルからのみに制限できます"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"キャンセル"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"仕事用プロファイルに切り替える"</string>
</resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index a76a9b8..38aff47 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"გაუქმება"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"მოსახსნელი eSIM-ის ნაგულისხმევად დაყენება"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"მობილური რადიოკავშირის ელკვება"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"სიმულაცია სერვისის გარეშე (მხოლოდ გამართვის აგება)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM-ის მისამართების წიგნის ნახვა"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"დაშვებული ნომრების ნახვა"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"სერვისის დარეკილი ნომრების ნახვა"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"დაკავშირებულია"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"შეჩერებულია"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"უცნობი"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"ძირითადი"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"პკტ."</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"ბაიტი"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"გაგზავნილი მონაცემები:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"მომლოდინე შეტყობინება:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ტელეფონის ნომერი:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"რადიოდიაპაზონის არჩევა"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"ხმოვანი კავშირის ტიპი:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"მობილური ინტერნეტის ტიპი:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"ქსელის ტიპის უგულებელყოფა"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"ხმოვანი რეგისტრაციის სახელწუფო უწყება:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"მონაცემთა რეგისტრაციის სახელმწიფო უწყება"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN მონაცემთა ნედლეულის რეგისტრაციის სახელწიფო უწყება:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"სატელეფონო ინდექსის არჩევა"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"მიუთითეთ ქსელის სასურველი ტიპი:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping-ის მოთხოვნა ჰოსტის სახელისთვის(www.google.com), IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"თქვენი Bluetooth სიგნალი სუსტია. სცადეთ სპიკერფონზე გადართვა."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"შეტყობინება ზარის ხარისხის შესახებ"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"მოძველებული SIP ანგარიშები"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"ამ პროფილიდან შეტყობინებების გაგზავნა შეუძლებელია"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"თქვენი სამსახურის წესები საშუალებას გაძლევთ, შეტყობინებები გაგზავნოთ მხოლოდ სამუშაო პროფილიდან"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"გაუქმება"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"სამსახურის პროფილზე გადართვა"</string>
</resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 763b767..55026e5 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -499,7 +499,7 @@
<string name="disable_pin_ok" msgid="888505244389647754">"PIN тазаланды"</string>
<string name="pin_failed" msgid="4527347792881939652">"PIN дұрыс емес"</string>
<string name="pin_changed" msgid="7291153750090452808">"PIN коды жаңартылды"</string>
- <string name="puk_requested" msgid="2061337960609806851">"Құпия сөз дұрыс емес. PIN қазір блокталған. PUK коды сұралды."</string>
+ <string name="puk_requested" msgid="2061337960609806851">"Құпия сөз дұрыс емес. PIN қазір бөгелген. PUK коды сұралды."</string>
<string name="enter_pin2_text" msgid="7266379426804295979">"PIN2"</string>
<string name="oldPin2Label" msgid="4648543187859997203">"Ескі PIN2"</string>
<string name="newPin2Label" msgid="1840905981784453939">"Жаңа PIN2 коды"</string>
@@ -513,7 +513,7 @@
<string name="label_puk2_code" msgid="2852217004288085562">"PUK2 кодын енгізіңіз"</string>
<string name="fdn_enable_puk2_requested" msgid="5793652792131588041">"Құпия сөз дұрыс емес. PIN2 енді бөгелді. Әрекетті қайталау үшін PIN 2 кодын өзгертіңіз."</string>
<string name="puk2_requested" msgid="6992374450720307514">"Құпия сөз дұрыс емес. SIM қазір бекітілген. PUK2 кодын енгізіңіз."</string>
- <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 уақытша блокталған."</string>
+ <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 уақытша бөгелген."</string>
<string name="pin2_attempts" msgid="5625178102026453023">\n"<xliff:g id="NUMBER">%d</xliff:g> әрекет қалды."</string>
<string name="puk2_locked" msgid="6497760825455461057">"PUK2 құлыптаулы. Құлпын ашу үшін қызмет көрсетушіге хабарласыңыз."</string>
<string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 енді бөгелмеген"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Өшіру"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Алынбалы eSIM әдепкі етіп орнату"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Радиосигнал күші"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"\"Істен шыққан\" қызметін симуляциялау (түзету құрамасы ғана)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM мекенжай кітапшасын көру"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Рұқсат нөмірлерді көру"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Қызметтік теру нөмірлерін көру"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Жалғанған"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Уақытша тоқтатылған"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Белгісіз"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Негізгі"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"байт"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Жіберілген деректер:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Хабар күтуде:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Телефон нөмірі:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Радио жолағын таңдау"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Дауыс желісінің түрі:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Деректер желісінің түрі:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Желі түрін қайта анықтау:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Өңделмеген дыбыс деректерін тіркеу күйі:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Өңделмеген деректерді тіркеу күйі:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Өңделмеген WLAN деректерін тіркеу күйі:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Телефон индексін таңдау"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Қалаулы желі түрін реттеу:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping хост атауы (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth сигналы нашар. Спикерфонға ауысып көріңіз."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Қоңырау сапасы туралы хабарландыру"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Қолданыстан шыққан SIP аккаунттары"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Бұл профильден хабар жіберу мүмкін емес"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Жұмыс саясатыңызға сәйкес тек жұмыс профилінен хабар жіберуге болады."</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Бас тарту"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Жұмыс профиліне ауысу"</string>
</resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 53aceaf..ee9bb00 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"បោះបង់"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"កំណត់ eSIM ដែលអាចដកបានជាលំនាំដើម"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"ថាមពលវិទ្យុទូរសព្ទចល័ត"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"ត្រាប់តាមពេលគ្មានសេវា (កំណែបង្កើតសម្រាប់ជួសជុលតែប៉ុណ្ណោះ)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"មើលសៀវភៅអាសយដ្ឋានក្នុងស៊ីមកាត"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"មើលលេខហៅថេរ"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"មើលលេខហៅសេវាកម្ម"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"បានភ្ជាប់"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"បានផ្អាក"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"មិនស្គាល់"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"ចម្បង"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"បៃ"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"បានផ្ញើទិន្នន័យ៖"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"រង់ចាំសារ៖"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"លេខទូរសព្ទ៖"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"ជ្រើសរើសកម្រិតបញ្ជូនវិទ្យុ"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"ប្រភេទបណ្តាញសំឡេង៖"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ប្រភេទបណ្តាញទិន្នន័យ៖"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"លុបពីលើប្រភេទបណ្ដាញ៖"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"ស្ថានភាពចុះបញ្ជីសំឡេងដែលមិនទាន់វិភាគ៖"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"ស្ថានភាពចុះបញ្ជីទិន្នន័យដែលមិនទាន់វិភាគ៖"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"ស្ថានភាពចុះបញ្ជីទិន្នន័យ WLAN ដែលមិនទាន់វិភាគ៖"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ជ្រើសរើសសន្ទស្សន៍ទូរសព្ទ"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"កំណត់ប្រភេទបណ្ដាញដែលពេញចិត្ត៖"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"ភីងឈ្មោះម៉ាស៊ីន (www.google.com) IPv4 ៖"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"រលកសញ្ញាប៊្លូធូសរបស់អ្នកមានកម្រិតខ្សោយ។ សូមសាកល្បងប្ដូរទៅឧបករណ៍បំពងសំឡេងទូរសព្ទ។"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"ការជូនដំណឹងអំពីគុណភាពហៅទូរសព្ទ"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"គណនី SIP ដែលបានបញ្ឈប់"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"មិនអាចផ្ញើសារពីកម្រងព័ត៌មាននេះបានទេ"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"គោលការណ៍ការងាររបស់អ្នកអនុញ្ញាតឱ្យអ្នកផ្ញើសារបានតែពីកម្រងព័ត៌មានការងារប៉ុណ្ណោះ"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"បោះបង់"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"ប្ដូរទៅកម្រងព័ត៌មានការងារ"</string>
</resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index bfb3fdc..fc0e952 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -422,9 +422,9 @@
<item msgid="462340042928284921">"ಮುಖಪುಟ ಮಾತ್ರ"</item>
<item msgid="6058010046783562674">"ಸ್ವಯಂಚಾಲಿತ"</item>
</string-array>
- <string name="cdma_subscription_title" msgid="3449527179325589434">"CDMA ಸಬ್ಸ್ಕ್ರಿಪ್ಶನ್"</string>
+ <string name="cdma_subscription_title" msgid="3449527179325589434">"CDMA ಚಂದಾದಾರಿಕೆ"</string>
<string name="cdma_subscription_summary" msgid="5681152534466169001">"RUIM/ಸಿಮ್ ಮತ್ತು NV ರ ನಡುವೆ ಬದಲಾಯಿಸಿ"</string>
- <string name="cdma_subscription_dialogtitle" msgid="8872086335839723980">"ಸಬ್ಸ್ಕ್ರಿಪ್ಶನ್"</string>
+ <string name="cdma_subscription_dialogtitle" msgid="8872086335839723980">"ಚಂದಾದಾರಿಕೆ"</string>
<string-array name="cdma_subscription_choices">
<item msgid="7989486897370727698">"RUIM/ಸಿಮ್"</item>
<item msgid="5445342771222849381">"NV"</item>
@@ -616,7 +616,7 @@
<string name="ota_touch_activate" msgid="838764494319694754">"ನಿಮ್ಮ ಫೋನ್ ಸೇವೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು ವಿಶೇಷ ಕರೆಯೊಂದನ್ನು ಮಾಡಬೇಕಾಗುತ್ತದೆ. \n\n “ಸಕ್ರಿಯಗೊಳಿಸು” ಒತ್ತಿದ ಬಳಿಕ ನಿಮ್ಮ ಫೋನ್ ಸಕ್ರಿಯಗೊಳಿಸಲು ನೀಡಲಾಗುವ ಸೂಚನೆಗಳನ್ನು ಕೇಳಿ."</string>
<string name="ota_hfa_activation_title" msgid="3300556778212729671">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗುತ್ತಿದೆ..."</string>
<string name="ota_hfa_activation_dialog_message" msgid="7921718445773342996">"ನಿಮ್ಮ ಮೊಬೈಲ್ ಡೇಟಾ ಸೇವೆಯನ್ನು ಫೋನ್ ಸಕ್ರಿಯಗೊಳಿಸುತ್ತಿದೆ.\n\nಇದು ಸುಮಾರು 5 ನಿಮಿಷಗಳ ಕಾಲಾವಕಾಶ ತೆಗೆದುಕೊಳ್ಳಬಹುದು."</string>
- <string name="ota_skip_activation_dialog_title" msgid="7666611236789203797">"ಸಕ್ರಿಯಗೊಳಿಸುವುದನ್ನು ಸ್ಕಿಪ್ ಮಾಡಬೇಕೆ?"</string>
+ <string name="ota_skip_activation_dialog_title" msgid="7666611236789203797">"ಸಕ್ರಿಯಗೊಳಿಸುವುದನ್ನು ಸ್ಕಿಪ್ ಮಾಡುವುದೇ?"</string>
<string name="ota_skip_activation_dialog_message" msgid="6691722887019708713">"ನೀವು ಸಕ್ರಿಯಗೊಳಿಸುವುದನ್ನು ಸ್ಕಿಪ್ ಮಾಡಿದರೆ, ನೀವು ಕರೆಗಳನ್ನು ಮಾಡಲು ಅಥವಾ ಮೊಬೈಲ್ ಡೇಟಾ ನೆಟ್ವರ್ಕ್ಗಳಿಗೆ ಸಂಪರ್ಕಗೊಳಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ (ಆದರೂ ನೀವು ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗಳಿಗೆ ಸಂಪರ್ಕಗೊಳಿಸಬಹುದು). ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ನೀವು ಸಕ್ರಿಯಗೊಳಿಸುವವರೆಗೂ, ನೀವು ಅದನ್ನು ಪ್ರತಿಬಾರಿ ಆನ್ ಮಾಡಿದಾಗಲೆಲ್ಲಾ ಅದನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು ನಿಮಗೆ ತಿಳಿಸಲಾಗುತ್ತದೆ."</string>
<string name="ota_skip_activation_dialog_skip_label" msgid="5908029466817825633">"ಸ್ಕಿಪ್"</string>
<string name="ota_activate" msgid="7939695753665438357">"ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"ರದ್ದುಮಾಡಿ"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"ತೆಗೆದುಹಾಕಬಹುದಾದ eSIM ಅನ್ನು ಡೀಫಾಲ್ಟ್ ಆಗಿ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"ಮೊಬೈಲ್ ರೇಡಿಯೋ ಪವರ್"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"ಸೇವೆಯಲ್ಲಿಲ್ಲದಿರುವುದನ್ನು ಸಿಮ್ಯುಲೇಟ್ ಮಾಡುವುದು (ಡೀಬಗ್ ಬಿಲ್ಡ್ ಮಾತ್ರ)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"ಸಿಮ್ ವಿಳಾಸ ಪುಸ್ತಕವನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"ಸ್ಥಿರ ಡಯಲಿಂಗ್ ಸಂಖ್ಯೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"ಸೇವಾ ಡಯಲಿಂಗ್ ಸಂಖ್ಯೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"ಅಮಾನತುಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"ಅಪರಿಚಿತ"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"ಪ್ರಾಥಮಿಕ"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"ಬೈಟ್ಗಳು"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"ಡೇಟಾ ಕಳುಹಿಸಲಾಗಿದೆ:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"ಸಂದೇಶ ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ಫೋನ್ ಸಂಖ್ಯೆ:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"ರೇಡಿಯೋ ಬ್ಯಾಂಡ್ ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"ಧ್ವನಿ ನೆಟ್ವರ್ಕ್ ಪ್ರಕಾರ:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ಡೇಟಾ ನೆಟ್ವರ್ಕ್ ಪ್ರಕಾರ:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"ನೆಟ್ವರ್ಕ್ ಪ್ರಕಾರವನ್ನು ಅತಿಕ್ರಮಿಸಿ:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"ಧ್ವನಿ ರಾ ರಿಜಿಸ್ಟ್ರೇಷನ್ ಸ್ಟೇಟ್:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"ಡೇಟಾ ರಾ ರಿಜಿಸ್ಟ್ರೇಷನ್ ಸ್ಟೇಟ್:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN ಡೇಟಾ ರಾ ರಿಜಿಸ್ಟ್ರೇಷನ್ ಸ್ಟೇಟ್:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ಫೋನ್ ಸೂಚಿಕೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"ಆದ್ಯತೆಯ ನೆಟ್ವರ್ಕ್ ಪ್ರಕಾರವನ್ನು ಹೊಂದಿಸಿ:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"ಹೋಸ್ಟ್ ಹೆಸರನ್ನು ಪಿಂಗ್ ಮಾಡಿ(www.google.com) IPv4:"</string>
@@ -917,6 +921,10 @@
<string name="carrier_provisioning" msgid="2668065041869578376">"ವಾಹಕ ಪೂರೈಕೆಯ ಮಾಹಿತಿ"</string>
<string name="trigger_carrier_provisioning" msgid="1301829588620638234">"ಟ್ರಿಗರ್ ವಾಹಕ ಪೂರೈಕೆ"</string>
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ನಿಮ್ಮ ಬ್ಲೂಟೂತ್ ಸಿಗ್ನಲ್ ದುರ್ಬಲವಾಗಿದೆ. ಸ್ಪೀಕರ್ಫೋನ್ಗೆ ಬದಲಾಯಿಸಲು ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="call_quality_notification_name" msgid="3476828289553948830">"ಕರೆ ಗುಣಮಟ್ಟದ ನೋಟಿಫಿಕೇಶನ್"</string>
+ <string name="call_quality_notification_name" msgid="3476828289553948830">"ಕರೆ ಗುಣಮಟ್ಟದ ಅಧಿಸೂಚನೆ"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"ತಡೆಹಿಡಿಯಲಾಗಿರುವ SIP ಖಾತೆಗಳು"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"ಈ ಪ್ರೊಫೈಲ್ನಿಂದ ಸಂದೇಶವನ್ನು ಕಳುಹಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"ನಿಮ್ಮ ಉದ್ಯೋಗದ ನೀತಿಯು ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ನಿಂದ ಮಾತ್ರ ಸಂದೇಶವನ್ನು ಕಳುಹಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"ರದ್ದುಮಾಡಿ"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ಗೆ ಬದಲಿಸಿ"</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index db4ca40..a343348 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"취소"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"삭제 가능한 eSIM을 기본으로 설정"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"모바일 무선 전력"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"\'서비스 지역 벗어남\' 시뮬레이션(디버그 빌드만 해당)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM 주소록 보기"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"발신 허용 번호 보기"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"SDN(Service Dialing Numbers) 보기"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"연결됨"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"정지됨"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"알 수 없음"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"기본"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"패킷"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"바이트"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"보낸 데이터:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"메시지 대기 중:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"전화번호:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"무선 주파수 대역 선택"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"음성 네트워크 유형:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"데이터 네트워크 유형:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"네트워크 유형 재정의:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"음성 Raw 등록 상태:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"데이터 Raw 등록 상태:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN 데이터 Raw 등록 상태:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"휴대전화 색인 선택"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"기본 네트워크 유형 설정:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"핑 호스트 이름(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"블루투스 신호 강도가 약합니다. 스피커폰으로 전환해 보세요."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"통화 품질 알림"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"지원 중단된 SIP 계정"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"이 프로필에서 메시지를 보낼 수 없음"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"직장 정책을 사용하면 직장 프로필에서만 메시지를 보낼 수 있습니다."</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"취소"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"직장 프로필로 전환"</string>
</resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 7dd80e1..a0e990a 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -296,7 +296,7 @@
<string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Башка SIM картаны колднгндо <xliff:g id="CARRIER_NAME">%1$s</xliff:g> чалуулары жана дайындар кызмттары бөгөттлшү мүмкүн."</string>
<string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Колдонуудан чыккан SIP аккаунттары табылды жана өчүрүлдү"</string>
<string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP аркылуу чалуу Android платформасында мындан ары колдоого алынбайт.\nУчурдагы SIP аккаунттарыңыз (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) өчүрүлдү.\nЧалууларга колдонулган демейки аккаунтун жөндөөсүн ырастаңыз."</string>
- <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Параметрлерге өтүү"</string>
+ <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Жөндөөлөргө өтүү"</string>
<string name="data_usage_title" msgid="8438592133893837464">"Колдонмолордун трафиги"</string>
<string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_2">%2$s</xliff:g> аралыгында <xliff:g id="ID_1">%1$s</xliff:g> мобилдик трафик колдонулду"</string>
<string name="advanced_options_title" msgid="9208195294513520934">"Өркүндөтүлгөн"</string>
@@ -475,7 +475,7 @@
<string name="delete_fdn_contact" msgid="7027405651994507077">"Туруктуу терүү номерин жок кылуу"</string>
<string name="deleting_fdn_contact" msgid="6872320570844460428">"Туруктуу терүү номери жок кылынууда…"</string>
<string name="fdn_contact_deleted" msgid="1680714996763848838">"Туруктуу терүү номери өчүрүлдү."</string>
- <string name="pin2_invalid" msgid="2313954262684494442">"БНТ жаңырган жок, анткени туура эмес PIN код киргизилди."</string>
+ <string name="pin2_invalid" msgid="2313954262684494442">"БНТ жаңырган жок, анткени туура эмес PIN-код киргизилди."</string>
<string name="fdn_invalid_number" msgid="9067189814657840439">"Уруксат берилген номер жаңырган жок, себеби жазылган номердин саны <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> ашпашы керек."</string>
<string name="pin2_or_fdn_invalid" msgid="7542639487955868181">"БНТ жаңырган жок. PIN2 туура эмес, же телефон номуру жараксыз."</string>
<string name="fdn_failed" msgid="216592346853420250">"БНТ иши кыйрады."</string>
@@ -489,9 +489,9 @@
<string name="oldPinLabel" msgid="8618515202411987721">"Эски PIN"</string>
<string name="newPinLabel" msgid="3585899083055354732">"Жаңы PIN"</string>
<string name="confirmPinLabel" msgid="7783531218662473778">"Жаңы PIN\'ди ырастаңыз"</string>
- <string name="badPin" msgid="4549286285015892321">"Сиз киргизген эски PIN кодуңуз туура эмес. Кайра аракеттениңиз."</string>
+ <string name="badPin" msgid="4549286285015892321">"Сиз киргизген эски PIN-кодуңуз туура эмес. Кайра аракеттениңиз."</string>
<string name="mismatchPin" msgid="1467254768290323845">"Сиз терген PIN\'дер дал келишпейт. Кайра аракеттениңиз."</string>
- <string name="invalidPin" msgid="7363723429414001979">"Узундугу 4төн 8ге чейинки сандан турган PIN кодду териңиз."</string>
+ <string name="invalidPin" msgid="7363723429414001979">"Узундугу 4төн 8ге чейинки сандан турган PIN-кодду териңиз."</string>
<string name="disable_sim_pin" msgid="3112303905548613752">"SIM PIN тазалоо"</string>
<string name="enable_sim_pin" msgid="445461050748318980">"SIM PIN орнотуу"</string>
<string name="enable_in_progress" msgid="4135305985717272592">"PIN орнотулууда…"</string>
@@ -596,7 +596,7 @@
<string name="hac_mode_summary" msgid="7774989500136009881">"Угуу аппараты иштетилет"</string>
<string name="rtt_mode_title" msgid="3075948111362818043">"Чалуу учурунда анык убакыттагы билдирүү (RTT)"</string>
<string name="rtt_mode_summary" msgid="8631541375609989562">"Үн чалуунун учурунда жазышууга уруксат берүү"</string>
- <string name="rtt_mode_more_information" msgid="587500128658756318">"RTT функциясы уга албаган же сүйлөй албаган адамдарга арналган.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Кеңири маалымат</a>\n <br><br> - RTT функциясында чалуулар транскрипциялар катары сакталат\n <br> - RTT функациясы видео чалууда жеткиликсиз болот"</string>
+ <string name="rtt_mode_more_information" msgid="587500128658756318">"RTT функциясы угуу жана сүйлөө жөндөмдүүлүгү бузулган адамдарга арналат.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Кеңири маалымат</a>\n <br><br> - RTT функциясында чалуулар транскрипциялар катары сакталат\n <br> - RTT функациясы видео чалууда жеткиликсиз болот"</string>
<string name="no_rtt_when_roaming" msgid="5268008247378355389">"Эскертүү: Роуминг учурунда RTT жеткиликсиз"</string>
<string-array name="tty_mode_entries">
<item msgid="3238070884803849303">"Телетайп түзмөгү өчүк"</item>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Жокко чыгаруу"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Чыгарылуучу eSIM-картаны демейки катары коюу"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Мобилдик радионун кубаты"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Тейлөө аймагынын сыртында режимин иштетүү (Мүчүлүштүктөрдү оңдоо үчүн гана)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM картадагы дарек китепчесин көрүү"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Туруктуу терүү номерлерин көрүү"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Кызматтык терүү номерлерин көрүү"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Туташты"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Убактылуу токтотулду"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Белгисиз"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Башкы"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"байттар"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Дайындар жөнөтүлдү:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Күтүүдөгү билдирүү:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Телефон номери:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Радио жыштыгын тандоо"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Үн кызматынын тармагы:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Мобилдик тармагынын түрү:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Тармактын түрүн өзгөртүп коюу:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Үндү баштапкы каттоо статусу:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Маалыматты баштапкы каттоо статусу:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN маалыматын баштапкы каттоо статусу:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Телефондун индексин тандоо"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Тандалган тармак түрүн коюу:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"www.google.com, IPv4 үчүн ping сурамы:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth сигналыңыз начар. Спикерфонго которулуп көрүңүз."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Чалуунун сапаты тууралуу билдирме"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Колдонуудан чыккан SIP аккаунттары"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Билдирүүлөрдү бул профилден жөнөтүүгө болбойт"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Жумуш саясатыңызга ылайык, билдирүүлөрдү жумуш профилинен гана жөнөтө аласыз"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Жокко чыгаруу"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Жумуш профилине которулуу"</string>
</resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index bdd63c9..4809c0d 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"ຍົກເລີກ"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"ຕັ້ງຄ່າ eSIM ແບບຖອດໄດ້ໃຫ້ເປັນຄ່າເລີ່ມຕົ້ນ"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"ພະລັງງານວິທະຍຸມືຖື"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"ຈໍາລອງເຫດການບໍ່ພ້ອມໃຫ້ບໍລິການ (ສໍາລັບ Build ດີບັກເທົ່ານັ້ນ)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"ເບິ່ງສະໝຸດທີ່ຢູ່ໃນຊິມ"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"ເບິ່ງໝາຍເລກໂທອອກທີ່ກຳນົດ"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"ເບິ່ງໝາຍເລກບໍລິການໂທອອກ"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"ລະງັບໄວ້ແລ້ວ"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"ບໍ່ຮູ້ຈັກ"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"ຫຼັກ"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"ໄບຕ໌"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"ຂໍ້ມູນສົ່ງໄປແລ້ວ:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"ຂໍ້ຄວາມທີ່ຖ້າຢູ່:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ເບີໂທລະສັບ:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"ເລືອກແຖບຄວາມຖີ່ວິທະຍຸ"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"ປະເພດເຄືອຂ່າຍສຽງ:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ປະເພດເຄືອຂ່າຍຂໍ້ມູນ:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"ຍົກເລີກປະເພດເຄືອຂ່າຍ:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"ສະຖານະການລົງທະບຽນຂໍ້ມູນສຽງດິບ:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"ສະຖານະການລົງທະບຽນຂໍ້ມູນດິບ:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"ສະຖານະການລົງທະບຽນຂໍ້ມູນດິບຂອງ WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ເລືອກດັດຊະນີໂທລະສັບ"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"ເລືອກປະເພດເຄືອຂ່າຍທີ່ຕ້ອງການໃຊ້:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping Hostname(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ສັນຍານ Bluetooth ຂອງທ່ານອ່ອນ. ລອງສະຫຼັບລຳໂພງໂທລະສັບ."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"ການແຈ້ງເຕືອນຄຸນນະພາບການໂທ"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"ບັນຊີ SIP ທີ່ເຊົາສະໜັບສະໜູນ"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"ບໍ່ສາມາດສົ່ງຂໍ້ຄວາມຈາກໂປຣໄຟລ໌ນີ້ໄດ້"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"ນະໂຍບາຍບ່ອນເຮັດວຽກຂອງທ່ານອະນຸຍາດໃຫ້ທ່ານສົ່ງຂໍ້ຄວາມໄດ້ຈາກໂປຣໄຟລ໌ບ່ອນເຮັດວຽກເທົ່ານັ້ນ"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"ຍົກເລີກ"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"ສະຫຼັບໄປໃຊ້ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index bede872..6af3c18 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Atšaukti"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Nustatyti pašalinimą „eSIM“ kaip numatytąją"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobiliojo ryšio radijo signalas"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Modeliavimas neteikiamas (tik derinimo versija)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Žiūrėti SIM kortelės adresų knygą"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Žiūrėti fiksuotojo rinkimo numerius"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Žiūrėti paslaugos renkamus numerius"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Prisijungta"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Laikinai sustabdyta"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Nežinoma"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Pagrindinis"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pakuot."</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"B"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Duomenys išsiųsti:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Laukiantis pranešimas:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefono numeris:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Pasirinkti radijo dažnių juostą"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Balso tinklo tipas:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Duomenų tinklo tipas:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Nepaisyti tinklo tipo:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Neapdorotų balso duomenų registracijos būsena:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Neapdorotų duomenų registracijos būsena:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Neapdorotų WLAN duomenų registracijos būsena:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Pasirinkti telefono indeksą"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Nustatyti pageidaujamą tinklo tipą:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ryšio patikros prieglobos serverio pavadinimas (www.google.com) „IPv4“:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Silpnas „Bluetooth“ signalas. Pabandykite perjungti garsiakalbį."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Pranešimas apie skambučio kokybę"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Nebenaudojamos SIP paskyros"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Nepavyko išsiųsti pranešimo iš šio profilio"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Pagal jūsų darbo politiką galite siųsti pranešimus tik iš darbo profilio"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Atšaukti"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Perjungti į darbo profilį"</string>
</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index d012da9..eba0413 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Atcelt"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Iestatīt noņemamu eSIM kā noklusējumu"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobilā tālruņa radio signāla stiprums"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulācijas ierīce nedarbojas (tikai būvējuma atkļūdošana)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Skatīt SIM adrešu grāmatu"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Skatīt ierobežotā zvanu saraksta numurus"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Pakalpojuma iezvanes numuru skatīšana"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Savienojums izveidots"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Darbība apturēta"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Nezināms"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primārais"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"baiti"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Dati nosūtīti:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Ziņojums gaida:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Tālruņa numurs:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Atlasīt radio frekvenču joslu"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Balss pakalpojumu tīkla veids:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Datu tīkla veids:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Tīkla veida ignorēšana:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Neapstrādātu Voice datu reģistrācijas statuss:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Neapstrādātu datu reģistrācijas statuss:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Neapstrādātu WLAN datu reģistrācijas statuss:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Atlasīt tālruņa kodu"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Iestatiet ieteicamo tīkla veidu:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ehotestēšanas saimniekdatora nosaukuma (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth signāls ir vājš. Mēģiniet pārslēgties uz skaļruni."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Paziņojums par zvana kvalitāti"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"SIP konti, kuru darbība ir pārtraukta"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Nevar nosūtīt ziņojumu no šī profila"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Saskaņā ar darbavietas politiku drīkstat sūtīt ziņojumus tikai no darba profila."</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Atcelt"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Pārslēgties uz darba profilu"</string>
</resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 1251a46..d8a1cb6 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -75,7 +75,7 @@
<string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"Конфигурирајте ги поставките на сметка"</string>
<string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"Сите сметки за повици"</string>
<string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"Изберете кои сметки може да повикуваат"</string>
- <string name="wifi_calling" msgid="3650509202851355742">"Повици преку Wi-Fi"</string>
+ <string name="wifi_calling" msgid="3650509202851355742">"Повик преку Wi-Fi"</string>
<string name="connection_service_default_label" msgid="7332739049855715584">"Вградена услуга на поврзување"</string>
<string name="voicemail" msgid="7697769412804195032">"Говорна пошта"</string>
<string name="voicemail_settings_with_label" msgid="4228431668214894138">"Говорна пошта (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -152,7 +152,7 @@
<string name="change_num" msgid="6982164494063109334">"Ажурирај"</string>
<string-array name="clir_display_values">
<item msgid="8477364191403806960">"Стандардна мрежа"</item>
- <item msgid="6813323051965618926">"Скриј број"</item>
+ <item msgid="6813323051965618926">"Сокриј број"</item>
<item msgid="9150034130629852635">"Прикажи број"</item>
</string-array>
<string name="vm_changed" msgid="4739599044379692505">"Бројот на говорна пошта е променет."</string>
@@ -296,7 +296,7 @@
<string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Повиците и услугите за интернет на <xliff:g id="CARRIER_NAME">%1$s</xliff:g> може да се блокирани со друга SIM-картичка."</string>
<string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Најдени и отстранети се неподдржани сметки на SIP"</string>
<string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Платформата Android веќе не поддржува повикување преку SIP.\nВашите постојни сметки на SIP <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> се отстранети.\nПотврдете ја вашата стандардна поставка за повикување."</string>
- <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Отворете „Поставки“"</string>
+ <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Одете во „Поставки“"</string>
<string name="data_usage_title" msgid="8438592133893837464">"Потрошен интернет од апликации"</string>
<string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> потрошен мобилен интернет во периодот <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="advanced_options_title" msgid="9208195294513520934">"Напредни"</string>
@@ -309,7 +309,7 @@
<string name="sim_selection_required_pref" msgid="6985901872978341314">"Треба да се избере"</string>
<string name="sim_change_data_title" msgid="9142726786345906606">"Да се промени SIM за интернет?"</string>
<string name="sim_change_data_message" msgid="3567358694255933280">"Да се користи <xliff:g id="NEW_SIM">%1$s</xliff:g> наместо <xliff:g id="OLD_SIM">%2$s</xliff:g> за мобилен интернет?"</string>
- <string name="wifi_calling_settings_title" msgid="5800018845662016507">"Повици преку Wi-Fi"</string>
+ <string name="wifi_calling_settings_title" msgid="5800018845662016507">"Повик преку Wi-Fi"</string>
<string name="video_calling_settings_title" msgid="342829454913266078">"Видеоповикување преку оператор"</string>
<string name="gsm_umts_options" msgid="4968446771519376808">"Опции за GSM/UMTS"</string>
<string name="cdma_options" msgid="3669592472226145665">"Опции на CDMA"</string>
@@ -458,7 +458,7 @@
<string name="auto_retry_mode_title" msgid="2985801935424422340">"Автоматски повторен обид"</string>
<string name="auto_retry_mode_summary" msgid="2863919925349511402">"Овозможи режим на автоматски повторен обид"</string>
<string name="tty_mode_not_allowed_video_call" msgid="6551976083652752815">"Промена на режим TTY не е дозволена за време на видеоповик"</string>
- <string name="menu_add" msgid="5616487894975773141">"Додајте контакт"</string>
+ <string name="menu_add" msgid="5616487894975773141">"Додај контакт"</string>
<string name="menu_edit" msgid="3593856941552460706">"Измени контакт"</string>
<string name="menu_delete" msgid="6326861853830546488">"Избриши контакт"</string>
<string name="menu_dial" msgid="4178537318419450012">"Повикај контакт"</string>
@@ -596,7 +596,7 @@
<string name="hac_mode_summary" msgid="7774989500136009881">"Вклучи компатибилност на слушни помагала"</string>
<string name="rtt_mode_title" msgid="3075948111362818043">"Повик со „Текст во реално време“ (RTT)"</string>
<string name="rtt_mode_summary" msgid="8631541375609989562">"Дозволете пораки во текот на гласовен повик"</string>
- <string name="rtt_mode_more_information" msgid="587500128658756318">"RTT им помага на корисниците коишто се глуви, наглуви, имаат попреченост во говорот или пак, не им е доволен само глас.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Дознајте повеќе</a>\n <br><br> - RTT-повиците се зачувуваат како транскрипции на пораки\n <br> - Функцијата RTT не е достапна за видеоповици"</string>
+ <string name="rtt_mode_more_information" msgid="587500128658756318">"Функцијата RTT им помага на повикувачи коишто се глуви, слабо слушаат, имаат говорна мана или пак, им е потребно многу повеќе од глас.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Дознајте повеќе</a>\n <br><br> - Повиците со RTT се зачувуваат како препис на порака\n <br> - Функцијата RTT не е достапна за видеоповици"</string>
<string name="no_rtt_when_roaming" msgid="5268008247378355389">"Напомена: RTT не е достапна во роаминг"</string>
<string-array name="tty_mode_entries">
<item msgid="3238070884803849303">"TTY исклучени"</item>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Откажи"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Поставување eSIM што може да се отстрани како стандардна"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Радио-напојување на мобилен"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Симулирање „Надвор од употреба“ (само за верзиите за отстранување грешки)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Прикажи именик на SIM-картичката"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Прикажи броеви со ограничено бирање"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Прикажи броеви за бирање служби"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Поврзан"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Суспендиран"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Непознат"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Примарен"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"бајти"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Испратени податоци:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Порака на чекање:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Телефонски број:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Избери појас на радио"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Тип гласовна мрежа:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Тип мрежа на податоци:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Тип отфрлање мрежа:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Необработена состојба на регистрација на Voice:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Необработена состојба на регистрација на податоци:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Необработена состојба на регистрација на податоци за WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Изберете индекс на телефонот"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Поставете претпочитан тип мрежа:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Пингај го хостот (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Вашиот сигнал на Bluetooth е слаб. Обидете се со префрлање на интерфон."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Известување за квалитет на повик"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Неподдржани сметки на SIP"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Не може да се испрати порака од профилов"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Вашето работно правило ви дозволува да испраќате пораки само од работниот профил"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Откажи"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Префрли на работен профил"</string>
</resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 0f94a97..4977d58 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -181,7 +181,7 @@
<string name="not_allowed" msgid="8541221928746104798">"നിങ്ങളുടെ സിം കാർഡ് ഈ നെറ്റ്വർക്കിലേക്ക് ഒരു കണക്ഷൻ അനുവദിക്കുന്നില്ല."</string>
<string name="connect_later" msgid="1950138106010005425">"ഈ നെറ്റ്വർക്കിൽ ഇപ്പോൾ കണക്റ്റുചെയ്യാനാകുന്നില്ല. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string>
<string name="registration_done" msgid="5337407023566953292">"ഒരു നെറ്റ്വർക്കിൽ രജിസ്റ്റർ ചെയ്തു."</string>
- <string name="already_auto" msgid="8607068290733079336">"സ്വയമേവ ഉള്ള തിരഞ്ഞെടുക്കലിൽ ഇതിനകം ഉണ്ട്."</string>
+ <string name="already_auto" msgid="8607068290733079336">"സ്വമേധയാ ഉള്ള തിരഞ്ഞെടുക്കലിൽ ഇതിനകം ഉണ്ട്."</string>
<string name="select_automatically" msgid="779750291257872651">"സ്വയമേവ നെറ്റ്വർക്ക് തിരഞ്ഞെടുക്കുക"</string>
<string name="manual_mode_disallowed_summary" msgid="3970048592179890197">"%1$s എന്നതിലേക്ക് കണക്റ്റ് ചെയ്തിരിക്കുമ്പോൾ ലഭ്യമല്ല"</string>
<string name="network_select_title" msgid="4117305053881611988">"നെറ്റ്വർക്ക്"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"റദ്ദാക്കുക"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"നീക്കം ചെയ്യാവുന്ന ഇ-സിം ഡിഫോൾട്ടായി സജ്ജീകരിക്കുക"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"മൊബൈൽ റേഡിയോ പവർ"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"സേവനം ലഭ്യമല്ലെന്ന് അനുകരിക്കുക (ഡീബഗ് ബിൽഡ് മാത്രം)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"സിം വിലാസ പുസ്തകം കാണുക"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"സ്ഥിര ഡയലിംഗ് നമ്പറുകൾ കാണുക"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"സർവീസ് ഡയലിംഗ് നമ്പറുകൾ കാണുക"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"കണക്റ്റ് ചെയ്തു"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"താൽക്കാലികമായി റദ്ദാക്കി"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"അജ്ഞാതം"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"പ്രാഥമികം"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"ബൈറ്റുകൾ"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"അയച്ച ഡാറ്റ:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"സന്ദേശം കാത്തിരിക്കുന്നു:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ഫോൺ നമ്പർ:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"റേഡിയോ ബാൻഡ് തിരഞ്ഞെടുക്കുക"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"വോയ്സ് നെറ്റ്വർക്ക് തരം:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ഡാറ്റ നെറ്റ്വർക്ക് തരം:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"അസാധുവാക്കൽ നെറ്റ്വർക്ക് തരം:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"റോ വോയ്സ് രജിസ്ട്രേഷന്റെ നില:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"റോ ഡാറ്റാ രജിസ്ട്രേഷന്റെ നില:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN റോ ഡാറ്റാ രജിസ്ട്രേഷന്റെ നില:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ഫോൺ സൂചിക തിരഞ്ഞെടുക്കുക"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"തിരഞ്ഞെടുത്ത നെറ്റ്വർക്ക് തരം സജ്ജീകരിക്കുക:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"ഹോസ്റ്റുനാമം(www.google.com) IPv4 പിംഗ് ചെയ്യുക:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"നിങ്ങളുടെ Bluetooth സിഗ്നൽ ദുർബലമാണ്. സ്പീക്കർഫോണിലേക്ക് മാറ്റി നോക്കൂ."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"കോൾ നിലവാര അറിയിപ്പ്"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"അവസാനിപ്പിച്ച SIP അക്കൗണ്ടുകൾ"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"ഈ പ്രൊഫൈലിൽ നിന്ന് സന്ദേശം അയയ്ക്കാനാകില്ല"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"ഔദ്യോഗിക പ്രൊഫൈലിൽ നിന്ന് മാത്രം സന്ദേശം അയയ്ക്കാനാണ് നിങ്ങളുടെ ഔദ്യോഗിക നയം അനുവദിക്കുന്നത്"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"റദ്ദാക്കുക"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"ഔദ്യോഗിക പ്രൊഫൈലിലേക്ക് മാറുക"</string>
</resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index e5e54cc..920074d 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Цуцлах"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Салгах боломжтой eSIM-г өгөгдмөлөөр тохируулах"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Мобайл радио цахилгаан"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Үйлчилгээний хүрээнээс гарсан нөхцөл байдлыг загварчлах (зөвхөн дебагийн хийц)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM хаягийн лавлахыг харах"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Залгахаар тохируулсан дугаарыг харах"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Үйлчилгээний залгах дугаарыг харах"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Холбогдсон"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Түр хаасан"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Тодорхойгүй"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Үндсэн"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"байт"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Дата илгээсэн:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Мессежийг хүлээж байна:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Утасны дугаар:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Радио мессежийг сонгох"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Дуут сүлжээний төрөл:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Дата сүлжээний төрөл:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Сүлжээний төрлийг дарах:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Дуу хоолойн түүхий бүртгэлийн төлөв:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Өгөгдлийн түүхий бүртгэлийн төлөв:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN-н өгөгдлийн түүхий бүртгэлийн төлөв:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Утасны индекс сонгох"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Сонгосон сүлжээний төрлийг тохируулна уу:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Пинг хостны нэр(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Таны Bluetooth-н дохио сул байна. Чанга яригчтай утас руу сэлгэж үзнэ үү."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Дуудлагын чанарын мэдэгдэл"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"SIP-н зогсоосон бүртгэлүүд"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Энэ профайлаас мессеж илгээх боломжгүй"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Таны ажлын бодлого танд зөвхөн ажлын профайлаас мессеж илгээхийг зөвшөөрдөг"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Цуцлах"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Ажлын профайл руу сэлгэх"</string>
</resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 394ac9d..e914d4c 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"रद्द करा"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"काढून टाकण्यायोग्य eSIM डीफॉल्ट म्हणून सेट करा"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"मोबाइल रेडिओ पॉवर"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"सेवा बंद आहे सिम्युलेट करा (फक्त डीबगचा बिल्ड)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"सिम ॲड्रेस बुक पहा"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"निश्चित डायलिंग नंबर पहा"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"सर्व्हिस डायलिंग नंबर पहा"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"कनेक्ट केले"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"निलंबित केले"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"अज्ञात"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"प्राथमिक"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"बाइट"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"डेटा पाठवला:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"मेसेज वेटिंग:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"फोन नंबर:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"रेडिओ बँड निवडा"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"व्हॉइस नेटवर्कचा प्रकार:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"डेटा नेटवर्कचा प्रकार:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"नेटवर्क प्रकार ओव्हरराइड करा:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"व्हॉइस रॉ नोंदणी स्थिती:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"डेटा रॉ नोंदणी स्थिती:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN डेटा रॉ नोंदणी स्थिती:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"फोनची अनुक्रमणिका निवडा"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"प्राधान्य दिलेला नेटवर्कचा प्रकार सेट करा:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"पिंग होस्ट नाव(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"तुमचा ब्लूटूथ सिग्नल कमकुवत आहे. स्पीकरफोनवर स्विच करून पहा."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"कॉल गुणवत्ता सूचना"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"कालबाह्य झालेली SIP खाती"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"या प्रोफाइलवरून मेसेज पाठवू शकत नाही"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"तुमचे कामाशी संबंधित धोरण हे तुम्हाला फक्त तुमच्या कार्य प्रोफाइलवरून मेसेज पाठवण्याची अनुमती देते"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"रद्द करा"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"कार्य प्रोफाइलवर स्विच करा"</string>
</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 2b81900..6ff9621 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Batal"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Tetapkan eSIM Boleh Tanggal sebagai Lalai"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Kuasa Radio Mudah Alih"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulasi Rosak (Binaan Penyahpepijatan sahaja)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Lihat Buku Alamat SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Lihat Nombor Dailan Tetap"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Lihat Nombor Dailan Perkhidmatan"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Disambungkan"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Digantung"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Tidak diketahui"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Utama"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"bgksn"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bait"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data Dihantar:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Mesej Menunggu:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Nombor Telefon:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Pilih Jalur Radio"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Jenis Rangkaian Suara:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Jenis Rangkaian Data:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Jenis Rangkaian Penggantian:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Keadaan Pendaftaran Awal Suara:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Keadaan Pendaftaran Awal Data:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Keadaan Pendaftaran Awal Data WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Pilih indeks telefon"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Tetapkan Jenis Rangkaian Pilihan:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping Nama Hos(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Isyarat bluetooth anda lemah. Cuba beralih kepada fon pembesar suara."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Pemberitahuan Kualiti Panggilan"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Akaun SIP ditamatkan"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Tidak dapat menghantar mesej daripada profil ini"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Dasar kerja anda membenarkan anda menghantar mesej hanya daripada profil kerja"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Batal"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Beralih kepada profil kerja"</string>
</resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 0a653c1..dea794d 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -108,21 +108,21 @@
<string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"ခေါ်ဆိုမှုအားလုံးအား တဆင့်ထပ်ပို့နေသည်"</string>
<string name="sum_cfu_enabled" msgid="5806923046528144526">"ခေါ်ဆိုမှုအားလုံးကို <xliff:g id="PHONENUMBER">{0}</xliff:g> သို့ ထပ်ဆင့်ပို့နေသည်"</string>
<string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"ဖုန်းနံပါတ်မှာ မရှိပါ"</string>
- <string name="sum_cfu_disabled" msgid="5010617134210809853">"ပိတ်"</string>
+ <string name="sum_cfu_disabled" msgid="5010617134210809853">"ပိတ်ထားသည်"</string>
<string name="labelCFB" msgid="615265213360512768">"မအားလပ်ချိန်"</string>
<string name="messageCFB" msgid="1958017270393563388">"ဖုန်းမအားလျှင် ပို့ရန် နံပါတ်"</string>
<string name="sum_cfb_enabled" msgid="332037613072049492">"<xliff:g id="PHONENUMBER">{0}</xliff:g> သို့ခေါ်ဆိုမှုအား တဆင့်ထပ်ပို့နေသည်"</string>
- <string name="sum_cfb_disabled" msgid="3589913334164866035">"ပိတ်"</string>
+ <string name="sum_cfb_disabled" msgid="3589913334164866035">"ပိတ်ထားသည်"</string>
<string name="disable_cfb_forbidden" msgid="4831494744351633961">"သင့် ဖုန်း အလုပ်များနေစဉ် call forwarding ပြုလုပ်မှု ပယ်ဖျက်ရန် သင့် အော်ပရေတာမှ ခွင့်မပြုပါ"</string>
<string name="labelCFNRy" msgid="3403533792248457946">"ဖုန်းမကိုင်ချိန်"</string>
<string name="messageCFNRy" msgid="7644434155765359009">"ဖုန်းမကိုင်လျှင်ပို့ရန် နံပါတ်"</string>
<string name="sum_cfnry_enabled" msgid="3000500837493854799">"<xliff:g id="PHONENUMBER">{0}</xliff:g> သို့ခေါ်ဆိုမှုအား တဆင့်ထပ်ပို့နေသည်"</string>
- <string name="sum_cfnry_disabled" msgid="1990563512406017880">"ပိတ်"</string>
+ <string name="sum_cfnry_disabled" msgid="1990563512406017880">"ပိတ်ထားသည်"</string>
<string name="disable_cfnry_forbidden" msgid="3174731413216550689">"သင့် ဖုန်း မကိုင်စဉ် call forwarding ပြုလုပ်မှု ပယ်ဖျက်ရန် သင့် အော်ပရေတာမှ ခွင့်မပြုပါ"</string>
<string name="labelCFNRc" msgid="4163399350778066013">"ဆက်သွယ်၍ မရချိန်"</string>
<string name="messageCFNRc" msgid="6980340731313007250">"ဖုန်းခေါ်မရလျှင် ပို့ရန် နံပါတ်"</string>
<string name="sum_cfnrc_enabled" msgid="1799069234006073477">"<xliff:g id="PHONENUMBER">{0}</xliff:g> သို့ခေါ်ဆိုမှုအား တဆင့်ထပ်ပို့နေသည်"</string>
- <string name="sum_cfnrc_disabled" msgid="739289696796917683">"ပိတ်"</string>
+ <string name="sum_cfnrc_disabled" msgid="739289696796917683">"ပိတ်ထားသည်"</string>
<string name="disable_cfnrc_forbidden" msgid="775348748084726890">"သင့် ဖုန်း ဆက်သွယ်မှု လုပ်လို့မရစဉ် call forwarding ပြုလုပ်မှု ပယ်ဖျက်ရန် သင့် အော်ပရေတာမှ ခွင့်မပြုပါ"</string>
<string name="registration_cf_forbidden" msgid="4386482610771190420">"သင်၏ ဝန်ဆောင်မှုပေးသူသည် အဝင်ဖုန်းကို ဆက်ပို့ပေးခြင်းအား ပံ့ပိုးမထားပါ။"</string>
<string name="cdma_call_waiting" msgid="4565070960879673216">"အဝင်ဖုန်း စောင့်ဆိုင်းခြင်းကို ဖွင့်မလား။"</string>
@@ -178,7 +178,7 @@
<string name="empty_networks_list" msgid="9216418268008582342">"ကွန်ယက်များ မတွေ့ပါ"</string>
<string name="network_query_error" msgid="3862515805115145124">"ကွန်ရက် ရှာမတွေ့ပါ။ ထပ်လုပ်ကြည့်ပါ။"</string>
<string name="register_on_network" msgid="4194770527833960423">"<xliff:g id="NETWORK">%s</xliff:g> တွင် မှတ်ပုံတင်နေခြင်း…"</string>
- <string name="not_allowed" msgid="8541221928746104798">"ဒီကွန်ယက်ကို ဆက်သွယ်ရန် သင့်ရဲ့ ဆင်းမ်ကတ်မှ ခွင့်မပြုပါ"</string>
+ <string name="not_allowed" msgid="8541221928746104798">"ဒီကွန်ယက်ကို ဆက်သွယ်ရန် သင့်ရဲ့ ဆင်းမ်ကဒ်မှ ခွင့်မပြုပါ"</string>
<string name="connect_later" msgid="1950138106010005425">"ကွန်ယက်ကို ဆက်သွယ်လို့မရပါ။ နောင်မှ ပြန်ကြိုးစားပါ"</string>
<string name="registration_done" msgid="5337407023566953292">"ကွန်ယက်ပေါ်တွင် မှတ်ပုံတင်ထားခြင်း"</string>
<string name="already_auto" msgid="8607068290733079336">"အလိုအလျောက် ရွေးချယ်မှုထဲတွင် ပါပြီးသားဖြစ်သည်။"</string>
@@ -479,11 +479,11 @@
<string name="fdn_invalid_number" msgid="9067189814657840439">"နံပါတ်တွင် ဂဏန်းအလုံးရေ <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> ကျော်နေပါသဖြင့် FDN ကို အပ်ဒိတ်လုပ်၍ မရပါ။"</string>
<string name="pin2_or_fdn_invalid" msgid="7542639487955868181">"FDN ပြောင်းလဲမှု မဖြစ်ပါ။ ပင်နံပါတ် ၂ မှားယွင်းခြင်း သို့မဟုတ် ဖုန်းနံပါတ်ကို ငြင်းဖယ်ခံရခြင်း တစ်ခုခုဖြစ်ပါသည်"</string>
<string name="fdn_failed" msgid="216592346853420250">"FDN လုပ်ဆောင်ချက် မအောင်မြင်ပါ"</string>
- <string name="simContacts_emptyLoading" msgid="4989040293858675483">"ဆင်းမ်ကတ်မှ ဖတ်နေပါသည်..."</string>
+ <string name="simContacts_emptyLoading" msgid="4989040293858675483">"ဆင်းမ်ကဒ်မှ ဖတ်နေပါသည်..."</string>
<string name="simContacts_empty" msgid="1135632055473689521">"ဆင်းမ်ကဒ်ထဲတွင် လိပ်စာများ မရှိပါ"</string>
<string name="simContacts_title" msgid="2714029230160136647">"ထည့်ယူရန် လိပ်စာများ ရွေးပါ"</string>
<string name="simContacts_airplaneMode" msgid="4654884030631503808">"SIM ကဒ်မှ အဆက်အသွယ်များ သွင်းယူရန် လေယာဉ်ပျံမုဒ် ပိတ်ထားပါ။"</string>
- <string name="enable_pin" msgid="967674051730845376">"ဆင်းမ်ကတ် ပင်နံပါတ်ကို ပယ်ဖျက်၊ပြုလုပ်ရန်"</string>
+ <string name="enable_pin" msgid="967674051730845376">"ဆင်းမ် ပင်နံပါတ်ကို ပယ်ဖျက်၊ပြုလုပ်ရန်"</string>
<string name="change_pin" msgid="3657869530942905790">"ဆင်းမ်ကတ် ပင်နံပါတ်ပြောင်းရန်"</string>
<string name="enter_pin_text" msgid="3182311451978663356">"ဆင်းမ်ကတ် ပင်နံပါတ်:"</string>
<string name="oldPinLabel" msgid="8618515202411987721">"ပင်နံပါတ် အဟောင်း"</string>
@@ -588,7 +588,7 @@
<string name="onscreenVideoCallText" msgid="1743992456126258698">"ဗီဒီယိုခေါ်ဆိုမှု"</string>
<string name="importSimEntry" msgid="3892354284082689894">"ထည့်ရန်"</string>
<string name="importAllSimEntries" msgid="2628391505643564007">"အားလုံးကို ထည့်သွင်းပါ"</string>
- <string name="importingSimContacts" msgid="4995457122107888932">"ဆင်းမ်ကတ်မှ လိပ်စာများအား ထည့်ပါ"</string>
+ <string name="importingSimContacts" msgid="4995457122107888932">"ဆင်းမ်ကဒ်မှ လိပ်စာများအား ထည့်ပါ"</string>
<string name="importToFDNfromContacts" msgid="5068664870738407341">"လိပ်စားများထဲမှ ထည့်ပါ"</string>
<string name="singleContactImportedMsg" msgid="3619804066300998934">"တင်သွင်းခဲ့သည့် အဆက်အသွယ်"</string>
<string name="failedToImportSingleContactMsg" msgid="228095510489830266">"အဆက်အသွယ်ကို တင်သွင်း မရခဲ့"</string>
@@ -663,7 +663,7 @@
<string name="voicemail_set_pin_dialog_title" msgid="7005128605986960003">"ပင်နံပါတ်သတ်မှတ်ရန်"</string>
<string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"ပင်နံပါတ်ပြောင်းပါ"</string>
<string name="preference_category_ringtone" msgid="8787281191375434976">"ဖုန်းမြည်သံ & တုန်ခါသံ"</string>
- <string name="pstn_connection_service_label" msgid="9200102709997537069">"တပ်ဆင်ပြီး ဆင်းမ်ကတ် ကဒ်များ"</string>
+ <string name="pstn_connection_service_label" msgid="9200102709997537069">"တပ်ဆင်ပြီး ဆင်းမ် ကဒ်များ"</string>
<string name="enable_video_calling_title" msgid="7246600931634161830">"ဗီဒီယို ခေါ်ဆိုမှုများကို ဖွင့်ထားရန်"</string>
<string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"ဗီဒီယိုခေါ်ဆိုမှု ဖွင့်လိုပါက အဆင့်မြှင့်ထားသည့် 4G LTE မုဒ်ကို ကွန်ရက်ဆက်တင်များတွင် ဖွင့်ရပါမည်။"</string>
<string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"ကွန်ရက် ဆက်တင်များ"</string>
@@ -764,7 +764,7 @@
<string name="clh_callFailed_interworking_unspecified_txt" msgid="7969686413930847182">"ခေါ်ဆိုမှုကို မပြုလုပ်နိုင်ပါ။ အမှားကုဒ် ၁၂၇။"</string>
<string name="labelCallBarring" msgid="4180377113052853173">"ခေါ်ဆိုမှုကို ပိတ်ပင်ရန်"</string>
<string name="sum_call_barring_enabled" msgid="5184331188926370824">"ဖွင့်"</string>
- <string name="sum_call_barring_disabled" msgid="5699448000600153096">"ပိတ်"</string>
+ <string name="sum_call_barring_disabled" msgid="5699448000600153096">"ပိတ်ထားသည်"</string>
<string name="call_barring_baoc" msgid="7400892586336429326">"အထွက်ခေါ်ဆိုမှုအားလုံး"</string>
<string name="call_barring_baoc_enabled" msgid="3131509193386668182">"အထွက်ခေါ်ဆိုမှုအားလုံး ပိတ်ထားခြင်းကို ပယ်ဖျက်မလား။"</string>
<string name="call_barring_baoc_disabled" msgid="8534224684091141509">"အထွက်ခေါ်ဆိုမှုအားလုံးကို ပိတ်မလား။"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"မလုပ်တော့ပါ"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"ဖယ်ရှားနိုင်သော eSIM ကို မူရင်းအဖြစ် သတ်မှတ်ရန်"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"မိုဘိုင်း ရေဒီယိုစွမ်းအား"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"အသွင်တူပြုလုပ်သောစက် အလုပ်မလုပ်ပါ (အမှားရှာပြင်ခြင်းသာလျှင်)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM ထဲရှိ လိပ်စာ စာအုပ်ကိုကြည့်ပါ"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"ခေါ်ဆိုရန် ကန့်သတ် နံပါတ်ကို ကြည့်မည်"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"ခေါ်ဆိုသည့်ဝန်ဆောင်မှုနံပါတ်အားကြည့်မည်"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"ချိတ်ဆက်ထားသည်"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"ဆိုင်းငံ့ထားသည်"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"မသိရသေးပါ"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"အဓိက"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"ဘိုက်"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"ဒေတာ ပို့လိုက်ပါပြီ −"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"မက်ဆေ့ဂျ်ကို စောင့်နေသည် −"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ဖုန်းနံပါတ် −"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"ရေဒီယိုလိုင်း ရွေးချယ်ပါ"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"အသံကွန်ရက် အမျိုးအစား −"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ဒေတာကွန်ရက် အမျိုးအစား −"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"အစားထိုး ကွန်ရက်အမျိုးအစား-"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"ပကတိအသံ မှတ်ပုံတင်ခြင်းအခြေအနေ-"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"ပကတိဒေတာ မှတ်ပုံတင်ခြင်းအခြေအနေ-"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"ပကတိ WLAN ဒေတာ မှတ်ပုံတင်ခြင်းအခြေအနေ-"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ဖုန်းအညွှန်း ရွေးရန်"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"ပိုနှစ်သက်သည့် ကွန်ရက်အမျိုးအစားကို သတ်မှတ်ပါ −"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"ပင်ကို လက်ခံဝန်ဆောင်ပေးသူအမည်(www.google.com) မှာ IPv4 -"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"သင်၏ ဘလူးတုသ်လိုင်းဆွဲအား မကောင်းပါ။ စပီကာဖုန်းသို့ ပြောင်းကြည့်ပါ။"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"ခေါ်ဆိုမှုအရည်အသွေး အကြောင်းကြားချက်"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"ရပ်ဆိုင်းထားသော SIP အကောင့်များ"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"ဤပရိုဖိုင်မှ မက်ဆေ့ဂျ်ပို့၍ မရပါ"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"သင့်အလုပ်ခွင်မူဝါဒသည် အလုပ်ပရိုဖိုင်မှသာ မက်ဆေ့ဂျ်ပို့ခွင့်ပြုသည်"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"မလုပ်တော့"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"အလုပ်ပရိုဖိုင်သို့ ပြောင်းရန်"</string>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 9d5d48f..700221e 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -692,7 +692,7 @@
<string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"Angi en ny PIN-kode"</string>
<string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"PIN-koden må bestå av <xliff:g id="MIN">%1$d</xliff:g>-<xliff:g id="MAX">%2$d</xliff:g> sifre."</string>
<string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"Bekreft PIN-koden"</string>
- <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"PIN-kodene er ikke like"</string>
+ <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"PIN-kodene stemmer ikke overens"</string>
<string name="change_pin_succeeded" msgid="2504705600693014403">"PIN-koden for talemeldinger ble oppdatert"</string>
<string name="change_pin_system_error" msgid="7772788809875146873">"Kan ikke angi PIN-kode"</string>
<string name="mobile_data_status_roaming_turned_off_subtext" msgid="6840673347416227054">"Data-roaming er slått av"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Avbryt"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Angi flyttbart eSIM-kort som standard"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Strømforsyning for mobilradio"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Ute av drift-simulering (bare for feilsøkingsversjoner)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Se adressebok for SIM-kort"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Vis forhåndsbestemte numre"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Vis tjenestenumre"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Tilkoblet"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Sperret midlertidig"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Ukjent"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primær"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pakker"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"byte"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data sendt:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Melding venter:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefonnummer:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Velg radiobånd"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Nettverkstype for tale:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Type datanettverk:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Overstyr nettverkstype:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Rå registreringstilstand for Voice:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Rå registreringstilstand for data:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Rå registreringstilstand for WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Velg telefonindeks"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Velg foretrukket nettverkstype:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping vertsnavn(www.google.no) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth-signalet er svakt. Prøv å bytte til høyttaleren."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Varsel om anropskvalitet"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Avviklede SIP-kontoer"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Kan ikke sende meldingen fra denne profilen"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"I henhold til jobbreglene dine kan du bare sende meldinger fra jobbprofilen"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Avbryt"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Bytt til jobbprofilen"</string>
</resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 721d091..66c41c9 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -688,7 +688,7 @@
<string name="change_pin_cancel_label" msgid="2301711566758827936">"रद्द गर्नुहोस्"</string>
<string name="change_pin_ok_label" msgid="6861082678817785330">"ठिक छ"</string>
<string name="change_pin_enter_old_pin_header" msgid="853151335217594829">"आफ्नो पुरानो PIN को पुष्टि गर्नुहोस्"</string>
- <string name="change_pin_enter_old_pin_hint" msgid="8801292976275169367">"जारी राख्नका लागि आफ्नो भ्वाइस मेलको PIN हाल्नुहोस्।"</string>
+ <string name="change_pin_enter_old_pin_hint" msgid="8801292976275169367">"जारी राख्नका लागि आफ्नो भ्वाइस मेलको PIN प्रविष्टि गर्नुहोस्।"</string>
<string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"एउटा नयाँ PIN सेट गर्नुहोस्"</string>
<string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"PIN <xliff:g id="MIN">%1$d</xliff:g>-<xliff:g id="MAX">%2$d</xliff:g> अङ्कको हुनु पर्छ।"</string>
<string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"तपाईँको PIN को पुष्टि गर्नुहोस्"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"रद्द गर्नुहोस्"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"हटाउन मिल्ने eSIM डिफल्ट रूपमा सेट गर्नुहोस्"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"मोबाइल रेडियोको पावर"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"\"सेवा उपलब्ध छैन\" सिमुलेट गर्नुहोस् (डिबग बिल्डमा मात्र सिमुलेट गर्न मिल्छ)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM को ठेगाना पुस्तिका हेर्नुहोस्"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"स्थिर डायल गर्ने नम्बरहरू हेर्नुहोस्"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"सेवामा डायल गर्ने नम्बरहरू हेर्नुहोस्"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"जडान गरियो"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"निलम्बित"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"अज्ञात"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"प्राथमिक"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"बाइटहरू"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"डेटा पठाइयो:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"सन्देशलाई कुर्दै:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"फोन नम्बर:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"रेडियोको ब्यान्ड चयन गर्नुहोस्"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Voice नेटवर्कको प्रकार:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"डेटा नेटवर्कको प्रकार:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"ओभरराइड नेटवर्कको प्रकार:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"अप्रशोधित आवाजको दर्ताको स्थिति:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"अप्रशोधित डेटाको दर्ताको स्थिति:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"अप्रशोधित WLAN डेटाको दर्ताको स्थिति:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"फोनको सूचक चयन गर्नुहोस्"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"रुचाइएको नेटवर्कको प्रकार सेट गर्नुहोस्:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"होस्टनाम (www.google.com) IPv4 मा पिङ गर्नुहोस्:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ब्लुटुथको सिग्नल कमजोर छ। स्पिकरफोन प्रयोग गरी हेर्नुहोस्।"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"कलको गुणस्तरसम्बन्धी सूचना"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"चल्तीबाट हटाइएका SIP खाताहरू"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"यो प्रोफाइलबाट म्यासेज पठाउन सकिँदैन"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"तपाईंको कामसम्बन्धी नीतिअनुसार कार्य प्रोफाइलबाट मात्र म्यासेज पठाउन सकिन्छ"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"रद्द गर्नुहोस्"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"कार्य प्रोफाइल प्रयोग गर्नुहोस्"</string>
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 77c8748..8ccd4c3 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Annuleren"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Verwisselbare e-simkaart instellen als standaard"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobiel radiovermogen"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"\'Niet in gebruik\' simuleren (alleen in foutopsporingsbuild)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Adresboek op simkaart bekijken"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Vaste nummers bekijken"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Servicenummers bekijken"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Verbonden"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Opgeschort"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Onbekend"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primair"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pakketten"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Gegevens verzonden:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Wachtend bericht:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefoonnummer:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Radioband selecteren"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Type spraaknetwerk:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Type gegevensnetwerk:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Netwerktype overschrijven:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Registratiestatus van onbewerkte Voice-gegevens:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Registratiestatus van onbewerkte gegevens:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Registratiestatus van onbewerkte WLAN-gegevens:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Telefoonindex selecteren"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Voorkeursnetwerktype instellen:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"IPv4 van hostnaam (www.google.com) pingen:"</string>
@@ -919,4 +923,12 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Het bluetooth-signaal is zwak. Schakel over naar bellen op luidspreker."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Melding over gesprekskwaliteit"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Beëindigde SIP-accounts"</string>
+ <!-- no translation found for send_from_work_profile_title (9201528838448432473) -->
+ <skip />
+ <!-- no translation found for send_from_work_profile_description (5002701841936861636) -->
+ <skip />
+ <!-- no translation found for send_from_work_profile_cancel (177746511030381711) -->
+ <skip />
+ <!-- no translation found for send_from_work_profile_action_str (6892775562934243337) -->
+ <skip />
</resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index cfa958d..8dd4ea2 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"ବାତିଲ କରନ୍ତୁ"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"କାଢ଼ି ହେଉଥିବା eSIMକୁ ଡିଫଲ୍ଟ ଭାବେ ସେଟ କରନ୍ତୁ"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"ମୋବାଇଲ୍ ରେଡିଓ ପାୱାର୍"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"\"କାମ କରୁନାହିଁ\"ରେ ସିମୁଲେଟ କରନ୍ତୁ (କେବଳ ଡିବଗ ବିଲ୍ଡ)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"ସିମ୍ରେ ଥିବା ଠିକଣା ପୁସ୍ତକ ଦେଖନ୍ତୁ"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"ସ୍ଥାୟୀ ଡାଏଲିଂ ନମ୍ୱରଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"ସର୍ଭିସ୍ ଡାଏଲିଂ ନମ୍ୱରଗୁଡ଼ିକ ଦେଖନ୍ତୁ"</string>
@@ -849,7 +850,7 @@
<string name="radio_info_ims_reg_status" msgid="25582845222446390">"IMS ରେଜିଷ୍ଟ୍ରସନ୍: <xliff:g id="STATUS">%1$s</xliff:g>\nଭଏସ୍ ଓଭର୍ LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nଭଏସ୍ ଓଭର୍ ୱାଇ-ଫାଇ: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nଭିଡିଓ କଲିଙ୍ଗ: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nUT ଇଣ୍ଟର୍ଫେସ୍: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
<string name="radioInfo_service_in" msgid="45753418231446400">"ସେବାରେ ଅଛି"</string>
<string name="radioInfo_service_out" msgid="287972405416142312">"ଏବେ କାମ କରୁନାହିଁ"</string>
- <string name="radioInfo_service_emergency" msgid="4763879891415016848">"କେବଳ ଜରୁରୀକାଳୀନ କଲ"</string>
+ <string name="radioInfo_service_emergency" msgid="4763879891415016848">"କେବଳ ଜରୁରୀକାଳୀନ କଲ୍"</string>
<string name="radioInfo_service_off" msgid="3456583511226783064">"ରେଡିଓ ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="radioInfo_roaming_in" msgid="3156335577793145965">"ରୋମିଂ"</string>
<string name="radioInfo_roaming_not" msgid="1904547918725478110">"ରୋମିଂରେ ନାହିଁ"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"ସଂଯୋଗ କରାଯାଇଛି"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"ନିଲମ୍ବନ କରାଯାଇଛି"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"ଅଜଣା"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"ପ୍ରାଥମିକ"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"ପ୍ୟାକେଟ୍"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"ବାଇଟ୍ସ"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"ଡାଟା ପଠାଯାଇଛି:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"ବାର୍ତ୍ତା ଅପକ୍ଷାରତ:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ଫୋନ୍ ନମ୍ଵର"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"ରେଡିଓ ବ୍ୟାଣ୍ଡ ଚୟନ କରନ୍ତୁ"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"ଭଏସ୍ ନେଟ୍ୱାର୍କ ପ୍ରକାର:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ଡାଟା ନେଟ୍ୱାର୍କର ପ୍ରକାର:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"ନେଟୱାର୍କ ପ୍ରକାରକୁ ଓଭରରାଇଡ କରନ୍ତୁ:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice Raw ପଞ୍ଜିକରଣ ସ୍ଥିତି:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Data Raw ପଞ୍ଜିକରଣ ସ୍ଥିତି:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN Data Raw ପଞ୍ଜିକରଣ ସ୍ଥିତି:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ଫୋନ୍ ଇଣ୍ଡେକ୍ସ ବାଛନ୍ତୁ"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"ନିଜ ପସନ୍ଦର ନେଟ୍ୱାର୍କ ପ୍ରକାର ସେଟ୍ କରନ୍ତୁ:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"ହୋଷ୍ଟନାମ (www.google.com) IPv4 ପିଙ୍ଗ କରନ୍ତୁ:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ଆପଣଙ୍କ ବ୍ଲୁଟୁଥ୍ ସିଗନାଲ୍ ଦୁର୍ବଳ ଅଛି। ସ୍ପିକରଫୋନକୁ ସ୍ୱିଚ୍ କରିବା ପାଇଁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"କଲ୍ ଗୁଣବତ୍ତା ବିଜ୍ଞପ୍ତି"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"ଅସମର୍ଥିତ SIP ଆକାଉଣ୍ଟଗୁଡ଼ିକ"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"ଏହି ପ୍ରୋଫାଇଲରୁ ମେସେଜ ପଠାଯାଇପାରିବ ନାହିଁ"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"ଆପଣଙ୍କ ୱାର୍କ ନୀତି ଆପଣଙ୍କୁ କେବଳ ୱାର୍କ ପ୍ରୋଫାଇଲରୁ ମେସେଜ ପଠାଇବାକୁ ଅନୁମତି ଦିଏ"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"ବାତିଲ କରନ୍ତୁ"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"ୱାର୍କ ପ୍ରୋଫାଇଲକୁ ସ୍ୱିଚ କରନ୍ତୁ"</string>
</resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index b9651b1..a779567 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -692,7 +692,7 @@
<string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"ਇੱਕ ਨਵਾਂ PIN ਸੈੱਟ ਕਰੋ"</string>
<string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"PIN ਲਾਜ਼ਮੀ ਤੌਰ \'ਤੇ <xliff:g id="MIN">%1$d</xliff:g>-<xliff:g id="MAX">%2$d</xliff:g> ਅੰਕਾਂ ਦਾ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ।"</string>
<string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"ਆਪਣੇ PIN ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ"</string>
- <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"ਪਿੰਨ ਮੇਲ ਨਹੀਂ ਖਾਂਦੇ"</string>
+ <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"PIN ਮੇਲ ਨਹੀਂ ਖਾਂਦੇ"</string>
<string name="change_pin_succeeded" msgid="2504705600693014403">"ਵੌਇਸਮੇਲ PIN ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="change_pin_system_error" msgid="7772788809875146873">"PIN ਸਥਾਪਤ ਕਰਨ ਦੇ ਅਯੋਗ"</string>
<string name="mobile_data_status_roaming_turned_off_subtext" msgid="6840673347416227054">" ਡਾਟਾ ਰੋਮਿੰਗ ਬੰਦ ਹੈ"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"ਰੱਦ ਕਰੋ"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"ਹਟਾਉਣਯੋਗ ਈ-ਸਿਮ ਨੂੰ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਜੋਂ ਸੈੱਟ ਕਰੋ"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"ਮੋਬਾਈਲ ਰੇਡੀਓ ਪਾਵਰ"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"\'ਸੇਵਾ ਵਿੱਚ ਨਹੀਂ\' ਨੂੰ ਸਿਮੂਲੇਟ ਕਰੋ (ਸਿਰਫ਼ ਡੀਬੱਗ ਬਿਲਡ)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"ਸਿਮ ਦੀ ਪਤਾ ਬੁੱਕ ਦੇਖੋ"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"ਫਿਕਸਡ ਡਾਇਲਿੰਗ ਨੰਬਰ ਦੇਖੋ"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"ਸੇਵਾ ਡਾਇਲਿੰਗ ਨੰਬਰ ਦੇਖੋ"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"ਕਨੈਕਟ ਹੈ"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"ਮੁਅੱਤਲ ਕੀਤਾ ਗਿਆ"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"ਅਗਿਆਤ"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"ਪ੍ਰਾਇਮਰੀ"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"ਬਾਈਟਾਂ"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"ਡਾਟਾ ਭੇਜਿਆ ਗਿਆ:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"ਸੁਨੇਹੇ ਦੀ ਉਡੀਕ:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ਫ਼ੋਨ ਨੰਬਰ:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"ਰੇਡੀਓ ਬੈਂਡ ਚੁਣੋ"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"ਅਵਾਜ਼ੀ ਨੈੱਟਵਰਕ ਦੀ ਕਿਸਮ:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ਡਾਟਾ ਨੈੱਟਵਰਕ ਕਿਸਮ:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"ਓਵਰਰਾਈਡ ਨੈੱਟਵਰਕ ਕਿਸਮ:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"ਗੈਰ-ਸੰਪਾਦਿਤ ਅਵਾਜ਼ ਦੇ ਰੂਪ ਵਿੱਚ ਰਜਿਸਟ੍ਰੇਸ਼ਨ ਦੀ ਸਥਿਤੀ:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"ਗੈਰ-ਸੰਪਾਦਿਤ ਡਾਟੇ ਦੇ ਰੂਪ ਵਿੱਚ ਰਜਿਸਟ੍ਰੇਸ਼ਨ ਦੀ ਸਥਿਤੀ:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"ਗੈਰ-ਸੰਪਾਦਿਤ WLAN ਡਾਟੇ ਦੇ ਰੂਪ ਵਿੱਚ ਰਜਿਸਟ੍ਰੇਸ਼ਨ ਦੀ ਸਥਿਤੀ:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ਫ਼ੋਨ ਕ੍ਰਮ-ਸੂਚੀ ਚੁਣੋ"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"ਤਰਜੀਹੀ ਨੈੱਟਵਰਕ ਕਿਸਮ ਸੈੱਟ ਕਰੋ:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"ਹੋਸਟਨੇਮ (www.google.com) IPv4 ਪਿੰਗ ਕਰੋ:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ਤੁਹਾਡਾ ਬਲੂਟੁੱਥ ਸਿਗਨਲ ਕਮਜ਼ੋਰ ਹੈ। ਸਪੀਕਰਫ਼ੋਨ \'ਤੇ ਲਿਜਾ ਕੇ ਦੇਖੋ।"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"ਕਾਲ ਦੀ ਕੁਆਲਿਟੀ ਸੰਬੰਧੀ ਸੂਚਨਾ"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"ਨਾਪਸੰਦ ਕੀਤੇ SIP ਖਾਤੇ"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"ਇਸ ਪ੍ਰੋਫਾਈਲ ਤੋਂ ਸੁਨੇਹਾ ਨਹੀਂ ਭੇਜਿਆ ਜਾ ਸਕਦਾ"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"ਤੁਹਾਡੀ ਕਾਰਜ ਨੀਤੀ ਤੁਹਾਨੂੰ ਸਿਰਫ਼ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਤੋਂ ਹੀ ਸੁਨੇਹਾ ਭੇਜਣ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"ਰੱਦ ਕਰੋ"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਜਾਓ"</string>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index e8e3f6f..9b060aa 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -594,7 +594,7 @@
<string name="failedToImportSingleContactMsg" msgid="228095510489830266">"Nie udało się zaimportować kontaktu"</string>
<string name="hac_mode_title" msgid="4127986689621125468">"Aparaty słuchowe"</string>
<string name="hac_mode_summary" msgid="7774989500136009881">"Włącz funkcje zgodności z aparatem słuchowym"</string>
- <string name="rtt_mode_title" msgid="3075948111362818043">"Połączenie RTT (tekst w czasie rzeczywistym)"</string>
+ <string name="rtt_mode_title" msgid="3075948111362818043">"Połączenie RTT (wysyłanie SMS-ów w czasie rzeczywistym)"</string>
<string name="rtt_mode_summary" msgid="8631541375609989562">"Zezwalaj na wymianę SMS-ów podczas rozmowy głosowej"</string>
<string name="rtt_mode_more_information" msgid="587500128658756318">"RTT pomaga osobom niesłyszącym, niedosłyszącym, mającym problemy z mówieniem oraz potrzebującym czegoś więcej oprócz głosu.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Więcej informacji</a>\n <br><br> – Połączenia RTT są zapisywane jako transkrypcje\n <br> – Funkcja RTT jest niedostępna w przypadku rozmów wideo"</string>
<string name="no_rtt_when_roaming" msgid="5268008247378355389">"Uwaga: funkcja RTT jest niedostępna podczas roamingu"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Anuluj"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Ustaw wymienną kartę eSIM jako domyślną"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Moc sygnału komórkowego"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Symulowana przerwa w działaniu usługi (tylko w kompilacji do debugowania)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Wyświetl książkę adresową z karty SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Wyświetl ustalone numery"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Wyświetl numery usług"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Połączono"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Zawieszone"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Brak informacji"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Główny"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"– pakiety"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"B"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Dane wysłane:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Wiadomość oczekująca:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Numer telefonu:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Wybierz pasmo radiowe"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Typ sieci dla połączeń głosowych:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Typ sieci danych:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Zastępuj typ sieci:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Stan nieprzetworzonej rejestracji Voice:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Stan nieprzetworzonej rejestracji danych:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Stan nieprzetworzonej rejestracji danych WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Wybierz indeks telefonu"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Ustaw preferowany typ sieci:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping nazwa_hosta(www.google.pl) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Twój sygnał Bluetooth jest słaby. Spróbuj przełączyć na głośnik."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Powiadomienie o jakości połączenia"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Wycofane konta SIP"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Nie możesz wysłać wiadomości z tego profilu"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Zasady obowiązujące w firmie zezwalają na wysyłanie wiadomości tylko z profilu służbowego"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Anuluj"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Przełącz na profil służbowy"</string>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 172ba28..1afd7aa 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Cancelar"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Predefinir eSIM removível"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Potência do rádio móvel"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simule o modo fora de serviço (apenas na versão de depuração)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Ver livro de endereços do SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Ver números autorizados"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Ver números de marcação de serviços"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Ligado"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspenso"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Desconhecido"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Principal"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Dados enviados:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Mensagem em espera:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Número de telefone:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Selecionar banda de rádio"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Tipo de rede de voz:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Tipo de rede de dados:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Substituir tipo de rede:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Estado do registo de dados do Voice não processados:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Estado do registo de dados não processados:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Estado do registo de dados WLAN não processados:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Selecionar lista telefónica"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Definir tipo de rede preferido:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Nome do anfitrião de ping (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"O seu sinal Bluetooth é fraco. Tente mudar para o altifalante."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notificação de qualidade da chamada"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Contas SIP descontinuadas"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Não é possível enviar mensagens a partir deste perfil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"A sua Política de Trabalho permite-lhe enviar mensagens apenas a partir do perfil de trabalho"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Cancelar"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Mudar para perfil de trabalho"</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 8761db9..eca6894 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Cancelar"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Definir eSIM removível como padrão"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Potência do rádio celular"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simular o modo fora de serviço (somente build de depuração)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Ver o catálogo de endereços do chip"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Ver números de discagem fixa"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Ver números de discagem do serviço"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Conectado"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspenso"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Desconhecido"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Principal"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Dados enviados:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Mensagem em espera:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Número de telefone:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Selecionar frequência de rádio"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Tipo de rede de voz:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Tipo de rede de dados:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Substituir tipo de rede:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Estado do registro bruto do Voice:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Estado do registro bruto de dados:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Estado do registro bruto de dados WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Selecionar lista telefônica"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Definir tipo de rede preferido:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Dar um ping no nome do host (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"O sinal do Bluetooth está fraco. Mude para o viva-voz."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notificação sobre a qualidade da chamada"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Contas SIP suspensas"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Não é possível enviar mensagens deste perfil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Sua política de trabalho só permite o envio de mensagens pelo perfil de trabalho"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Cancelar"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Mudar para o perfil de trabalho"</string>
</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 3f906fd..8e59266 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Anulează"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Setează cartela eSIM portabilă drept prestabilită"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Alimentare radio celular"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulează modul în afara ariei de acoperire (numai în versiunea pentru remedierea erorilor)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Afișează agenda de pe SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Afișează numerele pentru apeluri restricționate"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Vezi numere de apelare de serviciu"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Conectat"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspendat"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Necunoscut"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Principal"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pachete"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"byți"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Date trimise:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Mesaj în așteptare:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Număr de telefon:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Selectează banda radio"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Tipul rețelei de voce:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Tipul rețelei de date:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Modifică tipul de rețea:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Starea brută a înregistrării vocii:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Starea brută a înregistrării datelor:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Starea brută a înregistrării datelor WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Selectează indexul telefonului"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Setați tipul preferat de rețea:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Dă ping adresei IPv4 a numelui de gazdă (www.google.com):"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Semnalul Bluetooth este slab. Încearcă să folosești difuzorul."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notificare privind calitatea apelului"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Conturi SIP învechite"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Nu se poate trimite un mesaj de pe acest profil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Politica privind activitatea îți permite să trimiți mesaje numai din profilul de serviciu"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Anulează"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Comută la profilul de serviciu"</string>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index bf0a56d..61d796a 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Отмена"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Установить съемную eSIM-карту в качестве используемой по умолчанию"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Мощность радиосигнала"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Моделирование нахождения вне зоны обслуживания (только отладочная сборка)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Посмотреть адресную книгу на SIM-карте"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Список разрешенных номеров"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Посмотреть номера служебного набора"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Подключено"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Заблокировано"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Неизвестно"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Основной"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"байт"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Отправлено данных:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Параллельное сообщение:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Номер телефона:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Выбрать радиостанцию"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Тип сети для голосовой связи:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Тип сети для передачи данных:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Переопределение типа сети:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Статус начальной регистрации Voice:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Статус начальной регистрации данных:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Статус начальной регистрации данных WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Выберите телефонный код"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Настроить предпочтительный тип сети:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Имя хоста запроса ping для www.google.com, IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Слабый сигнал Bluetooth. Попробуйте переключиться на громкую связь."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Уведомление о качестве связи"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Неподдерживаемые SIP-аккаунты"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Нельзя отправить сообщение из этого профиля"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Согласно правилам вашей организации вы можете отправлять сообщения только из рабочего профиля."</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Отмена"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Перейти в рабочий профиль"</string>
</resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index afd0abf..2cb36b6 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"අවලංගු කරන්න"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"ඉවත් කළ හැකි eSIM පෙරනිමිය ලෙස සකසන්න"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"ජංගම රේඩියෝ බලය"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"සේවයෙන් බැහැරව අනුකරණය කරන්න (නිදොස් තැනුම පමණි)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM ලිපින පොත බලන්න"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"ස්ථාවර ඇමතුම් අංක පෙන්වන්න"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"සේවා ඩයල් කිරීමේ අංක පෙන්වන්න"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"සම්බන්ධිතයි"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"අත්හිටුවා ඇත"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"නොදන්නා"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"මූලික"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"බයිට"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"යැවූ දත්ත:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"පණිවිඩය රැඳී සිටී:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"දුරකථන අංකය:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"රේඩියෝ කලාපය තෝරන්න"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"හඬ ජාල වර්ගය:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"දත්ත ජාල වර්ගය:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"ජාල වර්ගය ප්රතික්ෂේප කරන්න:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice Raw ලියාපදිංචි තත්ත්වය:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Data Raw ලියාපදිංචි තත්ත්වය:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN Data Raw ලියාපදිංචි තත්ත්වය:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"දුරකථන දර්ශකය තෝරන්න"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"වඩා කැමති ජාල වර්ගය සකසන්න:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"සත්කාරක නම පිං කරන්න(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ඔබේ බ්ලූටූත් සංඥාව දුර්වලයි. ස්පීකර් දුරකථනයට මාරු වීමට උත්සාහ කරන්න."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"ඇමතුම් ගුණත්ව දැනුම්දීම"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"අතහැර දැමූ SIP ගිණුම්"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"මෙම පැතිකඩෙන් පණිවිඩ යැවිය නොහැක"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"ඔබේ වැඩ ප්රතිපත්තිය ඔබට කාර්යාල පැතිකඩෙන් පමණක් පණිවිඩ යැවීමට ඉඩ දෙයි"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"අවලංගු කරන්න"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"කාර්යාල පැතිකඩ වෙත මාරු වන්න"</string>
</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 167d6a9..7dcc467 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Zrušiť"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Nastaviť odoberateľnú eSIM kartu ako predvolenú"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Sila signálu GSM"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulácia nefungujúceho zariadenia (možné iba v ladiacej zostave)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Zobraziť adresár SIM karty"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Zobraziť povolené čísla"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Zobraziť čísla volaní služieb"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Pripojené"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Pozastavené"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Neznáme"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primárne"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pakety"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"B"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Odoslané dáta:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Čakajúca správa:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefónne číslo:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Vybrať pásmo rádia"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Typ hlasovej siete:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Typ dátovej siete:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Prekonať typ siete:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Stav registrácie nespracovaných hlasových údajov:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Stav registrácie nespracovaných údajov:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Stav registrácie nespracovaných údajov v sieti WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Vybrať index telefónu"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Nastaviť preferovaný typ siete:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Testovať dostupnosť (ping) názvu hostiteľa (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Signál Bluetooth je slabý. Skúste prepnúť na reproduktor."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Upozornenie o kvalite hovoru"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Účty SIP s ukončenou podporou"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Z tohto profilu nemôžete odosielať správy"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Pracovné pravidlá vám umožňujú odosielať správy iba v pracovnom profile"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Zrušiť"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Prepnúť na pracovný profil"</string>
</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 99e320a..327e2b4 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Prekliči"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Nastavi izmenljivo kartico e-SIM kot privzeto"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Moč radia mobilne naprave"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulacija nedelovanja (samo za gradnjo za odpravljanje napak)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Prikaži imenik na kartici SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Prikaži številke za zaporo odhodnih klicev"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Prikaži številke za klicanje storitev"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Povezano"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Onemogočeno"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Neznano"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Glavno"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"B"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Poslani podatki:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Čakajoče sporočilo:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefonska številka:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Izbira radijskega območja"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Vrsta glasovnega omrežja:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Vrsta podatkovnega omrežja:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Preglasi vrsto omrežja:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Stanje neobdelane glasovne registracije:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Stanje registracije neobdelanih podatkov:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Stanje registracije neobdelanih podatkov WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Izberite indeks telefona"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Nastavitev vrste prednostnega omrežja:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Preverjanje imena gostitelja (www.google.com) za IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Signal povezave Bluetooth je šibek. Poskusite preklopiti na zvočnik."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Obvestilo o kakovosti klica"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Opuščeni računi SIP"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Sporočila ni mogoče poslati iz tega profila"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Službeni pravilnik dovoljuje pošiljanje sporočil le iz delovnega profila."</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Prekliči"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Preklopi na delovni profil"</string>
</resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index d83727a..75afce1 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Anulo"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Caktoje kartën e lëvizshme eSIM si të parazgjedhur"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Fuqia e radios së rrjetit celular"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulo gjendjen jashtë shërbimit (vetëm versioni i korrigjimit)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Shiko librin e adresave të kartës SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Shiko numrat me telefonim të përzgjedhur"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Shiko numrat e telefonit të shërbimit"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Lidhur"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Pezulluar"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"E panjohur"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Kryesore"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"paketa"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bajte"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Të dhënat e dërguara:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Mesazh në pritje:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Numri i telefonit:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Përzgjidh brezin e radios"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Lloji i rrjetit të zërit:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Lloji i rrjetit të të dhënave:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Anulo llojin e rrjetit:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Gjendja e regjistrimit e telefonatave të papërpunuara:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Gjendja e regjistrimit e të dhënave të papërpunuara:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Gjendja e regjistrimit e të dhënave të papërpunuara të WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Zgjidh indeksin e telefonit"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Cakto llojin e preferuar të rrjetit:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Verifiko emrin e pritësit (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Sinjali i Bluetooth-it është i dobët. Provo të kalosh te altoparlanti."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Njoftim për cilësinë e telefonatës"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Llogaritë e zhvlerësuara SIP"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Mesazhi nuk mund të dërgohet nga ky profil"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Politika jote e punës të lejon të dërgosh mesazhe vetëm nga profili i punës"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Anulo"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Kalo te profili i punës"</string>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 3d5efd1..3b754b1 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Откажи"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Подеси преносиви eSIM као подразумевани"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Напајање за радио на мобилним уређајима"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Симулација не функционише (само верзија са отклоњеним грешкама)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Прикажи адресар SIM-а"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Прикажи бројеве за фиксно бирање"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Прикажи бројеве за сервисно бирање"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Повезано"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Суспендовано"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Непознато"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Примарно"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"пак."</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"бајт(ов)а"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Послати подаци:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Порука на чекању:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Број телефона:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Изаберите радијски опсег"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Тип гласовне мреже:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Тип мреже за пренос података:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Замени тип мреже:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Стање регистрације необрађених гласовних података:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Стање регистрације необрађених података:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Стање регистрације необрађених WLAN података:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Изаберите индекс телефона"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Подесите жељени тип мреже:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"IPv4 имена хоста за пинговање (www.google.com):"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth сигнал је слаб. Пробајте да пређете на спикерфон."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Обавештење о квалитету позива"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Застарели SIP налози"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Не можете да пошаљете поруку са овог профила"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Смернице за посао вам омогућавају да шаљете поруке само са пословног профила"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Откажи"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Пређи на пословни профил"</string>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 051be92..0021e6d 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Avbryt"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Ställ in Flyttbart eSIM som standard"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Strömförsörjning för mobilradio"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Simulera ur funktion (endast felsökningsversion)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Visa SIM-adressbok"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Visa Fasta nummer"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Visa tjänstenummer"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Ansluten"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Tillfälligt avstängt"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Okänt"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primär"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"paket"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Skickad data:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Meddelande väntar:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefonnummer:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Välj radioband"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Nätverkstyp för röst:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Typ av datanätverk:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Åsidosätt nätverkstyp:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Rå registreringsstatus för röst:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Rå registreringsstatus för data:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Rå registreringsstatus för WLAN-data:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Välj telefonindex"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Ställ in önskad nätverkstyp:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Pinga värdnamn(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Svag Bluetooth-signal. Försök med att växla till högtalartelefon."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Avisering om samtalskvalitet"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Utfasade SIP-konton"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Det går inte att skicka meddelanden från den här profilen"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Jobbprincipen tillåter endast att skicka meddelanden från jobbprofilen"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Avbryt"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Byt till jobbprofilen"</string>
</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 202381f..0a73885 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Ghairi"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Weka eSIM Inayoweza Kuondolewa kama Chaguomsingi"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Nishati ya Redio ya Vifaa vya Mkononi"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Kifaa cha Kuiga Hakifanyi Kazi (Muundo wa Utatuzi pekee)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Angalia Kitabu cha Anwani katika SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Ona Nambari za Simu Zilizobainishwa"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Angalia Nambari Zilizowekwa na Mtoa Huduma"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Imeunganishwa"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Imesimamishwa"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Haijulikani"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Msingi"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"baiti"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Data Imetumwa:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Ujumbe Unasubiri Kutumwa:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Nambari ya Simu:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Chagua Bendi ya Redio"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Aina ya Mtandao wa Sauti:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Aina ya Mtandao wa Data:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Batilisha Aina ya Mtandao:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Hali ya Usajili wa Sauti Ghafi:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Hali ya Usajili wa Data Ghafi:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Hali ya Usajili wa Data Ghafi ya WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Chagua faharasa ya simu"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Weka Aina ya Mtandao Unayoiopendelea:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ita Jina la Mpangishaji(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Muunganisho wako wa bluetooth ni dhaifu. Jaribu kubadilisha ili utumie spika ya simu."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Arifa ya Ubora wa Simu"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Akaunti za SIP ambazo zimefungwa"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Huwezi kutuma ujumbe kutoka kwenye wasifu huu"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Sera ya mahali pako pa kazi inakuruhusu utume ujumbe kutoka kwenye wasifu wa kazini pekee"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Ghairi"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Tumia wasifu wa kazini"</string>
</resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 70276be..33db134 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"ரத்துசெய்"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"அகற்றக்கூடிய eSIMமை இயல்பாக அமை"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"மொபைல் ரேடியோ பவர்"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"சாதனம் \'வேலை செய்யவில்லை\' என்பதை சிமுலேட் செய்தல் (பிழைதிருத்தப் பதிப்பில் மட்டும்)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"சிம் முகவரிப் புத்தகத்தைக் காட்டு"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"நிலையான அழைப்பு எண்களைக் காட்டு"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"சேவை அழைப்பு எண்களைக் காட்டு"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"இணைக்கப்பட்டுள்ளது"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"இடைநீக்கப்பட்டது"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"தெரியாதது"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"முதன்மை"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"பைட்டுகள்"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"அனுப்பிய தரவு:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"காத்திருப்பில் உள்ள செய்தி:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ஃபோன் எண்:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"ரேடியோ பேண்டைத் தேர்ந்தெடுக்கவும்"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"குரல் நெட்வொர்க் வகை:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"டேட்டா நெட்வொர்க்கின் வகை:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"மீறிச் செயல்படும் நெட்வொர்க் வகை:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice தரவின் அசல் பதிவு நிலை:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"தரவின் அசல் பதிவு நிலை:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN தரவின் அசல் பதிவு நிலை:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ஃபோன் அட்டவணையைத் தேர்ந்தெடுக்கவும்"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"விரும்பப்படும் நெட்வொர்க் வகையை அமைக்கவும்:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"ஹோஸ்ட்பெயர்(www.google.com) IPv4ஐப் பிங் செய்:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"புளூடூத் சிக்னல் வலுவற்றதாக உள்ளது. ஸ்பீக்கர் ஃபோனிற்கு மாற்றவும்."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"அழைப்பின் தரம் தொடர்பான அறிவிப்பு"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"நிறுத்தப்பட்ட SIP கணக்குகள்"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"இந்தச் சுயவிவரத்தில் இருந்து மெசேஜ் அனுப்ப முடியாது"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"உங்கள் பணிக் கொள்கையின்படி நீங்கள் பணிக் கணக்கில் இருந்து மட்டுமே மெசேஜ் அனுப்ப முடியும்"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"ரத்துசெய்"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"பணிக் கணக்கிற்கு மாறு"</string>
</resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 8f0b582..a62c6a5 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -31,7 +31,7 @@
<string name="ussdRunning" msgid="1163586813106772717">"USSD కోడ్ అమలు చేయబడుతోంది…"</string>
<string name="mmiCancelled" msgid="5339191899200678272">"MMI కోడ్ రద్దు చేయబడింది"</string>
<string name="cancel" msgid="8984206397635155197">"రద్దు చేయండి"</string>
- <string name="enter_input" msgid="6193628663039958990">"USSD మెసేజ్ తప్పనిసరిగా <xliff:g id="MIN_LEN">%1$d</xliff:g> మరియు <xliff:g id="MAX_LEN">%2$d</xliff:g> అక్షరాల మధ్య ఉండాలి. దయచేసి మళ్లీ ట్రై చేయండి."</string>
+ <string name="enter_input" msgid="6193628663039958990">"USSD మెసేజ్ తప్పనిసరిగా <xliff:g id="MIN_LEN">%1$d</xliff:g> మరియు <xliff:g id="MAX_LEN">%2$d</xliff:g> అక్షరాల మధ్య ఉండాలి. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
<string name="manageConferenceLabel" msgid="8415044818156353233">"కాన్ఫరెన్స్ కాల్ను నిర్వహించండి"</string>
<string name="ok" msgid="7818974223666140165">"సరే"</string>
<string name="audio_mode_speaker" msgid="243689733219312360">"స్పీకర్"</string>
@@ -176,10 +176,10 @@
<string name="label_available" msgid="1316084116670821258">"అందుబాటులో ఉన్న నెట్వర్క్లు"</string>
<string name="load_networks_progress" msgid="4051433047717401683">"శోధిస్తోంది..."</string>
<string name="empty_networks_list" msgid="9216418268008582342">"నెట్వర్క్లు కనుగొనబడలేదు."</string>
- <string name="network_query_error" msgid="3862515805115145124">"నెట్వర్క్లను కనుగొనడం సాధ్యపడలేదు. మళ్లీ ట్రై చేయండి."</string>
+ <string name="network_query_error" msgid="3862515805115145124">"నెట్వర్క్లను కనుగొనడం సాధ్యపడలేదు. మళ్లీ ప్రయత్నించండి."</string>
<string name="register_on_network" msgid="4194770527833960423">"<xliff:g id="NETWORK">%s</xliff:g>లో నమోదు అవుతోంది…"</string>
<string name="not_allowed" msgid="8541221928746104798">"మీ SIM కార్డు ఈ నెట్వర్క్కు కనెక్షన్ను అనుమతించదు."</string>
- <string name="connect_later" msgid="1950138106010005425">"ప్రస్తుతం ఈ నెట్వర్క్కు కనెక్ట్ చేయడం సాధ్యపడదు. తర్వాత మళ్లీ ట్రై చేయండి."</string>
+ <string name="connect_later" msgid="1950138106010005425">"ప్రస్తుతం ఈ నెట్వర్క్కు కనెక్ట్ చేయడం సాధ్యపడదు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
<string name="registration_done" msgid="5337407023566953292">"నెట్వర్క్లో నమోదు అయింది."</string>
<string name="already_auto" msgid="8607068290733079336">"ఇప్పటికే ఆటోమేటిక్ ఎంపికలో ఉంది."</string>
<string name="select_automatically" msgid="779750291257872651">"నెట్వర్క్ను ఆటోమేటిక్గా ఎంచుకో"</string>
@@ -319,7 +319,7 @@
<string name="throttle_rate" msgid="7641913901133634905">"డేటా రేట్ విధానం"</string>
<string name="throttle_help" msgid="2624535757028809735">"మరింత తెలుసుకోండి"</string>
<string name="throttle_status_subtext" msgid="1110276415078236687">"గరిష్ఠ వ్యవధి అయిన <xliff:g id="USED_2">%3$s</xliff:g>లో <xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g>٪)\nతర్వాతి వ్యవధి <xliff:g id="USED_3">%4$d</xliff:g> రోజుల్లో (<xliff:g id="USED_4">%5$s</xliff:g>) ప్రారంభమవుతుంది"</string>
- <string name="throttle_data_usage_subtext" msgid="3185429653996709840">"గరిష్ఠంగా <xliff:g id="USED_2">%3$s</xliff:g>లో <xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g>٪)"</string>
+ <string name="throttle_data_usage_subtext" msgid="3185429653996709840">"గరిష్టంగా <xliff:g id="USED_2">%3$s</xliff:g>లో <xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g>٪)"</string>
<string name="throttle_data_rate_reduced_subtext" msgid="8369839346277847725">"<xliff:g id="USED_0">%1$s</xliff:g> గరిష్ట పరిమితి మించిపోయింది\nడేటా రేట్ <xliff:g id="USED_1">%2$d</xliff:g> Kb/sకి తగ్గించబడింది"</string>
<string name="throttle_time_frame_subtext" msgid="6462089615392402127">"సైకిల్లో <xliff:g id="USED_0">%1$d</xliff:g>٪ గడిచిపోయింది\nతదుపరి వ్యవధి <xliff:g id="USED_1">%2$d</xliff:g> రోజుల్లో (<xliff:g id="USED_2">%3$s</xliff:g>) ప్రారంభమవుతుంది"</string>
<string name="throttle_rate_subtext" msgid="7221971817325779535">"డేటా వినియోగ పరిమితి మించిపోయినప్పుడు డేటా రేట్ <xliff:g id="USED">%1$d</xliff:g> Kb/sకి తగ్గించబడుతుంది"</string>
@@ -489,8 +489,8 @@
<string name="oldPinLabel" msgid="8618515202411987721">"పాత పిన్"</string>
<string name="newPinLabel" msgid="3585899083055354732">"కొత్త పిన్"</string>
<string name="confirmPinLabel" msgid="7783531218662473778">"కొత్త PINని నిర్ధారించండి"</string>
- <string name="badPin" msgid="4549286285015892321">"మీరు టైప్ చేసిన పాత పిన్ చెల్లదు. మళ్లీ ట్రై చేయండి."</string>
- <string name="mismatchPin" msgid="1467254768290323845">"మీరు టైప్ చేసిన PINలు సరిపోలలేదు. మళ్లీ ట్రై చేయండి."</string>
+ <string name="badPin" msgid="4549286285015892321">"మీరు టైప్ చేసిన పాత పిన్ చెల్లదు. మళ్లీ ప్రయత్నించండి."</string>
+ <string name="mismatchPin" msgid="1467254768290323845">"మీరు టైప్ చేసిన PINలు సరిపోలలేదు. మళ్లీ ప్రయత్నించండి."</string>
<string name="invalidPin" msgid="7363723429414001979">"4 నుండి 8 సంఖ్యలు ఉండే PINని టైప్ చేయండి."</string>
<string name="disable_sim_pin" msgid="3112303905548613752">"SIM PINను తీసివేయండి"</string>
<string name="enable_sim_pin" msgid="445461050748318980">"SIM PINను సెట్ చేయండి"</string>
@@ -504,9 +504,9 @@
<string name="oldPin2Label" msgid="4648543187859997203">"పాత PIN2"</string>
<string name="newPin2Label" msgid="1840905981784453939">"కొత్త PIN2"</string>
<string name="confirmPin2Label" msgid="4336025914667593762">"కొత్త PIN2ని నిర్ధారించండి"</string>
- <string name="badPuk2" msgid="6438182906645832235">"PUK2 చెల్లదు. మళ్లీ ట్రై చేయండి."</string>
- <string name="badPin2" msgid="2760917538643074635">"పాత PIN2 చెల్లదు. మళ్లీ ట్రై చేయండి."</string>
- <string name="mismatchPin2" msgid="4952718725266700631">"PIN2లు సరిపోలలేదు. మళ్లీ ట్రై చేయండి."</string>
+ <string name="badPuk2" msgid="6438182906645832235">"PUK2 చెల్లదు. మళ్లీ ప్రయత్నించండి."</string>
+ <string name="badPin2" msgid="2760917538643074635">"పాత PIN2 చెల్లదు. మళ్లీ ప్రయత్నించండి."</string>
+ <string name="mismatchPin2" msgid="4952718725266700631">"PIN2లు సరిపోలలేదు. మళ్లీ ప్రయత్నించండి."</string>
<string name="invalidPin2" msgid="6467957903056379343">"4 నుండి 8 సంఖ్యలు ఉండే PIN2ని నమోదు చేయండి."</string>
<string name="invalidPuk2" msgid="713729511903849544">"8 సంఖ్యలు ఉండే PUK2ను నమోదు చేయండి."</string>
<string name="pin2_changed" msgid="5710551850481287821">"PIN2 అప్డేట్ చేయబడింది"</string>
@@ -615,7 +615,7 @@
<string name="ota_title_activate" msgid="4049645324841263423">"మీ ఫోన్ను యాక్టివేట్ చేయండి"</string>
<string name="ota_touch_activate" msgid="838764494319694754">"మీ ఫోన్ సేవను సక్రియం చేయడానికి ప్రత్యేక కాల్ చేయాల్సి ఉంటుంది. \n\n“యాక్టివేట్ చేయండి” నొక్కిన తర్వాత, మీ ఫోన్ను సక్రియం చేయడానికి అందించబడే సూచనలను వినండి."</string>
<string name="ota_hfa_activation_title" msgid="3300556778212729671">"సక్రియం చేస్తోంది..."</string>
- <string name="ota_hfa_activation_dialog_message" msgid="7921718445773342996">"ఫోన్ మీ మొబైల్ డేటా సేవను సక్రియం చేస్తోంది.\n\nదీనికి గరిష్ఠంగా 5 నిమిషాలు పట్టవచ్చు."</string>
+ <string name="ota_hfa_activation_dialog_message" msgid="7921718445773342996">"ఫోన్ మీ మొబైల్ డేటా సేవను సక్రియం చేస్తోంది.\n\nదీనికి గరిష్టంగా 5 నిమిషాలు పట్టవచ్చు."</string>
<string name="ota_skip_activation_dialog_title" msgid="7666611236789203797">"సక్రియం చేయడాన్ని దాటవేయాలా?"</string>
<string name="ota_skip_activation_dialog_message" msgid="6691722887019708713">"మీరు సక్రియం చేయడాన్ని దాటవేస్తే, కాల్స్ చేయలేరు లేదా మొబైల్ డేటా నెట్వర్క్లకు కనెక్ట్ చేయలేరు (మీరు Wi-Fi నెట్వర్క్లకు కనెక్ట్ చేయగలిగినప్పటికీ). మీరు మీ ఫోన్ను సక్రియం చేసేవరకు, దాన్ని ప్రారంభించే ప్రతిసారీ సక్రియం చేయమని మిమ్మల్ని అడుగుతుంది."</string>
<string name="ota_skip_activation_dialog_skip_label" msgid="5908029466817825633">"స్కిప్ చేయండి"</string>
@@ -626,8 +626,8 @@
<string name="ota_speaker" msgid="1086766980329820528">"స్పీకర్"</string>
<string name="ota_progress" msgid="8837259285255700132">"మీ ఫోన్ ప్రోగ్రామ్ చేయబడుతోంది…"</string>
<string name="ota_failure" msgid="5674217489921481576">"మీ ఫోన్ను ప్రోగ్రామ్ చేయడం సాధ్యపడలేదు"</string>
- <string name="ota_successful" msgid="1106825981548107774">"మీ ఫోన్ ఇప్పుడు సక్రియం అయ్యింది. సేవ ప్రారంభం కావడానికి గరిష్ఠంగా 15 నిమిషాలు పట్టవచ్చు."</string>
- <string name="ota_unsuccessful" msgid="8531037653803955754">"మీ ఫోన్ సక్రియం చేయబడలేదు. \nమీరు మెరుగైన కవరేజీ గల ప్రాంతాన్ని (కిటికీ దగ్గర లేదా వెలుపల) కనుగొనాల్సి ఉంటుంది. \n\nమళ్లీ ట్రై చేయండి లేదా మరిన్ని ఎంపికల కోసం కస్టమర్ సేవకు కాల్ చేయండి."</string>
+ <string name="ota_successful" msgid="1106825981548107774">"మీ ఫోన్ ఇప్పుడు సక్రియం అయ్యింది. సేవ ప్రారంభం కావడానికి గరిష్టంగా 15 నిమిషాలు పట్టవచ్చు."</string>
+ <string name="ota_unsuccessful" msgid="8531037653803955754">"మీ ఫోన్ సక్రియం చేయబడలేదు. \nమీరు మెరుగైన కవరేజీ గల ప్రాంతాన్ని (కిటికీ దగ్గర లేదా వెలుపల) కనుగొనాల్సి ఉంటుంది. \n\nమళ్లీ ప్రయత్నించండి లేదా మరిన్ని ఎంపికల కోసం కస్టమర్ సేవకు కాల్ చేయండి."</string>
<string name="ota_spc_failure" msgid="904092035241370080">"అత్యధిక SPC వైఫల్యాలు"</string>
<string name="ota_call_end" msgid="8657746378290737034">"వెనుకకు"</string>
<string name="ota_try_again" msgid="6914781945599998550">"మళ్లీ ప్రయత్నించు"</string>
@@ -692,7 +692,7 @@
<string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"కొత్త PINని సెట్ చేయండి"</string>
<string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"PIN తప్పనిసరిగా <xliff:g id="MIN">%1$d</xliff:g>-<xliff:g id="MAX">%2$d</xliff:g> అంకెల మధ్య ఉండాలి."</string>
<string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"మీ PINని నిర్ధారించండి"</string>
- <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"PINలు మ్యాచ్ కాలేదు"</string>
+ <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"PINలు సరిపోలలేదు"</string>
<string name="change_pin_succeeded" msgid="2504705600693014403">"వాయిస్ మెయిల్ PIN అప్డేట్ చేయబడింది"</string>
<string name="change_pin_system_error" msgid="7772788809875146873">"PINని సెట్ చేయడం సాధ్యపడలేదు"</string>
<string name="mobile_data_status_roaming_turned_off_subtext" msgid="6840673347416227054">"డేటా రోమింగ్ ఆఫ్ చేయబడింది"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"రద్దు చేయండి"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"తీసివేయగలిగే eSIMని ఆటోమేటిక్ సెట్టింగ్గా సెట్ చేయండి"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"మొబైల్ రేడియో పవర్"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"పరికరాన్ని సిమ్యులేట్ చేయడం అందుబాటులో లేదు (డీబగ్ బిల్డ్ మోడ్లో మాత్రమే)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM అడ్రస్ పుస్తకాన్ని చూడండి"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"ఫిక్స్డ్ డయలింగ్ నంబర్లను చూడండి"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"సర్వీస్ డయలింగ్ నంబర్లను చూడండి"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"కనెక్ట్ చేయబడింది"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"తాత్కాలికంగా నిలిపివేయబడింది"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"తెలియదు"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"ప్రాథమిక"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"బైట్లు"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"పంపిన డేటా:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"సందేశ నిరీక్షణ:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ఫోన్ నంబర్:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"రేడియో బ్యాండ్ను ఎంచుకోండి"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"వాయిస్ నెట్వర్క్ రకం:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"డేటా నెట్వర్క్ రకం:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"నెట్వర్క్ రకాన్ని ఓవర్రైడ్ చేయండి:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"వాయిస్ ప్రాసెస్ చేయని నమోదు స్టేటస్:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"డేటా ప్రాసెస్ చేయని నమోదు స్టేటస్:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN డేటా ప్రాసెస్ చేయని నమోదు స్టేటస్:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"ఫోన్ సూచికను ఎంచుకోండి"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"సెట్ చేసిన ప్రాధాన్య నెట్వర్క్ రకం:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"పింగ్ హోస్ట్ పేరు(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"మీ బ్లూటూత్ సిగ్నల్ బలహీనంగా ఉంది. స్పీకర్ఫోన్కు స్విచ్ అవ్వడానికి ట్రై చేయండి."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"కాల్ క్వాలిటీ నోటిఫికేషన్"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"తీసివేయబడిన SIP ఖాతాలు"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"ఈ ప్రొఫైల్ నుండి మెసేజ్ పంపడం సాధ్యపడలేదు"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"మీ వర్క్ పాలసీ మిమ్మల్ని వర్క్ ప్రొఫైల్ నుండి మాత్రమే మెసేజ్ పంపడానికి అనుమతిస్తుంది"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"రద్దు చేయండి"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"వర్క్ ప్రొఫైల్కు స్విచ్ అవ్వండి"</string>
</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 6e577e2..029c6f3 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"ยกเลิก"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"กำหนดให้ eSIM แบบนำออกได้เป็นค่าเริ่มต้น"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"กำลังส่งของวิทยุเครือข่ายมือถือ"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"จําลองความไม่พร้อมให้บริการ (บิลด์การแก้ไขข้อบกพร่องเท่านั้น)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"ดูสมุดที่อยู่ของซิม"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"ดูการจำกัดหมายเลขโทรออก"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"ดูหมายเลขรับบริการโทรออก"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"เชื่อมต่อแล้ว"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"ถูกระงับ"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"ไม่รู้จัก"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"หลัก"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"ไบต์"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"ส่งข้อมูลแล้ว:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"ข้อความที่รออยู่:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"หมายเลขโทรศัพท์:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"เลือกย่านความถี่วิทยุ"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"ประเภทของเครือข่ายเสียง:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ประเภทเครือข่ายข้อมูล:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"ประเภทการลบล้างเครือข่าย:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"สถานะการจดทะเบียนข้อมูลดิบไฟล์เสียง:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"สถานะการจดทะเบียนข้อมูลดิบ:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"สถานะการจดทะเบียนข้อมูลดิบของ WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"เลือกดัชนีโทรศัพท์"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"ตั้งค่าประเภทเครือข่ายที่ต้องการ:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"ใช้คำสั่ง ping ชื่อโฮสต์ (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"สัญญาณบลูทูธอ่อน ลองเปลี่ยนไปใช้ลำโพงแทน"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"การแจ้งเตือนคุณภาพการโทร"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"บัญชี SIP ที่เลิกใช้งาน"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"ส่งข้อความจากโปรไฟล์นี้ไม่ได้"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"นโยบายการทำงานอนุญาตให้คุณส่งข้อความได้จากโปรไฟล์งานเท่านั้น"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"ยกเลิก"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"สลับไปใช้โปรไฟล์งาน"</string>
</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index fa812ca..37b3f44 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Kanselahin"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Itakda na Default ang Naaalis na eSIM"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobile Radio Power"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Mag-simulate ng Hindi Gumagana (Build sa Pag-debug lang)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Tingnan ang Address Book ng SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Tingnan ang Mga Fixed Dialing Number"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Tingnan ang Mga Service Dialing Number"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Konektado"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Sinuspinde"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Hindi alam"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Pangunahin"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bytes"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Ipinadalang Data:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Naghihintay na Mensahe:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Numero ng Telepono:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Pumili ng Band ng Radyo"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Uri ng Voice Network:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Uri ng Data Network:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"I-override ang Network Type:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Status ng Pagpaparehistro ng Voice Raw:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Status ng Pagpaparehistro ng Data Raw:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Status ng Pagpaparehistro ng WLAN Data Raw:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Pumili ng index ng telepono"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Itakda ang Uri ng Gustong Network:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"I-ping ang Hostname(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Mahina ang signal ng iyong bluetooth. Subukang lumipat sa speakerphone."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Notification sa Kalidad ng Tawag"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Mga hindi na ginagamit na SIP account"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Hindi makakapagpadala ng mensahe mula sa profile na ito"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Pinapayagan ka ng iyong patakaran sa trabaho na magpadala ng mensahe mula lang sa profile sa trabaho"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Kanselahin"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Lumipat sa profile sa trabaho"</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index af9687b..6916fde 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"İptal"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Çıkarılabilir eSIM\'i Varsayılan Yap"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Mobil Radyo Gücü"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Hizmet Dışı Simülasyonu (Yalnızca Hata Ayıklama Derlemesi)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM Adres Defterini Görüntüle"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Sabit Arama Numaralarını Görüntüle"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Hizmet Arama Numaralarını Görüntüle"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Bağlı"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Askıya alındı"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Bilinmiyor"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Birincil"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkt"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bayt"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Veri Gönderildi:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Mesaj Bekleniyor:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefon Numarası:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Radyo Bandını Seç"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Sesli Ağ Türü:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Veri Ağı Türü:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Ağ Türünü Geçersiz Kıl:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Ses Ham Kayıt Durumu:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Veri Ham Kayıt Durumu:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN Verileri Ham Kayıt Durumu:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Telefon dizinini seç"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Tercih Edilen Ağ Türünü Ayarla:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Ping Ana Makine Adı (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth sinyaliniz zayıf. Hoparlöre geçmeyi deneyin."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Arama Kalitesiyle İlgili Bildirim"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Kullanımdan kaldırılan SIP hesapları"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Bu profilden mesaj gönderilemiyor"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"İşletme politikanız yalnızca iş profilinden mesaj göndermenize izin veriyor"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"İptal"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"İş profiline geç"</string>
</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 65f59bd..ea49e93 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Скасувати"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Установити знімну eSIM-карту як карту за умовчанням"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Потужність мобільного радіо"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Імітація знаходження поза зоною обслуговування (лише складання для налагодження)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Переглянути адресну книгу SIM-карти"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Переглянути фіксовані номери"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Переглянути службові номери"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Підключено"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Заблоковано"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Невідомо"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Основний"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"пак."</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"байт"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"дБм"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Надіслані дані:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Очікування на повідомлення:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Номер телефону:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Виберіть радіодіапазон"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Тип мережі голосових дзвінків:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Тип мережі передавання даних:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Перевизначити тип мережі:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Статус реєстрації необробленого голосу:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Статус реєстрації необроблених даних:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Статус реєстрації необроблених даних WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Виберіть телефонний код"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Установити потрібний тип мережі:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Надіслати запит ping на ім\'я хосту (www.google.com) через IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Сигнал Bluetooth заслабкий. Спробуйте переключитися на гучний зв\'язок."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Сповіщення про якість виклику"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Облікові записи SIP, що не підтримуються"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Надіслати повідомлення з цього профілю не можна"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Відповідно до правил організації ви можете надсилати повідомлення лише з робочого профілю"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Скасувати"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Перейти в робочий профіль"</string>
</resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 344a9ec..ebac5d0 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="phoneAppLabel" product="tablet" msgid="1916019789885839910">"موبائل ڈیٹا"</string>
<string name="phoneAppLabel" product="default" msgid="130465039375347763">"فون سروسز"</string>
- <string name="emergencyDialerIconLabel" msgid="8668005772339436680">"ایمرجنسی ڈائلر"</string>
+ <string name="emergencyDialerIconLabel" msgid="8668005772339436680">"ہنگامی ڈائلر"</string>
<string name="phoneIconLabel" msgid="3015941229249651419">"فون"</string>
<string name="fdnListLabel" msgid="4119121875004244097">"FDN کی فہرست"</string>
<string name="unknown" msgid="8279698889921830815">"نامعلوم"</string>
@@ -84,7 +84,7 @@
<string name="smart_forwarding_settings_menu" msgid="8850429887958938540">"اسمارٹ فارورڈنگ"</string>
<string name="smart_forwarding_settings_menu_summary" msgid="5096947726032885325">"ایک نمبر کے قابل رسائی نہ ہونے کی صورت میں کالز کو ہمیشہ دوسرے نمبر پر فارورڈ کریں"</string>
<string name="voicemail_notifications_preference_title" msgid="7829238858063382977">"اطلاعات"</string>
- <string name="cell_broadcast_settings" msgid="8135324242541809924">"ایمرجنسی براڈکاسٹس"</string>
+ <string name="cell_broadcast_settings" msgid="8135324242541809924">"ہنگامی براڈکاسٹس"</string>
<string name="call_settings" msgid="3677282690157603818">"کال کی ترتیبات"</string>
<string name="additional_gsm_call_settings" msgid="1561980168685658846">"اضافی ترتیبات"</string>
<string name="additional_gsm_call_settings_with_label" msgid="7973920539979524908">"اضافی ترتیبات (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -329,9 +329,9 @@
<string name="cell_bc_sms_enable" msgid="2019708772024632073">"سیل نشریہ SMS فعال ہوگیا"</string>
<string name="cell_bc_sms_disable" msgid="1214238639910875347">"سیل نشریہ SMS غیر فعال ہوگیا"</string>
<string name="cb_sms_settings" msgid="6858093721831312036">"سیل نشریہ SMS کی ترتیبات"</string>
- <string name="enable_disable_emergency_broadcast" msgid="6325655044472196496">"ایمرجنسی نشریہ"</string>
- <string name="emergency_broadcast_enable" msgid="5759610647771102442">"ایمرجنسی نشریہ فعال ہوگیا"</string>
- <string name="emergency_broadcast_disable" msgid="2844904734469323266">"ایمرجنسی نشریہ غیر فعال ہوگیا"</string>
+ <string name="enable_disable_emergency_broadcast" msgid="6325655044472196496">"ہنگامی نشریہ"</string>
+ <string name="emergency_broadcast_enable" msgid="5759610647771102442">"ہنگامی نشریہ فعال ہوگیا"</string>
+ <string name="emergency_broadcast_disable" msgid="2844904734469323266">"ہنگامی نشریہ غیر فعال ہوگیا"</string>
<string name="enable_disable_administrative" msgid="7825925366822117961">"انتظامی"</string>
<string name="administrative_enable" msgid="5717963431079532028">"انتظامی فعال ہوگیا"</string>
<string name="administrative_disable" msgid="156796633660118691">"انتظامی غیر فعال ہوگیا"</string>
@@ -537,8 +537,8 @@
<string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"کال کرنے کیلئے موبائل نیٹ ورک آن کریں، ہوائی جہاز موڈ یا بیٹری سیور موڈ آف کریں۔"</string>
<string name="incall_error_power_off" product="default" msgid="8131672264311208673">"کال کرنے کیلئے ہوائی جہاز وضع آف کریں۔"</string>
<string name="incall_error_power_off_wfc" msgid="9125661184694727052">"کال کرنے کیلئے ہوائی جہاز وضع آف کریں یا کسی وائرلیس نیٹ ورک سے منسلک ہوں۔"</string>
- <string name="incall_error_power_off_thermal" product="default" msgid="8695809601655300168"><b>"فون بہت گرم ہے"</b>\n\n"اس کال کو مکمل نہیں کیا جا سکتا۔ جب آپ کا فون ٹھنڈا ہو جائے تو دوبارہ کوشش کریں۔\n\nآپ اب بھی ایمرجنسی کالز کر سکتے ہیں۔"</string>
- <string name="incall_error_ecm_emergency_only" msgid="5622379058883722080">"غیر ایمرجنسی کال کرنے کیلئے ایمرجنسی کال بیک موڈ سے اخراج کریں۔"</string>
+ <string name="incall_error_power_off_thermal" product="default" msgid="8695809601655300168"><b>"فون بہت گرم ہے"</b>\n\n"اس کال کو مکمل نہیں کیا جا سکتا۔ جب آپ کا فون ٹھنڈا ہو جائے تو دوبارہ کوشش کریں۔\n\nآپ اب بھی ہنگامی کالز کر سکتے ہیں۔"</string>
+ <string name="incall_error_ecm_emergency_only" msgid="5622379058883722080">"غیر ہنگامی کال کرنے کیلئے ہنگامی کال بیک موڈ سے اخراج کریں۔"</string>
<string name="incall_error_emergency_only" msgid="8786127461027964653">"نیٹ ورک پر رجسٹرڈ نہیں ہے۔"</string>
<string name="incall_error_out_of_service" msgid="1927265196942672791">"موبائل نیٹ ورک دستیاب نہیں ہے۔"</string>
<string name="incall_error_out_of_service_wfc" msgid="4497663185857190885">"موبائل نیٹ ورک دستیاب نہیں ہے۔ کال کرنے کیلئے کسی وائرلیس نیٹ ورک سے منسلک ہوں۔"</string>
@@ -556,19 +556,19 @@
<string name="incall_error_supp_service_hold" msgid="8535056414643540997">"کالز کو ہولڈ نہیں کیا جا سکتا۔"</string>
<string name="incall_error_wfc_only_no_wireless_network" msgid="5860742792811400109">"کال کرنے کیلئے کسی وائرلیس نیٹ ورک سے منسلک ہوں۔"</string>
<string name="incall_error_promote_wfc" msgid="9164896813931363415">"کال کرنے کیلئے Wi-Fi کالنگ فعال کریں۔"</string>
- <string name="emergency_information_hint" msgid="9208897544917793012">"ایمرجنسی معلومات"</string>
+ <string name="emergency_information_hint" msgid="9208897544917793012">"ہنگامی معلومات"</string>
<string name="emergency_information_owner_hint" msgid="6256909888049185316">"مالک"</string>
<string name="emergency_information_confirm_hint" msgid="5109017615894918914">"معلومات دیکھنے کیلئے دوبارہ تھپتھپائیں"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="2667568200755388829">"ایمرجنسی کال"</string>
+ <string name="emergency_enable_radio_dialog_title" msgid="2667568200755388829">"ہنگامی کال"</string>
<string name="single_emergency_number_title" msgid="8413371079579067196">"ایمرجنسی نمبر"</string>
<string name="numerous_emergency_numbers_title" msgid="8972398932506755510">"ایمرجنسی نمبرز"</string>
<string name="emergency_call_shortcut_hint" msgid="1290485125107779500">"<xliff:g id="EMERGENCY_NUMBER">%s</xliff:g> کو کال کرنے کے لئے دوبارہ تھپتھپائيں"</string>
<string name="emergency_enable_radio_dialog_message" msgid="1695305158151408629">"ریڈیو آن کر رہا ہے…"</string>
<string name="emergency_enable_radio_dialog_retry" msgid="4329131876852608587">"کوئی سروس نہیں ہے۔ دوبارہ کوشش کی جا رہی ہے…"</string>
- <string name="radio_off_during_emergency_call" msgid="8011154134040481609">"ایمرجنسی کال کے دوران ہوائی جہاز موڈ میں داخل نہیں ہو سکتا۔"</string>
- <string name="dial_emergency_error" msgid="825822413209026039">"کال نہیں کی جا سکتی۔ <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ایک ایمرجنسی نمبر نہیں ہے۔"</string>
- <string name="dial_emergency_empty_error" msgid="2785803395047793634">"کال نہیں کی جا سکتی۔ ایک ایمرجنسی نمبر ڈائل کریں۔"</string>
- <string name="dial_emergency_calling_not_available" msgid="6485846193794727823">"ایمرجنسی کالنگ دستیاب نہیں ہے"</string>
+ <string name="radio_off_during_emergency_call" msgid="8011154134040481609">"ہنگامی کال کے دوران ہوائی جہاز موڈ میں داخل نہیں ہو سکتا۔"</string>
+ <string name="dial_emergency_error" msgid="825822413209026039">"کال نہیں کی جا سکتی۔ <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ایک ہنگامی نمبر نہیں ہے۔"</string>
+ <string name="dial_emergency_empty_error" msgid="2785803395047793634">"کال نہیں کی جا سکتی۔ ایک ہنگامی نمبر ڈائل کریں۔"</string>
+ <string name="dial_emergency_calling_not_available" msgid="6485846193794727823">"ہنگامی کالنگ دستیاب نہیں ہے"</string>
<string name="pin_puk_system_user_only" msgid="1045147220686867922">"صرف آلہ کا مالک ہی PIN/PUK کوڈ درج کرسکتا ہے۔"</string>
<string name="police_type_description" msgid="2819533883972081757">"پولیس"</string>
<string name="ambulance_type_description" msgid="6798237503553180461">"ایمبولینس"</string>
@@ -633,18 +633,18 @@
<string name="ota_try_again" msgid="6914781945599998550">"دوبارہ کوشش کریں"</string>
<string name="ota_next" msgid="2041016619313475914">"اگلا"</string>
<string name="ecm_exit_dialog" msgid="4200691880721429078">"EcmExitDialog"</string>
- <string name="phone_entered_ecm_text" msgid="8431238297843035842">"ایمرجنسی کال بیک طرز میں داخل ہو گیا"</string>
- <string name="phone_in_ecm_notification_title" msgid="6825016389926367946">"ایمرجنسی کال بیک طرز"</string>
+ <string name="phone_entered_ecm_text" msgid="8431238297843035842">"ہنگامی کال بیک طرز میں داخل ہو گیا"</string>
+ <string name="phone_in_ecm_notification_title" msgid="6825016389926367946">"ہنگامی کال بیک طرز"</string>
<string name="phone_in_ecm_call_notification_text" msgid="653972232922670335">"ڈیٹا کنکشن غیر فعال ہوگیا"</string>
<string name="phone_in_ecm_notification_complete_time" msgid="7341624337163082759">"<xliff:g id="COMPLETETIME">%s</xliff:g> تک کوئی ڈیٹا کنکشن نہیں"</string>
<string name="alert_dialog_exit_ecm" msgid="7661603870224398025">"{count,plural, =1{فون ایک منٹ تک ایمرجنسی کال بیک وضع میں رہے گا۔ اس وضع میں رہتے ہوئے ڈیٹا کنکشن کا استعمال کرنے والی کوئی بھی ایپ استعمال نہیں ہو سکتی۔ کیا آپ ابھی باہر نکلنا چاہتے ہیں؟}other{فون %s منٹ تک ایمرجنسی کال بیک وضع میں رہے گا۔ اس وضع میں رہتے ہوئے ڈیٹا کنکشن کا استعمال کرنے والی کوئی بھی ایپلیکیشن استعمال نہیں ہو سکتی۔ کیا آپ ابھی باہر نکلنا چاہتے ہیں؟}}"</string>
<string name="alert_dialog_not_avaialble_in_ecm" msgid="8717711120099503279">"{count,plural, =1{منتخب کردہ کارروائی ایمرجنسی کال بیک وضع میں دستیاب نہیں ہے۔ فون ایک منٹ تک اس وضع میں رہے گا۔ کیا آپ ابھی نکلنا چاہتے ہیں؟}other{منتخب کردہ کارروائی ایمرجنسی کال بیک وضع میں دستیاب نہیں ہے۔ فون %s منٹ تک اس وضع میں رہے گا۔ کیا آپ ابھی نکلنا چاہتے ہیں؟}}"</string>
- <string name="alert_dialog_in_ecm_call" msgid="1207545603149771978">"منتخب کردہ کارروائی ایک ایمرجنسی کال میں رہتے ہوئے دستیاب نہیں ہے۔"</string>
- <string name="progress_dialog_exiting_ecm" msgid="9159080081676927217">"موجودہ ایمرجنسی کال بیک طرز"</string>
+ <string name="alert_dialog_in_ecm_call" msgid="1207545603149771978">"منتخب کردہ کارروائی ایک ہنگامی کال میں رہتے ہوئے دستیاب نہیں ہے۔"</string>
+ <string name="progress_dialog_exiting_ecm" msgid="9159080081676927217">"موجودہ ہنگامی کال بیک طرز"</string>
<string name="alert_dialog_yes" msgid="3532525979632841417">"ہاں"</string>
<string name="alert_dialog_no" msgid="1075632654085988420">"نہیں"</string>
<string name="alert_dialog_dismiss" msgid="1336356286354517054">"کالعدم کریں"</string>
- <string name="phone_in_ecm_call_notification_text_without_data_restriction_hint" msgid="3747860785153531225">"فون ایمرجنسی کال بیک وضع میں ہے"</string>
+ <string name="phone_in_ecm_call_notification_text_without_data_restriction_hint" msgid="3747860785153531225">"فون ہنگامی کال بیک وضع میں ہے"</string>
<string name="phone_in_ecm_notification_complete_time_without_data_restriction_hint" msgid="3690292264812050858">"<xliff:g id="COMPLETETIME">%s</xliff:g> تک"</string>
<string name="alert_dialog_exit_ecm_without_data_restriction_hint" msgid="7549850847524907932">"{count,plural, =1{فون ایک منٹ تک ایمرجنسی کال بیک وضع میں رہے گا۔\nکیا آپ ابھی باہر نکلنا چاہتے ہیں؟}other{فون %s منٹ تک ایمرجنسی کال بیک وضع میں رہے گا۔\nکیا آپ ابھی باہر نکلنا چاہتے ہیں؟}}"</string>
<string name="voicemail_provider" msgid="4158806657253745294">"سروس"</string>
@@ -668,8 +668,8 @@
<string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"ویڈیو کالنگ کو آن کرنے کیلئے، آپ کیلئے نیٹ ورک ترتیبات میں Enhanced 4G LTE وضع فعال کرنا ضروری ہے۔"</string>
<string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"نیٹ ورک کی ترتیبات"</string>
<string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"بند کریں"</string>
- <string name="sim_label_emergency_calls" msgid="9078241989421522310">"ایمرجنسی کالز"</string>
- <string name="sim_description_emergency_calls" msgid="5146872803938897296">"صرف ایمرجنسی کالنگ"</string>
+ <string name="sim_label_emergency_calls" msgid="9078241989421522310">"ہنگامی کالز"</string>
+ <string name="sim_description_emergency_calls" msgid="5146872803938897296">"صرف ہنگامی کالنگ"</string>
<string name="sim_description_default" msgid="7474671114363724971">"SIM کارڈ، سلاٹ: <xliff:g id="SLOT_ID">%s</xliff:g>"</string>
<string name="accessibility_settings_activity_title" msgid="7883415189273700298">"ایکسیسبیلٹی"</string>
<string name="status_hint_label_incoming_wifi_call" msgid="2606052595898044071">"Wi-Fi کال منجانب"</string>
@@ -681,7 +681,7 @@
<string name="callFailed_wifi_lost" msgid="1788036730589163141">"Wi-Fi کنکشن کھو گیا۔ کال ختم ہو گئی۔"</string>
<string name="dialFailed_low_battery" msgid="6857904237423407056">"کم بیٹری کی وجہ سے آپ کی ویڈیو کال نہیں کی جا سکتی۔"</string>
<string name="callFailed_low_battery" msgid="4056828320214416182">"کم بیٹری کی وجہ سے ویڈیو کال ختم ہو گئی۔"</string>
- <string name="callFailed_emergency_call_over_wfc_not_available" msgid="5944309590693432042">"اس مقام میں Wi-Fi کالنگ پر ایمرجنسی کالز دستیاب نہیں ہیں۔"</string>
+ <string name="callFailed_emergency_call_over_wfc_not_available" msgid="5944309590693432042">"اس مقام میں Wi-Fi کالنگ پر ہنگامی کالز دستیاب نہیں ہیں۔"</string>
<string name="callFailed_wfc_service_not_available_in_this_location" msgid="3624536608369524988">"اس مقام میں Wi-Fi کالنگ دستیاب نہیں ہے۔"</string>
<string name="change_pin_title" msgid="3564254326626797321">"صوتی میل PIN تبدیل کریں"</string>
<string name="change_pin_continue_label" msgid="5177011752453506371">"جاری رکھیں"</string>
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"منسوخ کریں"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"ہٹانے لائق eSIM کو بطور ڈیفالٹ سیٹ کریں"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"موبائل ریڈیو پاور"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"\'سروس دستیاب نہیں ہے\' موڈ کو سمیولیٹ کریں (صرف ڈیبگ بلڈ کیلئے)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM ایڈریس بک دیکھیں"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"فکسڈ ڈائلنگ نمبرز دیکھیں"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"سروس ڈائلنگ نمبرز دیکھیں"</string>
@@ -849,7 +850,7 @@
<string name="radio_info_ims_reg_status" msgid="25582845222446390">"IMS رجسٹریشن: <xliff:g id="STATUS">%1$s</xliff:g>\nوائس اوور LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nوائس اوور WiFi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nویڈیو کالنگ: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nUT انٹرفیس: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
<string name="radioInfo_service_in" msgid="45753418231446400">"سروس میں"</string>
<string name="radioInfo_service_out" msgid="287972405416142312">"سروس میں نہیں ہے"</string>
- <string name="radioInfo_service_emergency" msgid="4763879891415016848">"صرف ایمرجنسی کالیں"</string>
+ <string name="radioInfo_service_emergency" msgid="4763879891415016848">"صرف ہنگامی کالیں"</string>
<string name="radioInfo_service_off" msgid="3456583511226783064">"ریڈیو آف کریں"</string>
<string name="radioInfo_roaming_in" msgid="3156335577793145965">"رومنگ"</string>
<string name="radioInfo_roaming_not" msgid="1904547918725478110">"رومنگ میں نہيں"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"منسلک"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"معطل کر دیا گیا"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"نامعلوم"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"بنیادی"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"پیکٹس"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"بائٹس"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"ڈیٹا بھیج دیا گیا:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"پیغام منتظر ہے:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"فون نمبر:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"ریڈیو بینڈ منتخب کریں"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"صوتی نیٹ ورک کی قسم:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"ڈیٹا نیٹ ورک کی قسم:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"نیٹ ورک کی قسم کو اوور رائیڈ کریں:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"آواز کی خام رجسٹریشن کی حیثیت:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"ڈیٹا کی خام رجسٹریشن کی حیثیت:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN ڈیٹا کی خام رجسٹریشن کی حیثیت:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"فون انڈیکس منتخب کریں"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"ترجیحی نیٹ ورک کی قسم سیٹ کریں:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"میزبان کا نام پنگ کریں(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"آپ کا بلوٹوتھ سگنل کمزور ہے۔ اسپیکر فون پر سوئچ کر کے آزمائیں۔"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"کال کی کوالٹی کی اطلاع"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"فرسودہ SIP اکاؤنٹس"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"اس پروفائل سے پیغام نہیں بھیج سکتے"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"آپ کے کام سے متعلق پالیسی آپ کو صرف دفتری پروفائل سے پیغام بھیجنے کی اجازت دیتی ہے"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"منسوخ کریں"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"دفتری پروفائل پر سوئچ کریں"</string>
</resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 98f663d..dea3dd8 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Bekor qilish"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Olinadigan eSIM kartani birlamchi qilib belgilash"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Radio signal quvvati"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Xizmatdan tashqari simulyatsiya (faqat nosozliklarni aniqlash dasturi uchun)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"SIM kartadagi abonentlar ro‘yxatini ochish"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Ruxsat etilgan raqamlar ro‘yxatini ochish"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Xizmatlarni terish raqamlarini ochish"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Ulangan"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Bloklangan"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Noma’lum"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Asosiy"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"bayt"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Yuborilgan trafik:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Parallel xabar:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Telefon raqami:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Radiostansiyani tanlash"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Ovozli aloqa uchun tarmoq turi:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Trafik uchun tarmoq turi:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Tarmoq turini almashtirish:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice dastlabki roʻyxatdan oʻtkazish holati:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Maʼlumotlarni dastlabki roʻyxatdan oʻtkazish holati:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN maʼlumotlarini dastlabki roʻyxatdan oʻtkazish holati:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Telefon kodini tanlang"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Afzal tarmoq turini tanlash:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"www.google.com uchun ping, IPv4 so‘rovining host nomi:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth signali kuchsiz. Baland ovoz rejimini yoqish tavsiya etiladi."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Chaqiruv sifati haqida bildirishnoma"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"To‘xtatilgan SIP hisoblar"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Bu profildan xabar yuborish imkonsiz"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Ishga oid siyosatingiz faqat ish profilidan xabar yuborish imkonini beradi"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Bekor qilish"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Ish profiliga almashish"</string>
</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 3030947..f74bbf4 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Hủy"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Đặt eSIM có thể tháo rời là Mặc định"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Cường độ của sóng di động"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Mô phỏng thiết bị không hoạt động (chỉ dành cho bản gỡ lỗi)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Xem sổ địa chỉ trên SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Xem số gọi định sẵn"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Xem số quay số dịch vụ"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Đã kết nối"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Bị tạm ngưng"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Không xác định"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Chính"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"byte"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Đã gửi dữ liệu:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Tin nhắn đang đợi:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Số điện thoại:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Chọn dải tần số"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Loại mạng thoại:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Loại mạng dữ liệu:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Ghi đè loại mạng:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Trạng thái đăng ký thô của Voice:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Trạng thái đăng ký thô dữ liệu:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Trạng thái đăng ký thô dữ liệu WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Chọn chỉ mục điện thoại"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Đặt loại mạng ưu tiên:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Tên máy chủ Ping (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Tín hiệu Bluetooth của bạn đang yếu. Hãy thử chuyển sang loa ngoài."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Thông báo về chất lượng cuộc gọi"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Tài khoản SIP không dùng nữa"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Không gửi được tin nhắn từ hồ sơ này"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Chính sách của nơi làm việc chỉ cho phép bạn gửi tin nhắn từ hồ sơ công việc"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Huỷ"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Chuyển sang hồ sơ công việc"</string>
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index a335b21..ed1825e 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"取消"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"将可卸载的 eSIM 卡设为默认 eSIM 卡"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"移动无线装置电源"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"模拟服务终止(仅限调试 build)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"查看 SIM 卡通讯录"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"查看固定拨号号码"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"查看服务拨号号码"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"已连接"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"已暂停"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"未知"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"主要"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"字节"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"已发送的数据量:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"消息等待:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"电话号码:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"选择无线装置频道"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"语音网络类型:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"数据网络类型:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"覆盖网络类型:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice 原始注册状态:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"数据原始注册状态:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN 数据原始注册状态:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"选择电话号码索引"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"设置首选网络类型:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"ping 主机名(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"您的蓝牙信号较弱。请尝试切换为扬声器模式。"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"通话质量通知"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"已弃用的 SIP 帐号"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"无法通过此资料发送消息"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"根据您的工作政策,您只能通过工作资料发送消息"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"取消"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"切换到工作资料"</string>
</resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 6b41bd9..2ddbf1d 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -48,7 +48,7 @@
<string name="no_vm_number_msg" msgid="5165161462411372504">"SIM 卡中沒有儲存任何留言信箱號碼。"</string>
<string name="add_vm_number_str" msgid="7368168964435881637">"新增電話號碼"</string>
<string name="voice_number_setting_primary_user_only" msgid="3394706575741912843">"只有主要使用者可以修改留言設定。"</string>
- <string name="puk_unlocked" msgid="4627340655215746511">"你的 SIM 卡已解除封鎖。你的手機正在解除鎖定..."</string>
+ <string name="puk_unlocked" msgid="4627340655215746511">"您的 SIM 卡已解除封鎖。您的手機正在解除鎖定..."</string>
<string name="label_ndp" msgid="7617392683877410341">"SIM 網絡解除鎖定 PIN"</string>
<string name="label_phoneid" msgid="8775611434123577808">"流動網絡供應商的 SIM 卡已上鎖"</string>
<string name="sim_ndp_unlock_text" msgid="7737338355451978338">"解除鎖定"</string>
@@ -113,20 +113,20 @@
<string name="messageCFB" msgid="1958017270393563388">"忙線號碼"</string>
<string name="sum_cfb_enabled" msgid="332037613072049492">"轉接至 <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfb_disabled" msgid="3589913334164866035">"關閉"</string>
- <string name="disable_cfb_forbidden" msgid="4831494744351633961">"你的流動網絡供應商不支援在手機通話時停用轉接功能。"</string>
+ <string name="disable_cfb_forbidden" msgid="4831494744351633961">"您的流動網絡供應商不支援在手機通話時停用轉接功能。"</string>
<string name="labelCFNRy" msgid="3403533792248457946">"無人接聽時"</string>
<string name="messageCFNRy" msgid="7644434155765359009">"未接聽時撥號"</string>
<string name="sum_cfnry_enabled" msgid="3000500837493854799">"轉接至 <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfnry_disabled" msgid="1990563512406017880">"關閉"</string>
- <string name="disable_cfnry_forbidden" msgid="3174731413216550689">"你的流動網絡供應商不支援在手機未接聽時停用轉接功能。"</string>
+ <string name="disable_cfnry_forbidden" msgid="3174731413216550689">"您的流動網絡供應商不支援在手機未接聽時停用轉接功能。"</string>
<string name="labelCFNRc" msgid="4163399350778066013">"無法接通時"</string>
<string name="messageCFNRc" msgid="6980340731313007250">"無法接通時的轉接號碼"</string>
<string name="sum_cfnrc_enabled" msgid="1799069234006073477">"轉接至 <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfnrc_disabled" msgid="739289696796917683">"關閉"</string>
- <string name="disable_cfnrc_forbidden" msgid="775348748084726890">"你的流動網絡供應商不支援在手機無法接通時停用轉接功能。"</string>
- <string name="registration_cf_forbidden" msgid="4386482610771190420">"你的流動網絡供應商不支援來電轉駁。"</string>
+ <string name="disable_cfnrc_forbidden" msgid="775348748084726890">"您的流動網絡供應商不支援在手機無法接通時停用轉接功能。"</string>
+ <string name="registration_cf_forbidden" msgid="4386482610771190420">"您的流動網絡供應商不支援來電轉駁。"</string>
<string name="cdma_call_waiting" msgid="4565070960879673216">"要開啟來電等候功能嗎?"</string>
- <string name="enable_cdma_call_waiting_setting" msgid="5906811747921744307">"你會在通話期間收到來電通知"</string>
+ <string name="enable_cdma_call_waiting_setting" msgid="5906811747921744307">"您會在通話期間收到來電通知"</string>
<string name="enable_cdma_cw" msgid="811047045863422232">"開啟"</string>
<string name="disable_cdma_cw" msgid="7119290446496301734">"取消"</string>
<string name="cdma_call_waiting_in_ims_on" msgid="6390979414188659218">"IMS 下的 CDMA 來電等候功能已開啟"</string>
@@ -144,7 +144,7 @@
<string name="stk_cc_ss_to_ussd_error" msgid="8330749347425752192">"SS 要求已變更為 USSD 要求"</string>
<string name="stk_cc_ss_to_ss_error" msgid="8297155544652134278">"已變更為新的 SS 要求"</string>
<string name="stk_cc_ss_to_dial_video_error" msgid="4255261231466032505">"SS 要求已變更為視像通話"</string>
- <string name="fdn_check_failure" msgid="1833769746374185247">"你的「手機」應用程式已開啟固定撥號設定,因此部分撥號相關功能無法正常運作。"</string>
+ <string name="fdn_check_failure" msgid="1833769746374185247">"您的「手機」應用程式已開啟固定撥號設定,因此部分撥號相關功能無法正常運作。"</string>
<string name="radio_off_error" msgid="8321564164914232181">"查看這些設定前,請先開啟無線電。"</string>
<string name="close_dialog" msgid="1074977476136119408">"確定"</string>
<string name="enable" msgid="2636552299455477603">"開啟"</string>
@@ -158,10 +158,10 @@
<string name="vm_changed" msgid="4739599044379692505">"留言信箱號碼已更改。"</string>
<string name="vm_change_failed" msgid="7877733929455763566">"無法更改留言信箱號碼。\n如果問題持續發生,請與流動網絡供應商聯絡。"</string>
<string name="fw_change_failed" msgid="9179241823460192148">"無法更改轉接號碼。\n如果問題持續發生,請與流動網絡供應商聯絡。"</string>
- <string name="fw_get_in_vm_failed" msgid="2432678237218183844">"無法擷取和儲存目前的轉接號碼設定。\n你仍要轉到新的供應商嗎?"</string>
+ <string name="fw_get_in_vm_failed" msgid="2432678237218183844">"無法擷取和儲存目前的轉接號碼設定。\n您仍要轉到新的供應商嗎?"</string>
<string name="no_change" msgid="3737264882821031892">"沒有更改。"</string>
<string name="sum_voicemail_choose_provider" msgid="6750824719081403773">"選擇留言信箱服務"</string>
- <string name="voicemail_default" msgid="6427575113775462077">"你的流動網絡供應商"</string>
+ <string name="voicemail_default" msgid="6427575113775462077">"您的流動網絡供應商"</string>
<string name="vm_change_pin_old_pin" msgid="7154951790929009241">"舊的 PIN"</string>
<string name="vm_change_pin_new_pin" msgid="2656200418481288069">"新的 PIN"</string>
<string name="vm_change_pin_progress_message" msgid="626015184502739044">"請稍候。"</string>
@@ -178,7 +178,7 @@
<string name="empty_networks_list" msgid="9216418268008582342">"找不到網絡。"</string>
<string name="network_query_error" msgid="3862515805115145124">"找不到網絡,請再試一次。"</string>
<string name="register_on_network" msgid="4194770527833960423">"正在註冊 <xliff:g id="NETWORK">%s</xliff:g>..."</string>
- <string name="not_allowed" msgid="8541221928746104798">"你的 SIM 卡無法連接這個網絡。"</string>
+ <string name="not_allowed" msgid="8541221928746104798">"您的 SIM 卡無法連接這個網絡。"</string>
<string name="connect_later" msgid="1950138106010005425">"目前無法連接這個網絡,請稍後再試。"</string>
<string name="registration_done" msgid="5337407023566953292">"已在網絡上完成註冊。"</string>
<string name="already_auto" msgid="8607068290733079336">"已選取自動選取功能。"</string>
@@ -288,14 +288,14 @@
<string name="roaming_enabled_message" msgid="9022249120750897">"可能需要支付漫遊費用。輕按即可修改。"</string>
<string name="roaming_notification_title" msgid="3590348480688047320">"流動數據連線中斷"</string>
<string name="roaming_on_notification_title" msgid="7451473196411559173">"數據漫遊已開啟"</string>
- <string name="roaming_warning" msgid="7855681468067171971">"你可能需要支付龐大的費用。"</string>
- <string name="roaming_check_price_warning" msgid="8212484083990570215">"請聯絡你的網絡供應商查詢定價。"</string>
+ <string name="roaming_warning" msgid="7855681468067171971">"您可能需要支付龐大的費用。"</string>
+ <string name="roaming_check_price_warning" msgid="8212484083990570215">"請聯絡您的網絡供應商查詢定價。"</string>
<string name="roaming_alert_title" msgid="5689615818220960940">"要允許數據漫遊嗎?"</string>
<string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM 卡功能受限"</string>
<string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"「<xliff:g id="CARRIER_NAME">%1$s</xliff:g>」通話和數據服務在使用 <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> 卡時可能遭到封鎖。"</string>
<string name="limited_sim_function_notification_message" msgid="5338638075496721160">"「<xliff:g id="CARRIER_NAME">%1$s</xliff:g>」通話和數據服務在使用其他 SIM 卡時可能遭到封鎖。"</string>
<string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"已找到並移除已停用的 SIP 帳戶"</string>
- <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android 平台不再支援 SIP 通話。\n你現有的 SIP 帳戶 (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) 已移除。\n請確認你的預設通話帳戶設定。"</string>
+ <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android 平台不再支援 SIP 通話。\n您現有的 SIP 帳戶 (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) 已移除。\n請確認您的預設通話帳戶設定。"</string>
<string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"前往設定"</string>
<string name="data_usage_title" msgid="8438592133893837464">"應用程式數據用量"</string>
<string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_2">%2$s</xliff:g>期間使用了 <xliff:g id="ID_1">%1$s</xliff:g>流動數據"</string>
@@ -475,7 +475,7 @@
<string name="delete_fdn_contact" msgid="7027405651994507077">"刪除固定撥號"</string>
<string name="deleting_fdn_contact" msgid="6872320570844460428">"正在刪除固定撥號…"</string>
<string name="fdn_contact_deleted" msgid="1680714996763848838">"固定撥號已刪除。"</string>
- <string name="pin2_invalid" msgid="2313954262684494442">"你所輸入的 PIN 碼不正確,FDN 未更新。"</string>
+ <string name="pin2_invalid" msgid="2313954262684494442">"您所輸入的 PIN 碼不正確,FDN 未更新。"</string>
<string name="fdn_invalid_number" msgid="9067189814657840439">"號碼超過 <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> 位數,FDN 未更新。"</string>
<string name="pin2_or_fdn_invalid" msgid="7542639487955868181">"FDN 無法更新。PIN2 碼不正確或電話號碼被拒。"</string>
<string name="fdn_failed" msgid="216592346853420250">"FDN 操作失敗。"</string>
@@ -489,8 +489,8 @@
<string name="oldPinLabel" msgid="8618515202411987721">"舊 PIN"</string>
<string name="newPinLabel" msgid="3585899083055354732">"新的 PIN"</string>
<string name="confirmPinLabel" msgid="7783531218662473778">"確認新 PIN"</string>
- <string name="badPin" msgid="4549286285015892321">"你所輸入的舊 PIN 碼不正確,請再試一次。"</string>
- <string name="mismatchPin" msgid="1467254768290323845">"你所輸入的 PIN 碼不符,請再試一次。"</string>
+ <string name="badPin" msgid="4549286285015892321">"您所輸入的舊 PIN 碼不正確,請再試一次。"</string>
+ <string name="mismatchPin" msgid="1467254768290323845">"您所輸入的 PIN 碼不符,請再試一次。"</string>
<string name="invalidPin" msgid="7363723429414001979">"請輸入一個 4 至 8 位數的 PIN。"</string>
<string name="disable_sim_pin" msgid="3112303905548613752">"清除 SIM PIN"</string>
<string name="enable_sim_pin" msgid="445461050748318980">"設定 SIM PIN"</string>
@@ -514,7 +514,7 @@
<string name="fdn_enable_puk2_requested" msgid="5793652792131588041">"密碼不正確。PIN2 現在已被封鎖。如要再試一次,請變更 PIN 2。"</string>
<string name="puk2_requested" msgid="6992374450720307514">"密碼不正確。SIM 現在已被封鎖,請輸入 PUK2。"</string>
<string name="puk2_blocked" msgid="3131139031779319911">"PUK2 碼已被永久封鎖。"</string>
- <string name="pin2_attempts" msgid="5625178102026453023">\n"你還有 <xliff:g id="NUMBER">%d</xliff:g> 次嘗試機會。"</string>
+ <string name="pin2_attempts" msgid="5625178102026453023">\n"您還有 <xliff:g id="NUMBER">%d</xliff:g> 次嘗試機會。"</string>
<string name="puk2_locked" msgid="6497760825455461057">"PUK2 已鎖定。請通知服務供應商解鎖。"</string>
<string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 目前沒有封鎖"</string>
<string name="pin2_error_exception" msgid="8116103864600823641">"網絡或 SIM 卡錯誤。"</string>
@@ -533,18 +533,18 @@
<string name="notification_voicemail_text_format" msgid="5720947141702312537">"撥號 <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
<string name="notification_voicemail_no_vm_number" msgid="3423686009815186750">"沒有可用的留言信箱號碼"</string>
<string name="notification_network_selection_title" msgid="255595526707809121">"沒有服務"</string>
- <string name="notification_network_selection_text" msgid="553288408722427659">"你所選取的網絡 (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) 無法使用"</string>
+ <string name="notification_network_selection_text" msgid="553288408722427659">"您所選取的網絡 (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) 無法使用"</string>
<string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"開啟流動網絡、關閉飛行模式或關閉慳電模式,以撥打電話。"</string>
<string name="incall_error_power_off" product="default" msgid="8131672264311208673">"關閉飛行模式以撥打電話。"</string>
<string name="incall_error_power_off_wfc" msgid="9125661184694727052">"關閉飛行模式或連接無線網絡,以撥打電話。"</string>
- <string name="incall_error_power_off_thermal" product="default" msgid="8695809601655300168"><b>"電話過熱"</b>\n\n"無法完成此通話。請在電話冷卻後再試一次。\n\n你仍可進行緊急電話。"</string>
+ <string name="incall_error_power_off_thermal" product="default" msgid="8695809601655300168"><b>"電話過熱"</b>\n\n"無法完成此通話。請在電話冷卻後再試一次。\n\n您仍可進行緊急電話。"</string>
<string name="incall_error_ecm_emergency_only" msgid="5622379058883722080">"離開緊急回撥模式即可撥打非緊急電話。"</string>
<string name="incall_error_emergency_only" msgid="8786127461027964653">"未在網絡上完成註冊。"</string>
<string name="incall_error_out_of_service" msgid="1927265196942672791">"無法使用流動網絡。"</string>
<string name="incall_error_out_of_service_wfc" msgid="4497663185857190885">"無法使用流動網絡。請連接無線網絡,以撥打電話。"</string>
<string name="incall_error_no_phone_number_supplied" msgid="8680831089508851894">"要撥打電話,請輸入有效的號碼。"</string>
<string name="incall_error_call_failed" msgid="393508653582682539">"無法接通。"</string>
- <string name="incall_error_cannot_add_call" msgid="5425764862628655443">"目前無法新增通話。你可以改以傳送短訊聯絡對方。"</string>
+ <string name="incall_error_cannot_add_call" msgid="5425764862628655443">"目前無法新增通話。您可以改以傳送短訊聯絡對方。"</string>
<string name="incall_error_supp_service_unknown" msgid="8751177117194592623">"不支援的服務"</string>
<string name="incall_error_supp_service_switch" msgid="5272822448189448479">"無法切換通話。"</string>
<string name="incall_error_supp_service_resume" msgid="1276861499306817035">"無法恢復通話。"</string>
@@ -612,22 +612,22 @@
</string-array>
<string name="network_info_message" msgid="7599413947016532355">"網絡訊息"</string>
<string name="network_error_message" msgid="4271579424089326618">"錯誤訊息"</string>
- <string name="ota_title_activate" msgid="4049645324841263423">"啟用你的手機"</string>
- <string name="ota_touch_activate" msgid="838764494319694754">"如要啟用手機服務,你必須撥打一個特別電話。\n\n按「啟用」 後,請聽取手機啟用說明。"</string>
+ <string name="ota_title_activate" msgid="4049645324841263423">"啟用您的手機"</string>
+ <string name="ota_touch_activate" msgid="838764494319694754">"如要啟用手機服務,您必須撥打一個特別電話。\n\n按「啟用」 後,請聽取手機啟用說明。"</string>
<string name="ota_hfa_activation_title" msgid="3300556778212729671">"正在啟用…"</string>
<string name="ota_hfa_activation_dialog_message" msgid="7921718445773342996">"手機正在啟用流動數據服務。\n\n這個程序最多需時 5 分鐘。"</string>
<string name="ota_skip_activation_dialog_title" msgid="7666611236789203797">"是否略過啟用?"</string>
- <string name="ota_skip_activation_dialog_message" msgid="6691722887019708713">"如果略過啟用程序,那麼你就無法撥打電話或是連上行動數據網絡 (雖然可連上 Wi-Fi 網絡)。除非你已啟用手機,否則每次開機時,系統都會要求你啟用手機。"</string>
+ <string name="ota_skip_activation_dialog_message" msgid="6691722887019708713">"如果略過啟用程序,那麼您就無法撥打電話或是連上行動數據網絡 (雖然可連上 Wi-Fi 網絡)。除非您已啟用手機,否則每次開機時,系統都會要求您啟用手機。"</string>
<string name="ota_skip_activation_dialog_skip_label" msgid="5908029466817825633">"略過"</string>
<string name="ota_activate" msgid="7939695753665438357">"啟用"</string>
<string name="ota_title_activate_success" msgid="1272135024761004889">"手機已啟用。"</string>
<string name="ota_title_problem_with_activation" msgid="7019745985413368726">"啟用時發生問題"</string>
- <string name="ota_listen" msgid="2772252405488894280">"依照語音指示進行,直到你聽到啟用完成為止。"</string>
+ <string name="ota_listen" msgid="2772252405488894280">"依照語音指示進行,直到您聽到啟用完成為止。"</string>
<string name="ota_speaker" msgid="1086766980329820528">"揚聲器"</string>
- <string name="ota_progress" msgid="8837259285255700132">"正在為你的手機編程…"</string>
- <string name="ota_failure" msgid="5674217489921481576">"無法為你的手機編程"</string>
- <string name="ota_successful" msgid="1106825981548107774">"你的手機已啟用,大概需要 15 分鐘才能開始提供服務。"</string>
- <string name="ota_unsuccessful" msgid="8531037653803955754">"你的手機未啟用。\n建議你移到接收較佳的地方 (例如窗邊或戶外)。\n\n請再試一次,或致電客戶服務中心查詢其他啟用方式。"</string>
+ <string name="ota_progress" msgid="8837259285255700132">"正在為您的手機編程…"</string>
+ <string name="ota_failure" msgid="5674217489921481576">"無法為您的手機編程"</string>
+ <string name="ota_successful" msgid="1106825981548107774">"您的手機已啟用,大概需要 15 分鐘才能開始提供服務。"</string>
+ <string name="ota_unsuccessful" msgid="8531037653803955754">"您的手機未啟用。\n建議您移到接收較佳的地方 (例如窗邊或戶外)。\n\n請再試一次,或致電客戶服務中心查詢其他啟用方式。"</string>
<string name="ota_spc_failure" msgid="904092035241370080">"SPC 錯誤過多"</string>
<string name="ota_call_end" msgid="8657746378290737034">"上一頁"</string>
<string name="ota_try_again" msgid="6914781945599998550">"重試"</string>
@@ -637,16 +637,16 @@
<string name="phone_in_ecm_notification_title" msgid="6825016389926367946">"緊急回撥模式"</string>
<string name="phone_in_ecm_call_notification_text" msgid="653972232922670335">"數據連線已停用"</string>
<string name="phone_in_ecm_notification_complete_time" msgid="7341624337163082759">"到<xliff:g id="COMPLETETIME">%s</xliff:g> 以前沒有數據連線"</string>
- <string name="alert_dialog_exit_ecm" msgid="7661603870224398025">"{count,plural, =1{手機在接下來的 1 分鐘都將處於緊急回撥模式。在這個模式中,所有使用數據連線的應用程式皆無法使用。你要立即退出嗎?}other{手機在接下來的 %s 分鐘都將處於緊急回撥模式。在這個模式中,所有使用數據連線的應用程式皆無法使用。你要立即退出嗎?}}"</string>
- <string name="alert_dialog_not_avaialble_in_ecm" msgid="8717711120099503279">"{count,plural, =1{緊急回撥模式不支援你所選取的操作。手機在接下來的 1 分鐘都將處於這個模式,你要立即退出嗎?}other{緊急回撥模式不支援你所選取的操作。手機在接下來的 %s 分鐘都將處於這個模式,你要立即退出嗎?}}"</string>
- <string name="alert_dialog_in_ecm_call" msgid="1207545603149771978">"緊急電話模式不支援你所選取的操作。"</string>
+ <string name="alert_dialog_exit_ecm" msgid="7661603870224398025">"{count,plural, =1{手機在接下來的 1 分鐘都將處於緊急回撥模式。在這個模式中,所有使用數據連線的應用程式皆無法使用。您要立即退出嗎?}other{手機在接下來的 %s 分鐘都將處於緊急回撥模式。在這個模式中,所有使用數據連線的應用程式皆無法使用。您要立即退出嗎?}}"</string>
+ <string name="alert_dialog_not_avaialble_in_ecm" msgid="8717711120099503279">"{count,plural, =1{緊急回撥模式不支援您所選取的操作。手機在接下來的 1 分鐘都將處於這個模式,您要立即退出嗎?}other{緊急回撥模式不支援您所選取的操作。手機在接下來的 %s 分鐘都將處於這個模式,您要立即退出嗎?}}"</string>
+ <string name="alert_dialog_in_ecm_call" msgid="1207545603149771978">"緊急電話模式不支援您所選取的操作。"</string>
<string name="progress_dialog_exiting_ecm" msgid="9159080081676927217">"正在退出緊急回撥模式"</string>
<string name="alert_dialog_yes" msgid="3532525979632841417">"是"</string>
<string name="alert_dialog_no" msgid="1075632654085988420">"否"</string>
<string name="alert_dialog_dismiss" msgid="1336356286354517054">"關閉"</string>
<string name="phone_in_ecm_call_notification_text_without_data_restriction_hint" msgid="3747860785153531225">"手機處於緊急回撥模式"</string>
<string name="phone_in_ecm_notification_complete_time_without_data_restriction_hint" msgid="3690292264812050858">"直至<xliff:g id="COMPLETETIME">%s</xliff:g>"</string>
- <string name="alert_dialog_exit_ecm_without_data_restriction_hint" msgid="7549850847524907932">"{count,plural, =1{手機在接下來的 1 分鐘都將處於緊急回撥模式。\n你要立即退出嗎?}other{手機在接下來的 %s 分鐘都將處於緊急回撥模式。\n你要立即退出嗎?}}"</string>
+ <string name="alert_dialog_exit_ecm_without_data_restriction_hint" msgid="7549850847524907932">"{count,plural, =1{手機在接下來的 1 分鐘都將處於緊急回撥模式。\n您要立即退出嗎?}other{手機在接下來的 %s 分鐘都將處於緊急回撥模式。\n您要立即退出嗎?}}"</string>
<string name="voicemail_provider" msgid="4158806657253745294">"服務"</string>
<string name="voicemail_settings" msgid="4451045613238972776">"設定"</string>
<string name="voicemail_number_not_set" msgid="8831561283386938155">"<未設定>"</string>
@@ -665,7 +665,7 @@
<string name="preference_category_ringtone" msgid="8787281191375434976">"鈴聲與震動"</string>
<string name="pstn_connection_service_label" msgid="9200102709997537069">"內置 SIM 卡"</string>
<string name="enable_video_calling_title" msgid="7246600931634161830">"開啟視像通話"</string>
- <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"如要開啟視像通話,你需要在網絡設定中啟用強化 4G LTE 模式。"</string>
+ <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"如要開啟視像通話,您需要在網絡設定中啟用強化 4G LTE 模式。"</string>
<string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"網絡設定"</string>
<string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"關閉"</string>
<string name="sim_label_emergency_calls" msgid="9078241989421522310">"緊急通話"</string>
@@ -675,11 +675,11 @@
<string name="status_hint_label_incoming_wifi_call" msgid="2606052595898044071">"Wi-Fi 通話來自"</string>
<string name="status_hint_label_wifi_call" msgid="942993035689809853">"Wi-Fi 通話"</string>
<string name="message_decode_error" msgid="1061856591500290887">"為訊息解碼時發生錯誤。"</string>
- <string name="callFailed_cdma_activation" msgid="5392057031552253550">"SIM 卡已啟動你的服務,並更新了你的手機漫遊功能。"</string>
+ <string name="callFailed_cdma_activation" msgid="5392057031552253550">"SIM 卡已啟動您的服務,並更新了您的手機漫遊功能。"</string>
<string name="callFailed_cdma_call_limit" msgid="1074219746093031412">"進行中的通話過多。請先結束或合併現有通話,再重新撥打。"</string>
<string name="callFailed_imei_not_accepted" msgid="7257903653685147251">"無法連線,請插入有效的 SIM 卡。"</string>
<string name="callFailed_wifi_lost" msgid="1788036730589163141">"已失去 Wi-Fi 連線。通話已結束。"</string>
- <string name="dialFailed_low_battery" msgid="6857904237423407056">"電量過低,因此你無法撥打視像通話。"</string>
+ <string name="dialFailed_low_battery" msgid="6857904237423407056">"電量過低,因此您無法撥打視像通話。"</string>
<string name="callFailed_low_battery" msgid="4056828320214416182">"電量過低,因此視像通話已結束。"</string>
<string name="callFailed_emergency_call_over_wfc_not_available" msgid="5944309590693432042">"這個地區無法透過 Wi-Fi 撥打緊急電話。"</string>
<string name="callFailed_wfc_service_not_available_in_this_location" msgid="3624536608369524988">"這個地區不支援 Wi-Fi 通話。"</string>
@@ -704,16 +704,16 @@
<string name="mobile_data_activate_prepaid_summary" msgid="6846085278531605925">"透過「<xliff:g id="PROVIDER_NAME">%s</xliff:g>」增加流動數據"</string>
<string name="mobile_data_activate_roaming_plan" msgid="922290995866269366">"沒有漫遊計劃"</string>
<string name="mobile_data_activate_roaming_plan_summary" msgid="5379228493306235969">"透過「<xliff:g id="PROVIDER_NAME">%s</xliff:g>」增加漫遊計劃"</string>
- <string name="mobile_data_activate_footer" msgid="7895874069807204548">"你可以透過你的流動網絡供應商「<xliff:g id="PROVIDER_NAME">%s</xliff:g>」增加流動數據或漫遊計劃。"</string>
+ <string name="mobile_data_activate_footer" msgid="7895874069807204548">"您可以透過您的流動網絡供應商「<xliff:g id="PROVIDER_NAME">%s</xliff:g>」增加流動數據或漫遊計劃。"</string>
<string name="mobile_data_activate_diag_title" msgid="5401741936224757312">"要增加數據嗎?"</string>
- <string name="mobile_data_activate_diag_message" msgid="3527260988020415441">"你可以透過「<xliff:g id="PROVIDER_NAME">%s</xliff:g>」增加數據"</string>
+ <string name="mobile_data_activate_diag_message" msgid="3527260988020415441">"您可以透過「<xliff:g id="PROVIDER_NAME">%s</xliff:g>」增加數據"</string>
<string name="mobile_data_activate_button" msgid="1139792516354374612">"增加數據"</string>
<string name="mobile_data_activate_cancel_button" msgid="3530174817572005860">"取消"</string>
<string name="clh_card_title_call_ended_txt" msgid="5977978317527299698">"通話已結束"</string>
<string name="clh_callFailed_powerOff_txt" msgid="8279934912560765361">"已開啟飛行模式"</string>
<string name="clh_callFailed_simError_txt" msgid="5128538525762326413">"無法存取 SIM 卡"</string>
<string name="clh_incall_error_out_of_service_txt" msgid="2736010617446749869">"無法使用流動網絡"</string>
- <string name="clh_callFailed_unassigned_number_txt" msgid="141967660286695682">"你嘗試撥打的電話號碼有問題。錯誤碼 1。"</string>
+ <string name="clh_callFailed_unassigned_number_txt" msgid="141967660286695682">"您嘗試撥打的電話號碼有問題。錯誤碼 1。"</string>
<string name="clh_callFailed_no_route_to_destination_txt" msgid="4805015149822352308">"無法完成通話。錯誤碼 3。"</string>
<string name="clh_callFailed_channel_unacceptable_txt" msgid="4062754579408613021">"無法完成通話。錯誤碼 6。"</string>
<string name="clh_callFailed_operator_determined_barring_txt" msgid="4202077821465974286">"無法完成通話。錯誤碼 8。"</string>
@@ -795,7 +795,7 @@
<string name="messageCallBarring" msgid="5537730400652466912">"輸入密碼"</string>
<string name="call_barring_settings" msgid="4616607285790258919">"通話限制設定"</string>
<string name="callFailed_NetworkBusy" msgid="5437103975842913681">"網絡忙碌中。請稍後再撥。"</string>
- <string name="callFailed_NetworkCongested" msgid="6801283142342775380">"網絡壅塞。請通知你的流動網絡供應商提供協助。"</string>
+ <string name="callFailed_NetworkCongested" msgid="6801283142342775380">"網絡壅塞。請通知您的流動網絡供應商提供協助。"</string>
<string name="supp_service_notification_call_deflected" msgid="4980942818105909813">"通話已轉向。"</string>
<string name="supp_service_notification_call_forwarded" msgid="7102930311735433088">"通話已轉駁。"</string>
<string name="supp_service_notification_call_waiting" msgid="4577403881609445324">"通話尚待接聽。"</string>
@@ -818,10 +818,10 @@
<string name="callFailed_already_ringing" msgid="2376603543544289303">"由於目前有尚未接聽的來電,因此無法撥打電話。請先接聽或拒絕來電,然後再撥打電話。"</string>
<string name="callFailed_calling_disabled" msgid="5010992739401206283">"致電功能已由 ro.telephony.disable-call 系統屬性停用,因此無法打電話。"</string>
<string name="callFailed_too_many_calls" msgid="2761754044990799580">"目前已有兩個通話正在進行,因此無法撥打電話。請先結束其中一個通話,或將兩個通話合併為一個會議,才撥打電話。"</string>
- <string name="supp_service_over_ut_precautions" msgid="2145018231396701311">"如要使用<xliff:g id="SUPP_SERVICE">%s</xliff:g>,請確保流動數據已開啟。你可以在流動網絡設定中變更此設定。"</string>
- <string name="supp_service_over_ut_precautions_roaming" msgid="670342104569972327">"如要使用<xliff:g id="SUPP_SERVICE">%s</xliff:g>,請確保流動數據和數據漫遊已開啟。你可以在流動網絡設定中變更此設定。"</string>
- <string name="supp_service_over_ut_precautions_dual_sim" msgid="5166866975550910474">"如要使用<xliff:g id="SUPP_SERVICE">%1$s</xliff:g>,請確保 SIM 卡 (<xliff:g id="SIM_NUMBER">%2$d</xliff:g>) 的流動數據已開啟。你可以在流動網絡設定中變更此設定。"</string>
- <string name="supp_service_over_ut_precautions_roaming_dual_sim" msgid="6627654855191817965">"如要使用<xliff:g id="SUPP_SERVICE">%1$s</xliff:g>,請確保 SIM 卡 <xliff:g id="SIM_NUMBER">%2$d</xliff:g> 的流動數據和數據漫遊已開啟。你可以在流動網絡設定中變更此設定。"</string>
+ <string name="supp_service_over_ut_precautions" msgid="2145018231396701311">"如要使用<xliff:g id="SUPP_SERVICE">%s</xliff:g>,請確保流動數據已開啟。您可以在流動網絡設定中變更此設定。"</string>
+ <string name="supp_service_over_ut_precautions_roaming" msgid="670342104569972327">"如要使用<xliff:g id="SUPP_SERVICE">%s</xliff:g>,請確保流動數據和數據漫遊已開啟。您可以在流動網絡設定中變更此設定。"</string>
+ <string name="supp_service_over_ut_precautions_dual_sim" msgid="5166866975550910474">"如要使用<xliff:g id="SUPP_SERVICE">%1$s</xliff:g>,請確保 SIM 卡 (<xliff:g id="SIM_NUMBER">%2$d</xliff:g>) 的流動數據已開啟。您可以在流動網絡設定中變更此設定。"</string>
+ <string name="supp_service_over_ut_precautions_roaming_dual_sim" msgid="6627654855191817965">"如要使用<xliff:g id="SUPP_SERVICE">%1$s</xliff:g>,請確保 SIM 卡 <xliff:g id="SIM_NUMBER">%2$d</xliff:g> 的流動數據和數據漫遊已開啟。您可以在流動網絡設定中變更此設定。"</string>
<string name="supp_service_over_ut_precautions_dialog_dismiss" msgid="5934541487903081652">"關閉"</string>
<string name="radio_info_data_connection_enable" msgid="6183729739783252840">"啟用數據連線"</string>
<string name="radio_info_data_connection_disable" msgid="6404751291511368706">"停用數據連線"</string>
@@ -832,11 +832,12 @@
<string name="cbrs_data_switch_string" msgid="6060356430838077653">"CBRS 數據"</string>
<string name="dsds_switch_string" msgid="7564769822086764796">"啟用 DSDS"</string>
<string name="dsds_dialog_title" msgid="8494569893941847575">"要重新啟動裝置嗎?"</string>
- <string name="dsds_dialog_message" msgid="4047480385678538850">"你需要重新啟動裝置,才可變更此設定。"</string>
+ <string name="dsds_dialog_message" msgid="4047480385678538850">"您需要重新啟動裝置,才可變更此設定。"</string>
<string name="dsds_dialog_confirm" msgid="9032004888134129885">"重新啟動"</string>
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"取消"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"將可移除的 eSIM 卡設為預設值"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"流動無線電的電源"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"模擬沒有服務 (僅限偵錯版本)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"查看 SIM 卡通訊錄"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"查看固定撥號"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"查看服務撥號"</string>
@@ -849,7 +850,7 @@
<string name="radio_info_ims_reg_status" msgid="25582845222446390">"IMS 註冊:<xliff:g id="STATUS">%1$s</xliff:g>\n語音 LTE:<xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\n語音 Wi-Fi:<xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\n視像通話:<xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nUT 介面:<xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
<string name="radioInfo_service_in" msgid="45753418231446400">"服務中"</string>
<string name="radioInfo_service_out" msgid="287972405416142312">"沒有服務"</string>
- <string name="radioInfo_service_emergency" msgid="4763879891415016848">"只可致電緊急電話"</string>
+ <string name="radioInfo_service_emergency" msgid="4763879891415016848">"只可撥打緊急電話"</string>
<string name="radioInfo_service_off" msgid="3456583511226783064">"收音機已關閉"</string>
<string name="radioInfo_roaming_in" msgid="3156335577793145965">"漫遊"</string>
<string name="radioInfo_roaming_not" msgid="1904547918725478110">"沒有使用漫遊"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"已連線"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"已停用"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"不明"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"主要"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkt"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"位元組"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"已傳送的資料:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"正在等待訊息:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"電話號碼:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"選擇無線電波段"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"語音網絡類型:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"數據網絡類型:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"覆寫網絡類型:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"語音原始註冊狀態:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"資料原始註冊狀態:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN 資料原始註冊狀態:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"選取手機索引"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"設定偏好的網絡類型:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"連線偵測主機名稱 (www.google.com) IPv4:"</string>
@@ -916,7 +920,11 @@
<string name="phone_info_label" product="default" msgid="1784175881556791433">"手機資訊"</string>
<string name="carrier_provisioning" msgid="2668065041869578376">"流動網絡供應商佈建資料"</string>
<string name="trigger_carrier_provisioning" msgid="1301829588620638234">"觸發流動網絡供應商佈建程序"</string>
- <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"你的藍牙訊號微弱。請改用擴音器。"</string>
+ <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"您的藍牙訊號微弱。請改用擴音器。"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"通話品質通知"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"已停用的 SIP 帳戶"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"無法從此個人檔案傳送訊息"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"您的工作政策只允許透過工作設定檔傳送訊息"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"取消"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"切換至工作設定檔"</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index b59d162..8e378c4 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"取消"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"將可移除的 eSIM 卡設為預設 eSIM 卡"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"行動無線電電源"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"模擬無法使用服務的情況 (僅限偵錯版本)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"查看 SIM 通訊錄"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"查看固定撥號"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"查看服務撥號號碼"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"已連線"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"待命"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"不明"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"主要"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"位元組"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"已傳送的數據:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"留言待取:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"電話號碼:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"選取無線電頻帶"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"語音網路類型:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"數據網路類型:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"覆寫網路類型:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Voice 原始註冊狀態:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"資料原始註冊狀態:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"WLAN 資料原始註冊狀態:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"選取電話索引"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"設定偏好的網路類型:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"連線偵測 (ping) 主機名稱 (www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"你的藍牙訊號微弱,建議你改用擴音模式。"</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"通話品質通知"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"不適用的 SIP 帳戶"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"無法透過這個資料夾傳送訊息"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"貴公司政策僅允許透過工作資料夾傳送訊息"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"取消"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"切換至工作資料夾"</string>
</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 3133e27..578e183 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -837,6 +837,7 @@
<string name="dsds_dialog_cancel" msgid="3245958947099586655">"Khansela"</string>
<string name="removable_esim_string" msgid="7931369811671787649">"Setha i-eSim Esusekayo Njengezenzakalelayo"</string>
<string name="radio_info_radio_power" msgid="8805595022160471587">"Amandla erediyo yeselula"</string>
+ <string name="simulate_out_of_service_string" msgid="7787925611727597193">"Lingisa okuthi Ayikho Isevisi (Umakhiwo Wokususa Iphutha kuphela)"</string>
<string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Buka incwadi yekheli le-SIM"</string>
<string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Buka Izinombolo Zokudayela Okungaguquki"</string>
<string name="radioInfo_menu_viewSDN" msgid="2613431584522392842">"Buka Izinombolo Zokudayela Isevisi"</string>
@@ -861,6 +862,7 @@
<string name="radioInfo_data_connected" msgid="7637335645634239508">"Ixhunyiwe"</string>
<string name="radioInfo_data_suspended" msgid="8695262782642002785">"Kumiswe okwesikhashana"</string>
<string name="radioInfo_unknown" msgid="5401423738500672850">"Akwaziwa"</string>
+ <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Okuyinhloko"</string>
<string name="radioInfo_display_packets" msgid="6794302192441084157">"amaphakethe"</string>
<string name="radioInfo_display_bytes" msgid="7701006329222413797">"amabhaythi"</string>
<string name="radioInfo_display_dbm" msgid="7491944975710865703">"dBm"</string>
@@ -887,10 +889,12 @@
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Idatha ithunyelwe:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Umlayezo ulindile:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"Inombolo yefoni:"</string>
- <string name="radio_info_band_mode_label" msgid="23480556225515290">"Khetha ibhendi yerediyo"</string>
<string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Uhlobo lwenethiwekhi yezwi:"</string>
<string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Uhlobo lwenethiwekhi yedatha:"</string>
<string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Khipha Uhlobo Lwenethiwekhi:"</string>
+ <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Isimo Sokubhaliswa Kwezwi:"</string>
+ <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Isimo Sokubhaliswa Kwedatha:"</string>
+ <string name="radio_info_wlan_data_raw_registration_state_label" msgid="6396894835757296612">"Isimo Sokubhaliswa Kwedatha Ye-WLAN:"</string>
<string name="phone_index_label" msgid="6222406512768964268">"Khetha inkomba yefoni"</string>
<string name="radio_info_set_perferred_label" msgid="7408131389363136210">"Setha uhlobo lwenethiwekhi olufisekayo:"</string>
<string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"Qhweba i-Hostname(www.google.com) IPv4:"</string>
@@ -919,4 +923,8 @@
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Isignali yakho ye-bluetooth ayiqinile. Zama ukushintshela kusipikhasefoni."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Isaziso Sekhwalithi"</string>
<string name="notification_channel_sip_account" msgid="1261816025156179637">"Ama-akhawunti we-SIP ehlisiwe"</string>
+ <string name="send_from_work_profile_title" msgid="9201528838448432473">"Ayikwazi ukuthumela umyalezo kusukela kule phrofayela"</string>
+ <string name="send_from_work_profile_description" msgid="5002701841936861636">"Inqubomgomo yakho yomsebenzi ikuvumela ukuthi uthumele umyalezo kuphela usuka kuphrofayela yomsebenzi"</string>
+ <string name="send_from_work_profile_cancel" msgid="177746511030381711">"Khansela"</string>
+ <string name="send_from_work_profile_action_str" msgid="6892775562934243337">"Shintshela kuphrofayela yomsebenzi"</string>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 19252c0..ba65302 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -317,4 +317,8 @@
<!-- The package names which can request thermal mitigation. -->
<string-array name="thermal_mitigation_allowlisted_packages" translatable="false">
</string-array>
+
+ <!-- Flag specifying whether the AOSP domain selection is enabled or
+ the device should fallback to the modem based domain selection architecture. -->
+ <bool name="config_enable_aosp_domain_selection">false</bool>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 38a86f9..b32b030 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -289,6 +289,8 @@
<string name="updating_title">Call settings</string>
<!-- Toast in Call settings when asked to launch settings for a secondary user -->
<string name="call_settings_admin_user_only">Call settings can only be changed by the admin user.</string>
+ <!-- Toast in Phone Account settings when asked to launch settings for a secondary/guest user -->
+ <string name="phone_account_settings_user_restriction">Phone account settings can only be changed by the admin or work user.</string>
<!-- Title of the "Call settings" settings screen, with a text label identifying which SIM the settings are for. -->
<string name="call_settings_with_label">Settings (<xliff:g id="subscriptionlabel" example="Mock Carrier">%s</xliff:g>)</string>
<!-- Title of the alert dialog displayed if an error occurs while updating Call settings -->
@@ -1237,6 +1239,8 @@
<string name="incall_error_wfc_only_no_wireless_network">Connect to a wireless network to make a call.</string>
<!-- In-call screen: call failure message displayed in an error dialog when the user is connected to a wireless network, but wifi calling is turned off. [CHAR_LIMIT=NONE] -->
<string name="incall_error_promote_wfc">Enable Wi-Fi calling to make a call.</string>
+ <!-- In-call screen: call failure message displayed in an error dialog when the satellite modem is on. [CHAR_LIMIT=NONE] -->
+ <string name="incall_error_satellite_enabled">Disable satellite mode to make a call.</string>
<!-- Hint for the button of emergency information -->
<string name="emergency_information_hint">Emergency information</string>
@@ -1709,6 +1713,8 @@
<string name="clh_callFailed_simError_txt">Can\'t access SIM card</string>
<!-- In-call screen: call failure message displayed in an error dialog -->
<string name="clh_incall_error_out_of_service_txt">Mobile network not available</string>
+ <!-- In-call screen: call failure reason (satellite modem is enabled) -->
+ <string name="clh_callFailed_satelliteEnabled_txt">Satellite mode is on</string>
<!-- See CallFailCause for details on what causes each message -->
<!-- In-call screen: call failure reason (Cause Number 1) -->
@@ -2010,6 +2016,9 @@
<!-- Title for controlling on/off for Mobile phone's radio power. Only shown in diagnostic screen, so precise translation is not needed. -->
<string name="radio_info_radio_power">Mobile Radio Power</string>
+ <!-- Title for simulating device out of service. -->
+ <string name="simulate_out_of_service_string">Simulate Out of Service (Debug Build only)</string>
+
<!-- Phone Info screen. Menu item label. Used for diagnostic info screens, precise translation isn't needed -->
<string name="radioInfo_menu_viewADN">View SIM Address Book</string>
<!-- Phone Info screen. Menu item label. Used for diagnostic info screens, precise translation isn't needed -->
@@ -2067,6 +2076,8 @@
<!-- Used for diagnostic info screens, precise translation isn't needed -->
<string name="radioInfo_unknown">Unknown</string>
+ <!-- Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_imei_primary">Primary</string>
<!-- Phone Info screen. Units shown after a value. Used for diagnostic info screens, precise translation isn't needed -->
<string name="radioInfo_display_packets">pkts</string>
<!-- Phone Info screen. Units shown after a value. Used for diagnostic info screens, precise translation isn't needed -->
@@ -2124,14 +2135,18 @@
<!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="radio_info_phone_number_label">Phone Number:</string>
<!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
- <string name="radio_info_band_mode_label">Select Radio Band</string>
- <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="radio_info_voice_network_type_label">Voice Network Type:</string>
<!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="radio_info_data_network_type_label">Data Network Type:</string>
<!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="radio_info_override_network_type_label">Override Network Type:</string>
<!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_voice_raw_registration_state_label">Voice Raw Registration State:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_data_raw_registration_state_label">Data Raw Registration State:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_wlan_data_raw_registration_state_label">WLAN Data Raw Registration State:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="phone_index_label">Select phone index</string>
<!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="radio_info_set_perferred_label">Set Preferred Network Type:</string>
@@ -2198,4 +2213,11 @@
<!-- Telephony notification channel name for a channel containing SIP accounts removed
notificatios -->
<string name="notification_channel_sip_account">Deprecated SIP accounts</string>
+
+ <string name="send_from_work_profile_title">Can\'t message from a personal app</string>
+ <string name="send_from_work_profile_description">Your organization only allows you to send messages from work apps</string>
+ <string name="send_from_work_profile_cancel">Cancel</string>
+ <string name="send_from_work_profile_action_str">Switch to work profile</string>
+ <string name="install_messages_on_work_profile_action_str">Install a work messages app</string>
+
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index d0f427c..19798f0 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -309,6 +309,17 @@
<item name="android:backgroundDimEnabled">false</item>
</style>
+ <style name="Theme.Telephony.Transparent" parent="@android:style/Theme.DeviceDefault.Light">
+ <item name="android:forceDarkAllowed">true</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
+ </style>
+
<style name="CallSettingsWithoutDividerTheme" parent="DialerSettingsLight">
<item name="android:listDivider">@null</item>
</style>
diff --git a/src/com/android/phone/CLIRListPreference.java b/src/com/android/phone/CLIRListPreference.java
old mode 100755
new mode 100644
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index fdd610e..145df41 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -16,6 +16,7 @@
package com.android.phone;
+import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -268,6 +269,12 @@
getActionBar(), getResourcesForSubId(), R.string.call_settings_with_label);
mTelecomManager = getSystemService(TelecomManager.class);
mTelephonyCallback = new CallFeaturesTelephonyCallback();
+
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ // sets the talkback voice prompt to say "Back" instead of "Navigate Up"
+ actionBar.setHomeActionContentDescription(R.string.ota_call_end);
+ }
}
private void updateImsManager(Phone phone) {
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index 2cbb7c5..d1c8303 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -28,6 +28,7 @@
import com.android.internal.telephony.Phone;
import java.util.HashMap;
+import java.util.Locale;
public class CallForwardEditPreference extends EditPhoneNumberPreference {
private static final String LOG_TAG = "CallForwardEditPreference";
@@ -209,8 +210,9 @@
// 3gpp spec. This can cause us to receive "numbers" that are sequences of letters. In this
// case, we must detect these series of characters and replace them with "Voicemail".
// PhoneNumberUtils#formatNumber returns null if the number is not valid.
- if (mReplaceInvalidCFNumber && (PhoneNumberUtils.formatNumber(callForwardInfo.number,
- getCurrentCountryIso()) == null)) {
+ if (mReplaceInvalidCFNumber && !TextUtils.isEmpty(callForwardInfo.number)
+ && (PhoneNumberUtils.formatNumber(callForwardInfo.number, getCurrentCountryIso())
+ == null)) {
callForwardInfo.number = getContext().getString(R.string.voicemail);
Log.i(LOG_TAG, "handleGetCFResponse: Overridding CF number");
}
@@ -287,7 +289,7 @@
if (telephonyManager == null) {
return "";
}
- return telephonyManager.getNetworkCountryIso().toUpperCase();
+ return telephonyManager.getNetworkCountryIso().toUpperCase(Locale.ROOT);
}
// Message protocol:
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 14db930..73b61b6 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -40,8 +40,6 @@
import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaDisplayInfoRec;
import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaSignalInfoRec;
import com.android.internal.telephony.cdma.SignalToneUtil;
@@ -489,16 +487,9 @@
}
public void updatePhoneStateListeners(boolean isRefresh, int updateType, int subIdToUpdate) {
- List<SubscriptionInfo> subInfos;
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- subInfos = SubscriptionManagerService.getInstance()
- .getActiveSubscriptionInfoList(mApplication.getOpPackageName(),
- mApplication.getAttributionTag());
- } else {
- subInfos = SubscriptionController.getInstance()
- .getActiveSubscriptionInfoList(mApplication.getOpPackageName(),
- mApplication.getAttributionTag());
- }
+ List<SubscriptionInfo> subInfos = SubscriptionManagerService.getInstance()
+ .getActiveSubscriptionInfoList(mApplication.getOpPackageName(),
+ mApplication.getAttributionTag());
// Sort sub id list based on slot id, so that CFI/MWI notifications will be updated for
// slot 0 first then slot 1. This is needed to ensure that when CFI or MWI is enabled for
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index ba1ba3e..ee73f6d 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -62,8 +62,8 @@
import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConfigurationManager;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionInfoUpdater;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.util.ArrayUtils;
@@ -126,9 +126,7 @@
// CarrierService change monitoring
@NonNull private CarrierServiceChangeCallback[] mCarrierServiceChangeCallbacks;
- // SubscriptionInfoUpdater
- @NonNull private final SubscriptionInfoUpdater mSubscriptionInfoUpdater;
- // Broadcast receiver for system events (BootCompleted, MultiSimConfigChanged etc.)
+ // Broadcast receiver for system events
@NonNull
private final BroadcastReceiver mSystemBroadcastReceiver = new ConfigLoaderBroadcastReceiver();
@NonNull private final LocalLog mCarrierConfigLoadingLog = new LocalLog(100);
@@ -137,7 +135,7 @@
// Message codes; see mHandler below.
- // Request from SubscriptionInfoUpdater when SIM becomes absent or error.
+ // Request from UiccController when SIM becomes absent or error.
private static final int EVENT_CLEAR_CONFIG = 0;
// Has connected to default app.
private static final int EVENT_CONNECTED_TO_DEFAULT = 3;
@@ -165,7 +163,7 @@
private static final int EVENT_FETCH_DEFAULT_TIMEOUT = 14;
// Fetching config timed out from a carrier app.
private static final int EVENT_FETCH_CARRIER_TIMEOUT = 15;
- // SubscriptionInfoUpdater has finished updating the sub for the carrier config.
+ // SubscriptionManagerService has finished updating the sub for the carrier config.
private static final int EVENT_SUBSCRIPTION_INFO_UPDATED = 16;
// Multi-SIM config changed.
private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 17;
@@ -696,9 +694,7 @@
* receiver for relevant events.
*/
@VisibleForTesting
- /* package */ CarrierConfigLoader(@NonNull Context context,
- //TODO: Remove SubscriptionInfoUpdater.
- @Nullable SubscriptionInfoUpdater subscriptionInfoUpdater, @NonNull Looper looper) {
+ /* package */ CarrierConfigLoader(@NonNull Context context, @NonNull Looper looper) {
mContext = context;
mPlatformCarrierConfigPackage =
mContext.getString(R.string.platform_carrier_config_package);
@@ -706,7 +702,6 @@
IntentFilter systemEventsFilter = new IntentFilter();
systemEventsFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
- systemEventsFilter.addAction(TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED);
context.registerReceiver(mSystemBroadcastReceiver, systemEventsFilter);
mNumPhones = TelephonyManager.from(context).getActiveModemCount();
@@ -728,7 +723,10 @@
new HandlerExecutor(mHandler), mCarrierServiceChangeCallbacks[phoneId]);
}
logd("CarrierConfigLoader has started");
- mSubscriptionInfoUpdater = subscriptionInfoUpdater;
+
+ PhoneConfigurationManager.registerForMultiSimConfigChange(
+ mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
+
mHandler.sendEmptyMessage(EVENT_CHECK_SYSTEM_UPDATE);
}
@@ -741,8 +739,7 @@
/* package */ static CarrierConfigLoader init(@NonNull Context context) {
synchronized (CarrierConfigLoader.class) {
if (sInstance == null) {
- sInstance = new CarrierConfigLoader(context,
- PhoneFactory.getSubscriptionInfoUpdater(), Looper.myLooper());
+ sInstance = new CarrierConfigLoader(context, Looper.myLooper());
// Make this service available through ServiceManager.
TelephonyFrameworkInitializer.getTelephonyServiceManager()
.getCarrierConfigServiceRegisterer().register(sInstance);
@@ -763,6 +760,11 @@
}
}
+ if (mConfigFromDefaultApp.length <= phoneId) {
+ Log.wtf(LOG_TAG, "Invalid phone id " + phoneId);
+ return;
+ }
+
mConfigFromDefaultApp[phoneId] = null;
mConfigFromCarrierApp[phoneId] = null;
mServiceConnection[phoneId] = null;
@@ -777,6 +779,7 @@
}
private void updateSubscriptionDatabase(int phoneId) {
+ logd("updateSubscriptionDatabase: phoneId=" + phoneId);
String configPackageName;
PersistableBundle configToSend;
int carrierId = getSpecificCarrierIdForPhoneId(phoneId);
@@ -801,16 +804,10 @@
configToSend.putAll(config);
}
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- SubscriptionManagerService.getInstance().updateSubscriptionByCarrierConfig(
- phoneId, configPackageName, configToSend,
- () -> mHandler.obtainMessage(EVENT_SUBSCRIPTION_INFO_UPDATED, phoneId, -1)
- .sendToTarget());
- } else {
- mSubscriptionInfoUpdater.updateSubscriptionByCarrierConfigAndNotifyComplete(
- phoneId, configPackageName, configToSend,
- mHandler.obtainMessage(EVENT_SUBSCRIPTION_INFO_UPDATED, phoneId, -1));
- }
+ SubscriptionManagerService.getInstance().updateSubscriptionByCarrierConfig(
+ phoneId, configPackageName, configToSend,
+ () -> mHandler.obtainMessage(EVENT_SUBSCRIPTION_INFO_UPDATED, phoneId, -1)
+ .sendToTarget());
}
private void broadcastConfigChangedIntent(int phoneId) {
@@ -1199,12 +1196,24 @@
});
if (packageFiles == null || packageFiles.length < 1) return false;
for (File f : packageFiles) {
- logd("Deleting " + f.getName());
+ logd("Deleting " + getFilePathForLogging(f.getName()));
f.delete();
}
return true;
}
+ private String getFilePathForLogging(String filePath) {
+ if (!TextUtils.isEmpty(filePath)) {
+ String[] fileTokens = filePath.split("-");
+ if (fileTokens != null && fileTokens.length > 2) {
+ String iccid = fileTokens[fileTokens.length -2];
+ return getFilePathForLogging(filePath, iccid);
+ }
+ return filePath;
+ }
+ return filePath;
+ }
+
/** Builds a canonical file name for a config file. */
@NonNull
private static String getFilenameForConfig(
@@ -1397,7 +1406,6 @@
boolean persistent) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE, null);
- //TODO: Also check for SHELL UID to restrict this method to testing only (b/131326259)
int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
@@ -1805,10 +1813,6 @@
case Intent.ACTION_BOOT_COMPLETED:
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_UNLOCKED, null));
break;
-
- case TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED:
- mHandler.sendEmptyMessage(EVENT_MULTI_SIM_CONFIG_CHANGED);
- break;
}
}
}
diff --git a/src/com/android/phone/CdmaCallForwardOptions.java b/src/com/android/phone/CdmaCallForwardOptions.java
index a8d2e93..d70e709 100644
--- a/src/com/android/phone/CdmaCallForwardOptions.java
+++ b/src/com/android/phone/CdmaCallForwardOptions.java
@@ -17,10 +17,13 @@
package com.android.phone;
import android.app.ActionBar;
+import android.content.ContentProvider;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.UserHandle;
import android.preference.Preference;
import android.preference.PreferenceScreen;
import android.telephony.CarrierConfigManager;
@@ -212,6 +215,15 @@
}
Cursor cursor = null;
try {
+ // check if the URI returned by the user belongs to the user
+ final int currentUser = UserHandle.getUserId(Process.myUid());
+ if (currentUser
+ != ContentProvider.getUserIdFromUri(data.getData(), currentUser)) {
+
+ Log.w(LOG_TAG, "onActivityResult: Contact data of different user, "
+ + "cannot access");
+ return;
+ }
cursor = getContentResolver().query(data.getData(),
NUM_PROJECTION, null, null, null);
if ((cursor == null) || (!cursor.moveToFirst())) {
diff --git a/src/com/android/phone/DataCollectorConfig.java b/src/com/android/phone/DataCollectorConfig.java
new file mode 100644
index 0000000..00f2fce
--- /dev/null
+++ b/src/com/android/phone/DataCollectorConfig.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 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.phone;
+
+import android.provider.DeviceConfig;
+
+public final class DataCollectorConfig {
+ public static final long LOGCAT_READ_TIMEOUT_MILLIS_VALUE = 500L;
+ public static final long DUMPSYS_READ_TIMEOUT_MILLIS_VALUE = 100L;
+ public static final long LOGCAT_PROC_TIMEOUT_MILLIS_VALUE = 500L;
+ public static final long DUMPSYS_PROC_TIMEOUT_MILLIS_VALUE = 100L;
+ public static final int MAX_LOGCAT_LINES_LOW_MEM_DEVICE_VALUE = 2000;
+ public static final int MAX_LOGCAT_LINES_VALUE = 8000;
+ private static String LOGCAT_READ_TIMEOUT_MILLIS = "logcat_read_timeout_millis";
+ private static String DUMPSYS_READ_TIMEOUT_MILLIS = "dumpsys_read_timeout_millis";
+ private static String LOGCAT_PROC_TIMEOUT_MILLIS = "logcat_proc_timeout_millis";
+ private static String DUMPSYS_PROC_TIMEOUT_MILLIS = "dumpsys_proc_timeout_millis";
+ private static String MAX_LOGCAT_LINES_LOW_MEM = "max_logcat_lines_low_mem";
+ private static String MAX_LOGCAT_LINES = "max_logcat_lines";
+
+ public static int getMaxLogcatLinesForLowMemDevice() {
+ return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY,
+ MAX_LOGCAT_LINES_LOW_MEM, MAX_LOGCAT_LINES_LOW_MEM_DEVICE_VALUE);
+ }
+
+ public static int getMaxLogcatLines() {
+ return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY,
+ MAX_LOGCAT_LINES, MAX_LOGCAT_LINES_VALUE);
+ }
+
+ public static long getLogcatReadTimeoutMillis() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
+ LOGCAT_READ_TIMEOUT_MILLIS, LOGCAT_READ_TIMEOUT_MILLIS_VALUE);
+ }
+
+ public static long getDumpsysReadTimeoutMillis() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
+ DUMPSYS_READ_TIMEOUT_MILLIS, DUMPSYS_READ_TIMEOUT_MILLIS_VALUE);
+ }
+
+ public static long getLogcatProcTimeoutMillis() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
+ LOGCAT_PROC_TIMEOUT_MILLIS, LOGCAT_PROC_TIMEOUT_MILLIS_VALUE);
+ }
+
+ public static long getDumpsysProcTimeoutMillis() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
+ DUMPSYS_PROC_TIMEOUT_MILLIS, DUMPSYS_PROC_TIMEOUT_MILLIS_VALUE);
+ }
+
+ public static class Adapter {
+ public Adapter() {
+ }
+
+ public int getMaxLogcatLinesForLowMemDevice() {
+ return DataCollectorConfig.getMaxLogcatLinesForLowMemDevice();
+ }
+
+ public int getMaxLogcatLines() {
+ return DataCollectorConfig.getMaxLogcatLines();
+ }
+
+ public long getLogcatReadTimeoutMillis() {
+ return DataCollectorConfig.getLogcatReadTimeoutMillis();
+ }
+
+ public long getDumpsysReadTimeoutMillis() {
+ return DataCollectorConfig.getDumpsysReadTimeoutMillis();
+ }
+
+ public long getLogcatProcTimeoutMillis() {
+ return DataCollectorConfig.getLogcatProcTimeoutMillis();
+ }
+
+ public long getDumpsysProcTimeoutMillis() {
+ return DataCollectorConfig.getDumpsysProcTimeoutMillis();
+ }
+ }
+
+
+}
diff --git a/src/com/android/phone/DiagnosticDataCollector.java b/src/com/android/phone/DiagnosticDataCollector.java
new file mode 100644
index 0000000..e997270
--- /dev/null
+++ b/src/com/android/phone/DiagnosticDataCollector.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2023 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.phone;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+import android.os.DropBoxManager;
+import android.os.SystemClock;
+import android.os.TransactionTooLargeException;
+import android.telephony.AnomalyReporter;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import java.util.UUID;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Locale;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A class to help collect dumpsys/logcat and persist it to the
+ * on-device dropbox service. It is purely a utility and does
+ * not make decisions on if/when to collect.
+ */
+public class DiagnosticDataCollector {
+
+ //error msg that is appended to output if cmd execution results in error
+ public static final String ERROR_MSG = "DiagnosticDataCollector error executing cmd";
+ private static final String TAG = "DDC";
+ private static final String[] TELECOM_DUMPSYS_COMMAND =
+ {"/system/bin/dumpsys", "telecom", "EmergencyDiagnostics"};
+ private static final String[] TELEPHONY_DUMPSYS_COMMAND =
+ {"/system/bin/dumpsys", "telephony.registry", "EmergencyDiagnostics"};
+ private static final String LOGCAT_BINARY =
+ "/system/bin/logcat";
+ private static final String LOGCAT_BUFFERS = "system,radio";
+ private static final long LOG_TIME_OFFSET_MILLIS = 75L;
+ private static final String DUMPSYS_BINARY = "/system/bin/dumpsys";
+ private final Runtime mJavaRuntime;
+ private final Executor mAsyncTaskExecutor;
+ private final DropBoxManager mDropBoxManager;
+ private final SimpleDateFormat mDateFormat = new SimpleDateFormat("MM-dd HH:mm:ss.mmm",
+ Locale.US);
+ private final boolean mIsLowRamDevice;
+ public static final UUID DROPBOX_TRANSACTION_TOO_LARGE_EXCEPTION =
+ UUID.fromString("ab27e97a-ef7b-11ed-a05b-0242ac120003");
+ public static final String DROPBOX_TRANSACTION_TOO_LARGE_MSG =
+ "DiagnosticDataCollector: transaction too large";
+ public DiagnosticDataCollector(Runtime javaRuntime, Executor asyncTaskExecutor,
+ DropBoxManager dropBoxManager, boolean isLowRamDevice) {
+ mJavaRuntime = javaRuntime;
+ mAsyncTaskExecutor = asyncTaskExecutor;
+ mDropBoxManager = dropBoxManager;
+ mIsLowRamDevice = isLowRamDevice;
+ }
+
+ public void persistEmergencyDianosticData(@NonNull DataCollectorConfig.Adapter dc,
+ @NonNull TelephonyManager.EmergencyCallDiagnosticParams edp, @NonNull String tag) {
+
+ if (edp.isTelephonyDumpSysCollectionEnabled()) {
+ persistTelephonyState(dc, tag);
+ }
+ if (edp.isTelecomDumpSysCollectionEnabled()) {
+ persistTelecomState(dc, tag);
+ }
+ if (edp.isLogcatCollectionEnabled()) {
+ persistLogcat(dc, tag, edp.getLogcatStartTime());
+ }
+ }
+
+
+ @SuppressWarnings("JavaUtilDate") //just used for DateFormatter.format (required by logcat)
+ private void persistLogcat(DataCollectorConfig.Adapter dc, String tag, long logcatStartTime) {
+ String startTime = mDateFormat.format(new Date(logcatStartTime - LOG_TIME_OFFSET_MILLIS));
+ Log.d(TAG, "Persisting Logcat");
+ int maxLines;
+ if (mIsLowRamDevice) {
+ maxLines = dc.getMaxLogcatLinesForLowMemDevice();
+ } else {
+ maxLines = dc.getMaxLogcatLines();
+ }
+ DiagnosticRunnable dr = new DiagnosticRunnable(
+ new String[]{LOGCAT_BINARY, "-t", startTime, "-b", LOGCAT_BUFFERS},
+ dc.getLogcatReadTimeoutMillis(), dc.getLogcatProcTimeoutMillis(),
+ tag, dc.getMaxLogcatLinesForLowMemDevice());
+ mAsyncTaskExecutor.execute(dr);
+ }
+
+ private void persistTelecomState(DataCollectorConfig.Adapter dc, String tag) {
+ Log.d(TAG, "Persisting Telecom state");
+ DiagnosticRunnable dr = new DiagnosticRunnable(TELECOM_DUMPSYS_COMMAND,
+ dc.getDumpsysReadTimeoutMillis(), dc.getDumpsysProcTimeoutMillis(),
+ tag, dc.getMaxLogcatLines());
+ mAsyncTaskExecutor.execute(dr);
+ }
+
+ private void persistTelephonyState(DataCollectorConfig.Adapter dc, String tag) {
+ Log.d(TAG, "Persisting Telephony state");
+ DiagnosticRunnable dr = new DiagnosticRunnable(TELEPHONY_DUMPSYS_COMMAND,
+ dc.getDumpsysReadTimeoutMillis(),
+ dc.getDumpsysProcTimeoutMillis(),
+ tag, dc.getMaxLogcatLines());
+ mAsyncTaskExecutor.execute(dr);
+ }
+
+ private class DiagnosticRunnable implements Runnable {
+
+ private static final String TAG = "DDC-DiagnosticRunnable";
+ private final String[] mCmd;
+ private final String mDropBoxTag;
+ private final int mMaxLogcatLines;
+ private long mStreamTimeout;
+ private long mProcTimeout;
+
+ DiagnosticRunnable(String[] cmd, long streamTimeout, long procTimeout, String dropboxTag,
+ int maxLogcatLines) {
+ mCmd = cmd;
+ mStreamTimeout = streamTimeout;
+ mProcTimeout = procTimeout;
+ mDropBoxTag = dropboxTag;
+ mMaxLogcatLines = maxLogcatLines;
+ Log.d(TAG, "Runnable created with cmd: " + Arrays.toString(cmd));
+ }
+
+ @Override
+ @WorkerThread
+ public void run() {
+ Log.d(TAG, "Running async persist for tag" + mDropBoxTag);
+ getProcOutputAndPersist(mCmd,
+ mStreamTimeout, mProcTimeout, mDropBoxTag, mMaxLogcatLines);
+ }
+
+ @WorkerThread
+ private void getProcOutputAndPersist(String[] cmd, long streamTimeout, long procTimeout,
+ String dropboxTag, int maxLogcatLines) {
+ Process process = null;
+ StringBuilder output = new StringBuilder();
+ long startProcTime = SystemClock.elapsedRealtime();
+ int outputSizeFromErrorStream = 0;
+ try {
+ process = mJavaRuntime.exec(cmd);
+ readStreamLinesWithTimeout(
+ new BufferedReader(new InputStreamReader(process.getInputStream())), output,
+ streamTimeout, maxLogcatLines);
+ int outputSizeFromInputStream = output.length();
+ readStreamLinesWithTimeout(
+ new BufferedReader(new InputStreamReader(process.getErrorStream())), output,
+ streamTimeout, maxLogcatLines);
+ Log.d(TAG, "[" + cmd[0] + "]" + "streams read in " + (SystemClock.elapsedRealtime()
+ - startProcTime) + " milliseconds");
+ process.waitFor(procTimeout, TimeUnit.MILLISECONDS);
+ outputSizeFromErrorStream = output.length() - outputSizeFromInputStream;
+ } catch (InterruptedException e) {
+ output.append(ERROR_MSG + e.toString() + System.lineSeparator());
+ } catch (IOException e) {
+ output.append(ERROR_MSG + e.toString() + System.lineSeparator());
+ } finally {
+ if (process != null) {
+ process.destroy();
+ }
+ }
+ Log.d(TAG, "[" + cmd[0] + "]" + "output collected in " + (SystemClock.elapsedRealtime()
+ - startProcTime) + " milliseconds. Size:" + output.toString().length());
+ if (outputSizeFromErrorStream > 0) {
+ Log.w(TAG, "Cmd ran with errors");
+ output.append(ERROR_MSG + System.lineSeparator());
+ }
+ try {
+ mDropBoxManager.addText(dropboxTag, output.toString());
+ } catch (Exception e) {
+ if (e instanceof TransactionTooLargeException) {
+ AnomalyReporter.reportAnomaly(
+ DROPBOX_TRANSACTION_TOO_LARGE_EXCEPTION,
+ DROPBOX_TRANSACTION_TOO_LARGE_MSG);
+ }
+ Log.w(TAG, "Exception while writing to Dropbox " + e);
+ }
+ }
+
+ @WorkerThread
+ private void readStreamLinesWithTimeout(
+ BufferedReader inReader, StringBuilder outLines, long timeout, int maxLines)
+ throws IOException {
+ long startTimeMs = SystemClock.elapsedRealtime();
+ int totalLines = 0;
+ while (SystemClock.elapsedRealtime() < startTimeMs + timeout) {
+ // If there is a burst of data, continue reading without checking for timeout.
+ while (inReader.ready() && (totalLines < maxLines)) {
+ String line = inReader.readLine();
+ if (line == null) return; // end of stream.
+ outLines.append(line);
+ totalLines++;
+ outLines.append(System.lineSeparator());
+ }
+ SystemClock.sleep(timeout / 10);
+ }
+ }
+ }
+}
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index fc0e513..6901789 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -43,6 +43,8 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
import java.util.HashMap;
import java.util.Map;
@@ -244,9 +246,16 @@
.setMessage(text)
.setPositiveButton(R.string.alert_dialog_yes,
new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog,int whichButton) {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
// User clicked Yes. Exit Emergency Callback Mode.
- mPhone.exitEmergencyCallbackMode();
+ if (DomainSelectionResolver.getInstance()
+ .isDomainSelectionSupported()) {
+ EmergencyStateTracker.getInstance()
+ .exitEmergencyCallbackMode();
+ } else {
+ mPhone.exitEmergencyCallbackMode();
+ }
// Show progress dialog
showDialog(EXIT_ECM_PROGRESS_DIALOG);
diff --git a/src/com/android/phone/ErrorDialogActivity.java b/src/com/android/phone/ErrorDialogActivity.java
new file mode 100644
index 0000000..b85a0f3
--- /dev/null
+++ b/src/com/android/phone/ErrorDialogActivity.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2022 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.phone;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.AlertDialog;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.util.List;
+
+/** Used to display an error dialog from Telephony service. */
+public class ErrorDialogActivity extends Activity {
+
+ private static final String TAG = ErrorDialogActivity.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow()
+ .addSystemFlags(
+ android.view.WindowManager.LayoutParams
+ .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ showDialog();
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ // Don't show the return to previous task animation to avoid showing a black screen.
+ // Just dismiss the dialog and undim the previous activity immediately.
+ overridePendingTransition(0, 0);
+ }
+
+ private void showDialog() {
+ int managedProfileUserId =
+ getManagedProfileUserId(
+ getApplicationContext(), getApplicationContext().getUserId());
+ if (managedProfileUserId == UserHandle.USER_NULL) {
+ Log.w(TAG, "Error dialog is only applicable to managed profile.");
+ finish();
+ }
+ String defaultMessagesAppPackage =
+ getBaseContext()
+ .getSystemService(RoleManager.class)
+ .getSmsRoleHolder(managedProfileUserId);
+
+ Intent smsIntent = new Intent(Intent.ACTION_SENDTO)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .setData(Uri.parse("smsto:"));
+ Intent marketIntent =
+ new Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("market://search?q=messages"));
+ int positiveButtonText = 0;
+ Intent intent = null;
+ boolean showPositiveActionButton = true;
+ // A messages app may not be available in the managed profile. We try to handle that
+ // gracefully by redirecting to install a suitable app.
+ // Failing that, we simply omit the positive action button as the user has no mechanism
+ // to send the message.
+ if (defaultMessagesAppPackage != null
+ || canStartActivityAsUser(
+ smsIntent,
+ managedProfileUserId)) {
+ positiveButtonText = R.string.send_from_work_profile_action_str;
+ intent = smsIntent;
+ } else if (canStartActivityAsUser(marketIntent, managedProfileUserId)) {
+ positiveButtonText = R.string.install_messages_on_work_profile_action_str;
+ intent = marketIntent;
+ } else {
+ showPositiveActionButton = false;
+ }
+
+ // Variable has to be effectively final to be passing into the lambda, so copying it
+ // here.
+ Intent finalIntent = intent;
+ final DialogInterface.OnClickListener listener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ switchToManagedProfile(
+ managedProfileUserId,
+ finalIntent);
+ finish();
+ break;
+ case DialogInterface.BUTTON_NEGATIVE:
+ default:
+ finish();
+ }
+ }
+ };
+
+ AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this)
+ .setTitle(R.string.send_from_work_profile_title)
+ .setMessage(R.string.send_from_work_profile_description)
+ .setNegativeButton(R.string.send_from_work_profile_cancel, listener)
+ .setOnCancelListener(
+ new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ });
+ if (showPositiveActionButton) {
+ alertDialogBuilder.setPositiveButton(positiveButtonText, listener);
+ }
+ alertDialogBuilder.show();
+ }
+
+ private boolean canStartActivityAsUser(Intent intent, int managedProfileUserId) {
+ return !this.getPackageManager()
+ .queryIntentActivitiesAsUser(
+ intent,
+ PackageManager.ResolveInfoFlags.of(0),
+ managedProfileUserId)
+ .isEmpty();
+ }
+
+ private void switchToManagedProfile(int managedProfileUserId, Intent intent) {
+ try {
+ startActivityAsUser(intent,
+ ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(),
+ UserHandle.of(managedProfileUserId));
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to switch to managed profile.", e);
+ }
+ }
+
+ private static int getManagedProfileUserId(Context context, int userId) {
+ UserManager um = context.getSystemService(UserManager.class);
+ List<UserInfo> userProfiles = um.getProfiles(userId);
+ for (UserInfo uInfo : userProfiles) {
+ if (uInfo.id == userId) {
+ continue;
+ }
+ if (uInfo.isManagedProfile()) {
+ return uInfo.id;
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+}
diff --git a/src/com/android/phone/GsmUmtsCallForwardOptions.java b/src/com/android/phone/GsmUmtsCallForwardOptions.java
index fda0ea5..db830de 100644
--- a/src/com/android/phone/GsmUmtsCallForwardOptions.java
+++ b/src/com/android/phone/GsmUmtsCallForwardOptions.java
@@ -1,10 +1,13 @@
package com.android.phone;
import android.app.ActionBar;
+import android.content.ContentProvider;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.UserHandle;
import android.preference.Preference;
import android.preference.PreferenceScreen;
import android.telephony.CarrierConfigManager;
@@ -203,6 +206,15 @@
}
Cursor cursor = null;
try {
+ // check if the URI returned by the user belongs to the user
+ final int currentUser = UserHandle.getUserId(Process.myUid());
+ if (currentUser
+ != ContentProvider.getUserIdFromUri(data.getData(), currentUser)) {
+
+ Log.w(LOG_TAG, "onActivityResult: Contact data of different user, "
+ + "cannot access");
+ return;
+ }
cursor = getContentResolver().query(data.getData(),
NUM_PROJECTION, null, null, null);
if ((cursor == null) || (!cursor.moveToFirst())) {
diff --git a/src/com/android/phone/ImsProvisioningController.java b/src/com/android/phone/ImsProvisioningController.java
index 696f567..a62980e 100644
--- a/src/com/android/phone/ImsProvisioningController.java
+++ b/src/com/android/phone/ImsProvisioningController.java
@@ -41,6 +41,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.os.AsyncResult;
+import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -55,6 +56,7 @@
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.aidl.IFeatureProvisioningCallback;
import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
import android.telephony.ims.stub.ImsConfigImplBase;
@@ -89,6 +91,7 @@
private static final int EVENT_PROVISIONING_CAPABILITY_CHANGED = 2;
@VisibleForTesting
protected static final int EVENT_MULTI_SIM_CONFIGURATION_CHANGE = 3;
+ private static final int EVENT_PROVISIONING_VALUE_CHANGED = 4;
// Provisioning Keys that are handled via AOSP cache and not sent to the ImsService
private static final int[] LOCAL_IMS_CONFIG_KEYS = {
@@ -245,6 +248,11 @@
int activeModemCount = (int) ((AsyncResult) msg.obj).result;
onMultiSimConfigChanged(activeModemCount);
break;
+ case EVENT_PROVISIONING_VALUE_CHANGED:
+ log("subId " + msg.arg1 + " changed provisioning value item : " + msg.arg2
+ + " value : " + (int) msg.obj);
+ updateCapabilityTechFromKey(msg.arg1, msg.arg2, (int) msg.obj);
+ break;
default:
log("unknown message " + msg);
break;
@@ -366,12 +374,15 @@
private boolean mRequiredNotify = false;
private int mSubId;
private int mSlotId;
+ private ConfigCallback mConfigCallback;
MmTelFeatureListener(int slotId) {
log(LOG_PREFIX, slotId, "created");
mSlotId = slotId;
mSubId = getSubId(slotId);
+ mConfigCallback = new ConfigCallback(mSubId);
+
mConnector = mMmTelFeatureConnector.create(
mApp, slotId, TAG, this, new HandlerExecutor(mHandler));
mConnector.connect();
@@ -389,10 +400,22 @@
mSubId = subId;
mSlotId = getSlotId(subId);
+ mConfigCallback.setSubId(subId);
}
public void destroy() {
log("destroy");
+ if (mImsManager != null) {
+ try {
+ ImsConfig imsConfig = getImsConfig(mImsManager);
+ if (imsConfig != null) {
+ imsConfig.removeConfigCallback(mConfigCallback);
+ }
+ } catch (ImsException e) {
+ logw(LOG_PREFIX, mSlotId, "destroy : " + e.getMessage());
+ }
+ }
+ mConfigCallback = null;
mConnector.disconnect();
mConnector = null;
mReady = false;
@@ -409,6 +432,17 @@
mReady = true;
mImsManager = manager;
+ if (mImsManager != null) {
+ try {
+ ImsConfig imsConfig = getImsConfig(mImsManager);
+ if (imsConfig != null) {
+ imsConfig.addConfigCallback(mConfigCallback);
+ }
+ } catch (ImsException e) {
+ logw(LOG_PREFIX, mSlotId, "addConfigCallback : " + e.getMessage());
+ }
+ }
+
onMmTelAvailable();
}
@@ -572,12 +606,15 @@
private boolean mRequiredNotify = false;
private int mSubId;
private int mSlotId;
+ private ConfigCallback mConfigCallback;
RcsFeatureListener(int slotId) {
log(LOG_PREFIX, slotId, "created");
mSlotId = slotId;
mSubId = getSubId(slotId);
+ mConfigCallback = new ConfigCallback(mSubId);
+
mConnector = mRcsFeatureConnector.create(
mApp, slotId, this, new HandlerExecutor(mHandler), TAG);
mConnector.connect();
@@ -595,10 +632,22 @@
mSubId = subId;
mSlotId = getSlotId(subId);
+ mConfigCallback.setSubId(subId);
}
public void destroy() {
log(LOG_PREFIX, mSlotId, "destroy");
+ if (mRcsFeatureManager != null) {
+ try {
+ ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
+ if (imsConfig != null) {
+ imsConfig.removeConfigCallback(mConfigCallback);
+ }
+ } catch (ImsException e) {
+ logw(LOG_PREFIX, mSlotId, "destroy :" + e.getMessage());
+ }
+ }
+ mConfigCallback = null;
mConnector.disconnect();
mConnector = null;
mReady = false;
@@ -611,6 +660,17 @@
mReady = true;
mRcsFeatureManager = manager;
+ if (mRcsFeatureManager != null) {
+ try {
+ ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
+ if (imsConfig != null) {
+ imsConfig.addConfigCallback(mConfigCallback);
+ }
+ } catch (ImsException e) {
+ logw(LOG_PREFIX, mSlotId, "addConfigCallback :" + e.getMessage());
+ }
+ }
+
onRcsAvailable();
}
@@ -726,6 +786,42 @@
}
}
+ // When vendor ImsService changed provisioning data, which should be updated in AOSP.
+ // Catch the event using IImsConfigCallback.
+ private final class ConfigCallback extends IImsConfigCallback.Stub {
+ private int mSubId;
+
+ ConfigCallback(int subId) {
+ mSubId = subId;
+ }
+
+ public void setSubId(int subId) {
+ mSubId = subId;
+ }
+
+ @Override
+ public void onIntConfigChanged(int item, int value) throws RemoteException {
+ if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == item)) {
+ return;
+ }
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ if (mHandler != null) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_PROVISIONING_VALUE_CHANGED, mSubId, item, (Object) value));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public void onStringConfigChanged(int item, String value) throws RemoteException {
+ // Ignore this callback.
+ }
+ }
+
/**
* Do NOT use this directly, instead use {@link #getInstance()}.
*/
@@ -1468,13 +1564,7 @@
}
protected int getSubId(int slotId) {
- final int[] subIds = mSubscriptionManager.getSubscriptionIds(slotId);
- int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- if (subIds != null && subIds.length >= 1) {
- subId = subIds[0];
- }
-
- return subId;
+ return SubscriptionManager.getSubscriptionId(slotId);
}
protected int getSlotId(int subId) {
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index bf55764..3f35454 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -50,6 +50,7 @@
import com.android.ims.ImsManager;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.ISipDialogStateCallback;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.ims.ImsResolver;
@@ -252,7 +253,7 @@
} catch (ImsException e) {
Log.e(TAG, "isCapable: sudId=" + subId
+ ", capability=" + capability + ", " + e.getMessage());
- return false;
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -278,7 +279,7 @@
} catch (ImsException e) {
Log.e(TAG, "isAvailable: sudId=" + subId
+ ", capability=" + capability + ", " + e.getMessage());
- return false;
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -667,6 +668,60 @@
}
/**
+ * Register a state of Sip Dialog callback
+ */
+ @Override
+ public void registerSipDialogStateCallback(int subId, ISipDialogStateCallback cb) {
+ enforceReadPrivilegedPermission("registerSipDialogStateCallback");
+ if (cb == null) {
+ throw new IllegalArgumentException("SipDialogStateCallback is null");
+ }
+ final long identity = Binder.clearCallingIdentity();
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
+ }
+ try {
+ SipTransportController transport = getRcsFeatureController(subId).getFeature(
+ SipTransportController.class);
+ if (transport == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+ "This transport does not support the registerSipDialogStateCallback"
+ + " of SIP delegates");
+ }
+ transport.addCallbackForSipDialogState(subId, cb);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Unregister a state of Sip Dialog callback
+ */
+ @Override
+ public void unregisterSipDialogStateCallback(int subId, ISipDialogStateCallback cb) {
+ enforceReadPrivilegedPermission("unregisterSipDialogStateCallback");
+ if (cb == null) {
+ throw new IllegalArgumentException("SipDialogStateCallback is null");
+ }
+ final long identity = Binder.clearCallingIdentity();
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
+ }
+ try {
+ SipTransportController transport = getRcsFeatureController(subId).getFeature(
+ SipTransportController.class);
+ if (transport == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+ "This transport does not support the unregisterSipDialogStateCallback"
+ + " of SIP delegates");
+ }
+ transport.removeCallbackForSipDialogState(subId, cb);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Registers for updates to the RcsFeature connection through the IImsServiceFeatureCallback
* callback.
*/
@@ -786,16 +841,43 @@
int slotId = phone.getPhoneId();
if (!skipVerifyingConfig) {
verifyImsRcsConfiguredOrThrow(slotId);
+ verifyRcsSubIdActiveOrThrow(slotId, subId);
}
RcsFeatureController c = mRcsService.getFeatureController(slotId);
if (c == null) {
+ // If we hit this case, we have verified that TelephonyRcsService has processed any
+ // subId changes for the associated slot and applied configs. In this case, the configs
+ // do not have the RCS feature enabled.
throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
"The requested operation is not supported for subId " + subId);
}
+ if (!skipVerifyingConfig && c.getAssociatedSubId() != subId) {
+ // If we hit this case, the ImsFeature has not finished setting up the RCS feature yet
+ // or the RCS feature has crashed and is being set up again.
+ Log.w(TAG, "getRcsFeatureController: service unavailable on slot " + slotId
+ + " for subId " + subId);
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+ "The ImsService is not currently available for subid " + subId
+ + ", please try again");
+ }
return c;
}
/**
+ * Ensure the TelephonyRcsService is tracking the supplied subId for the supplied slotId and has
+ * set up the stack.
+ */
+ private void verifyRcsSubIdActiveOrThrow(int slotId, int subId) {
+ if (mRcsService.verifyActiveSubId(slotId, subId)) return;
+
+ Log.w(TAG, "verifyRcsSubIdActiveOrThrow: verify failed, service not set up yet on "
+ + "slot " + slotId + " for subId " + subId);
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+ "ImsService set up in progress for subId " + subId
+ + ", please try again");
+ }
+
+ /**
* Throw an ImsException if the IMS resolver does not have an ImsService configured for RCS
* for the given slot ID or no ImsResolver instance has been created.
* @param slotId The slot ID that the IMS service is created for.
diff --git a/src/com/android/phone/ImsStateCallbackController.java b/src/com/android/phone/ImsStateCallbackController.java
index 57c1787..edad754 100644
--- a/src/com/android/phone/ImsStateCallbackController.java
+++ b/src/com/android/phone/ImsStateCallbackController.java
@@ -68,6 +68,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
/**
@@ -143,6 +144,10 @@
private final SparseArray<MmTelFeatureListener> mMmTelFeatureListeners = new SparseArray<>();
private final SparseArray<RcsFeatureListener> mRcsFeatureListeners = new SparseArray<>();
+ // Container to store ImsManager instance by subId
+ private final ConcurrentHashMap<Integer, ImsManager> mSubIdToImsManagerCache =
+ new ConcurrentHashMap<>();
+
private final SubscriptionManager mSubscriptionManager;
private final TelephonyRegistryManager mTelephonyRegistryManager;
private MmTelFeatureConnectorFactory mMmTelFeatureFactory;
@@ -282,6 +287,13 @@
if (mSubId == subId) return;
logd(mLogPrefix + "setSubId changed subId=" + subId);
+ // subId changed from valid to invalid
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (VDBG) logv(mLogPrefix + "setSubId remove ImsManager " + mSubId);
+ // remove ImsManager reference associated with subId
+ mSubIdToImsManagerCache.remove(mSubId);
+ }
+
mSubId = subId;
}
@@ -298,6 +310,12 @@
mSubId = subId;
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) return;
+ // store ImsManager reference associated with subId
+ if (manager != null) {
+ if (VDBG) logv(mLogPrefix + "connectionReady add ImsManager " + subId);
+ mSubIdToImsManagerCache.put(subId, manager);
+ }
+
mState = STATE_READY;
mReason = AVAILABLE;
mHasConfig = true;
@@ -311,6 +329,10 @@
reason = convertReasonType(reason);
if (mReason == reason) return;
+ // remove ImsManager reference associated with subId
+ if (VDBG) logv(mLogPrefix + "connectionUnavailable remove ImsManager " + mSubId);
+ mSubIdToImsManagerCache.remove(mSubId);
+
connectionUnavailableInternal(reason);
}
@@ -319,7 +341,7 @@
mReason = reason;
/* If having no IMS package for MMTEL,
- * dicard the reason except REASON_NO_IMS_SERVICE_CONFIGURED. */
+ * discard the reason except REASON_NO_IMS_SERVICE_CONFIGURED. */
if (!mHasConfig && reason != REASON_NO_IMS_SERVICE_CONFIGURED) return;
onFeatureStateChange(mSubId, FEATURE_MMTEL, mState, mReason);
@@ -973,6 +995,19 @@
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_CALLBACK, cb));
}
+ /**
+ * Get ImsManager reference associated with subId
+ *
+ * @param subId subscribe ID
+ * @return instance of ImsManager associated with subId, but if ImsService is not
+ * available return null
+ */
+ public ImsManager getImsManager(int subId) {
+ if (VDBG) logv("getImsManager subId = " + subId);
+
+ return mSubIdToImsManagerCache.get(subId);
+ }
+
private void removeInactiveCallbacks(
ArrayList<IBinder> inactiveCallbacks, String message) {
if (inactiveCallbacks == null || inactiveCallbacks.size() == 0) return;
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 6f8826f..60287e5 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -59,6 +59,7 @@
import android.util.SparseArray;
import android.widget.Toast;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RILConstants;
@@ -166,7 +167,8 @@
* Private constructor (this is a singleton).
* @see #init(PhoneGlobals)
*/
- private NotificationMgr(PhoneGlobals app) {
+ @VisibleForTesting
+ /* package */ NotificationMgr(PhoneGlobals app) {
mApp = app;
mContext = app;
mStatusBarManager =
@@ -363,10 +365,16 @@
null));
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
}
-
- PendingIntent pendingIntent =
- PendingIntent.getActivity(mContext, subId /* requestCode */, intent,
- PendingIntent.FLAG_IMMUTABLE);
+ PendingIntent pendingIntent;
+ UserHandle subAssociatedUserHandle =
+ mSubscriptionManager.getSubscriptionUserHandle(subId);
+ if (subAssociatedUserHandle == null) {
+ pendingIntent = PendingIntent.getActivity(mContext, subId /* requestCode */, intent,
+ PendingIntent.FLAG_IMMUTABLE);
+ } else {
+ pendingIntent = PendingIntent.getActivityAsUser(mContext, subId /* requestCode */,
+ intent, PendingIntent.FLAG_IMMUTABLE, null, subAssociatedUserHandle);
+ }
Resources res = mContext.getResources();
PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
@@ -387,34 +395,21 @@
final Notification notification = builder.build();
List<UserHandle> users = getUsersExcludeDying();
for (UserHandle userHandle : users) {
- if (!hasUserRestriction(
- UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
- && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
- if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber,
- pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
- notifyAsUser(
- Integer.toString(subId) /* tag */,
- VOICEMAIL_NOTIFICATION,
- notification,
- userHandle);
- }
+ boolean isManagedUser = mUserManager.isManagedProfile(userHandle.getIdentifier());
+ if (!hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
+ && (userHandle.equals(subAssociatedUserHandle)
+ || (subAssociatedUserHandle == null && !isManagedUser))
+ && !maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount,
+ vmNumber, pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
+ notifyAsUser(
+ Integer.toString(subId) /* tag */,
+ VOICEMAIL_NOTIFICATION,
+ notification,
+ userHandle);
}
}
} else {
- List<UserHandle> users = getUsersExcludeDying();
- for (UserHandle userHandle : users) {
- if (!hasUserRestriction(
- UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
- && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
- if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
- false, userHandle, isRefresh)) {
- cancelAsUser(
- Integer.toString(subId) /* tag */,
- VOICEMAIL_NOTIFICATION,
- userHandle);
- }
- }
- }
+ cancelAsUser(Integer.toString(subId) /* tag */, VOICEMAIL_NOTIFICATION, UserHandle.ALL);
}
}
@@ -898,15 +893,16 @@
// can be overridden to hide the network selection to the end user. In this case, the
// notification is not shown to avoid confusion to the end user.
if (!shouldDisplayNetworkSelectOptions(subId)) {
- logi("Skipping network selection unavailable notification due to carrier policy.");
+ logi("Carrier configs refuse to show network selection not available notification");
return;
}
- // In unstable network condition, the phone may go in and out of service. Add logic here to
- // debounce the network selection notification. The notification only shows after phone is
- // out of service, AND fulfills one of the two conditions below:
- // - Out of service lasts {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS}
- // - Or has checked {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times
+ // In case network selection notification shows up repeatedly under
+ // unstable network condition. The logic is to check whether or not
+ // the service state keeps in no service condition for at least
+ // {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS}.
+ // And checking {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times.
+ // To avoid the notification showing up for the momentary state.
if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
if (mPreviousServiceState.get(subId, STATE_UNKNOWN_SERVICE)
!= ServiceState.STATE_OUT_OF_SERVICE) {
@@ -933,36 +929,10 @@
}
}
- private void startPendingNetworkSelectionNotification(int subId) {
- if (!mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
- if (DBG) {
- log("startPendingNetworkSelectionNotification: subId = " + subId);
- }
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId),
- NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS);
- mPendingEventCounter.put(subId, mPendingEventCounter.get(subId, 0) + 1);
- }
- }
-
- private void clearUpNetworkSelectionNotificationParam(int subId) {
- if (mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
- mHandler.removeMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId);
- }
- mPreviousServiceState.remove(subId);
- mOOSTimestamp.remove(subId);
- mPendingEventCounter.remove(subId);
- mSelectedNetworkOperatorName.remove(subId);
- }
-
- private static long getTimeStamp() {
- return SystemClock.elapsedRealtime();
- }
-
// TODO(b/243010310): merge methods below with Settings#MobileNetworkUtils and optimize them.
// The methods below are copied from com.android.settings.network.telephony.MobileNetworkUtils
- // to make sure the network selection unavailable notification should not show when network
- // selection menu is not visible to the end user in Settings app.
+ // to make sure the network selection unavailable notification should not show when Network
+ // Selection menu is not present in Settings app.
private boolean shouldDisplayNetworkSelectOptions(int subId) {
final TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(subId);
final CarrierConfigManager carrierConfigManager = mContext.getSystemService(
@@ -1065,4 +1035,31 @@
return false;
}
// END of TODO:(b/243010310): merge methods above with Settings#MobileNetworkUtils and optimize.
+
+ private void startPendingNetworkSelectionNotification(int subId) {
+ if (!mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
+ if (DBG) {
+ log("startPendingNetworkSelectionNotification: subId = " + subId);
+ }
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId),
+ NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS);
+ mPendingEventCounter.put(subId, mPendingEventCounter.get(subId, 0) + 1);
+ }
+ }
+
+ private void clearUpNetworkSelectionNotificationParam(int subId) {
+ if (mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
+ mHandler.removeMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId);
+ }
+ mPreviousServiceState.remove(subId);
+ mOOSTimestamp.remove(subId);
+ mPendingEventCounter.remove(subId);
+ mSelectedNetworkOperatorName.remove(subId);
+ }
+
+ @VisibleForTesting
+ public long getTimeStamp() {
+ return SystemClock.elapsedRealtime();
+ }
}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index c902e68..88fdf3a 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -17,6 +17,7 @@
package com.android.phone;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.KeyguardManager;
import android.app.ProgressDialog;
@@ -49,6 +50,7 @@
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyLocalConnection;
import android.telephony.TelephonyManager;
+import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
import android.widget.Toast;
@@ -67,14 +69,18 @@
import com.android.internal.telephony.TelephonyComponentFactory;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.data.DataEvaluation.DataDisallowedReason;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
import com.android.internal.telephony.ims.ImsResolver;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
+import com.android.internal.telephony.satellite.SatelliteController;
import com.android.internal.telephony.uicc.UiccPort;
import com.android.internal.telephony.uicc.UiccProfile;
import com.android.internal.util.IndentingPrintWriter;
import com.android.phone.settings.SettingsConstants;
import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
+import com.android.services.telephony.domainselection.TelephonyDomainSelectionService;
import com.android.services.telephony.rcs.TelephonyRcsService;
import java.io.FileDescriptor;
@@ -157,6 +163,7 @@
public ImsStateCallbackController mImsStateCallbackController;
public ImsProvisioningController mImsProvisioningController;
CarrierConfigLoader configLoader;
+ TelephonyDomainSelectionService mDomainSelectionService;
private Phone phoneInEcm;
@@ -188,6 +195,9 @@
@RoamingNotification
private int mPrevRoamingNotification = ROAMING_NOTIFICATION_NO_NOTIFICATION;
+ /** Operator numerics for which we've shown is-roaming notifications. **/
+ private ArraySet<String> mPrevRoamingOperatorNumerics = new ArraySet<>();
+
private WakeState mWakeState = WakeState.SLEEP;
private PowerManager mPowerManager;
@@ -378,7 +388,7 @@
.unregisterTelephonyCallback(callback);
callback = new PhoneAppCallback(subId);
tm.createForSubscriptionId(subId).registerTelephonyCallback(
- TelephonyManager.INCLUDE_LOCATION_DATA_NONE, mHandler::post,
+ TelephonyManager.INCLUDE_LOCATION_DATA_COARSE, mHandler::post,
callback);
mTelephonyCallbacks[phone.getPhoneId()] = callback;
}
@@ -440,9 +450,27 @@
// Inject telephony component factory if configured using other jars.
XmlResourceParser parser = getResources().getXml(R.xml.telephony_injection);
TelephonyComponentFactory.getInstance().injectTheComponentFactory(parser);
+
+ // Create DomainSelectionResolver always, but it MUST be initialized only when
+ // the device supports AOSP domain selection architecture and
+ // has new IRadio that supports its related HAL APIs.
+ DomainSelectionResolver.make(this,
+ getResources().getBoolean(R.bool.config_enable_aosp_domain_selection));
+
// Initialize the telephony framework
PhoneFactory.makeDefaultPhones(this);
+ // Initialize the DomainSelectionResolver after creating the Phone instance
+ // to check the Radio HAL version.
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ mDomainSelectionService = new TelephonyDomainSelectionService(this);
+ DomainSelectionResolver.getInstance().initialize(mDomainSelectionService);
+ // Initialize EmergencyStateTracker if domain selection is supported
+ boolean isSuplDdsSwitchRequiredForEmergencyCall = getResources()
+ .getBoolean(R.bool.config_gnss_supl_requires_default_data_for_emergency);
+ EmergencyStateTracker.make(this, isSuplDdsSwitchRequiredForEmergencyCall);
+ }
+
// Only bring up ImsResolver if the device supports having an IMS stack.
if (getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY_IMS)) {
@@ -486,6 +514,10 @@
// status bar icons and control other status bar behavior.
notificationMgr = NotificationMgr.init(this);
+ // Create the SatelliteController singleton, which acts as a backend service for
+ // {@link android.telephony.satellite.SatelliteManager}.
+ SatelliteController.make(this);
+
// Create an instance of CdmaPhoneCallState and initialize it to IDLE
cdmaPhoneCallState = new CdmaPhoneCallState();
cdmaPhoneCallState.CdmaPhoneCallStateInit();
@@ -503,6 +535,8 @@
imsRcsController = ImsRcsController.init(this);
+ configLoader = CarrierConfigLoader.init(this);
+
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
mImsStateCallbackController =
ImsStateCallbackController.make(this, PhoneFactory.getPhones().length);
@@ -514,8 +548,6 @@
ImsProvisioningController.make(this, PhoneFactory.getPhones().length);
}
- configLoader = CarrierConfigLoader.init(this);
-
// Create the CallNotifier singleton, which handles
// asynchronous events from the telephony layer (like
// launching the incoming-call UI when an incoming call comes
@@ -739,6 +771,13 @@
Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT, 0);
TelephonyProperties.airplane_mode_on(true); // true means int value 1
PhoneUtils.setRadioPower(false);
+ clearCacheOnRadioOff();
+ }
+
+ /** Clear fields on power off radio **/
+ private void clearCacheOnRadioOff() {
+ // Re-show is-roaming notifications after APM mode
+ mPrevRoamingOperatorNumerics.clear();
}
private void setRadioPowerOn() {
@@ -862,26 +901,27 @@
+ mDefaultDataSubId + ", ss roaming=" + serviceState.getDataRoaming());
}
if (subId == mDefaultDataSubId) {
- updateDataRoamingStatus();
+ updateDataRoamingStatus(serviceState.getOperatorNumeric());
}
}
/**
- * @return whether or not we should show a notification when connecting to data roaming if the
- * user has data roaming enabled
- */
- private boolean shouldShowDataConnectedRoaming(int subId) {
- PersistableBundle config = getCarrierConfigForSubId(subId);
- return config.getBoolean(CarrierConfigManager
- .KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL);
- }
-
- /**
* When roaming, if mobile data cannot be established due to data roaming not enabled, we need
* to notify the user so they can enable it through settings. Vise versa if the condition
* changes, we need to dismiss the notification.
*/
private void updateDataRoamingStatus() {
+ updateDataRoamingStatus(null /*roamingOperatorNumeric*/);
+ }
+
+ /**
+ * When roaming, if mobile data cannot be established due to data roaming not enabled, we need
+ * to notify the user so they can enable it through settings. Vise versa if the condition
+ * changes, we need to dismiss the notification.
+ * @param roamingOperatorNumeric The operator numeric for the current roaming. {@code null} if
+ * the current roaming operator numeric didn't change.
+ */
+ private void updateDataRoamingStatus(@Nullable String roamingOperatorNumeric) {
if (VDBG) Log.v(LOG_TAG, "updateDataRoamingStatus");
Phone phone = getPhone(mDefaultDataSubId);
if (phone == null) {
@@ -896,10 +936,21 @@
dataAllowed = reasons.isEmpty();
notAllowedDueToRoamingOff = (reasons.size() == 1
&& reasons.contains(DataDisallowedReason.ROAMING_DISABLED));
- mDataRoamingNotifLog.log("dataAllowed=" + dataAllowed + ", reasons=" + reasons);
- if (VDBG) Log.v(LOG_TAG, "dataAllowed=" + dataAllowed + ", reasons=" + reasons);
+ mDataRoamingNotifLog.log("dataAllowed=" + dataAllowed + ", reasons=" + reasons
+ + ", roamingOperatorNumeric=" + roamingOperatorNumeric);
+ if (VDBG) {
+ Log.v(LOG_TAG, "dataAllowed=" + dataAllowed + ", reasons=" + reasons
+ + ", roamingOperatorNumeric=" + roamingOperatorNumeric);
+ }
if (!dataAllowed && notAllowedDueToRoamingOff) {
+ // Don't show roaming notification if we've already shown for this MccMnc
+ if (roamingOperatorNumeric != null
+ && !mPrevRoamingOperatorNumerics.add(roamingOperatorNumeric)) {
+ Log.d(LOG_TAG, "Skip roaming disconnected notification since already shown in "
+ + "MccMnc " + roamingOperatorNumeric);
+ return;
+ }
// No need to show it again if we never cancelled it explicitly.
if (mPrevRoamingNotification == ROAMING_NOTIFICATION_DISCONNECTED) return;
// If the only reason of no data is data roaming disabled, then we notify the user
@@ -910,8 +961,18 @@
Message msg = mHandler.obtainMessage(EVENT_DATA_ROAMING_DISCONNECTED);
msg.arg1 = mDefaultDataSubId;
msg.sendToTarget();
- } else if (dataAllowed && dataIsNowRoaming(mDefaultDataSubId)
- && shouldShowDataConnectedRoaming(mDefaultDataSubId)) {
+ } else if (dataAllowed && dataIsNowRoaming(mDefaultDataSubId)) {
+ boolean isShowRoamingNotificationEnabled = getCarrierConfigForSubId(mDefaultDataSubId)
+ .getBoolean(CarrierConfigManager
+ .KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL);
+ if (!isShowRoamingNotificationEnabled) return;
+ // Don't show roaming notification if we've already shown for this MccMnc
+ if (roamingOperatorNumeric != null
+ && !mPrevRoamingOperatorNumerics.add(roamingOperatorNumeric)) {
+ Log.d(LOG_TAG, "Skip roaming connected notification since already shown in "
+ + "MccMnc " + roamingOperatorNumeric);
+ return;
+ }
// No need to show it again if we never cancelled it explicitly, or carrier config
// indicates this is not needed.
if (mPrevRoamingNotification == ROAMING_NOTIFICATION_CONNECTED) return;
@@ -1055,7 +1116,21 @@
} catch (Exception e) {
e.printStackTrace();
}
+ pw.println("DomainSelectionResolver:");
+ pw.increaseIndent();
+ try {
+ if (DomainSelectionResolver.getInstance() != null) {
+ DomainSelectionResolver.getInstance().dump(fd, pw, args);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
pw.decreaseIndent();
+ if (mDomainSelectionService != null) {
+ mDomainSelectionService.dump(fd, pw, args);
+ }
+ pw.decreaseIndent();
+ pw.println("mPrevRoamingOperatorNumerics:" + mPrevRoamingOperatorNumerics);
pw.println("------- End PhoneGlobals -------");
}
}
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 809b1c2..5cccd7f 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -17,6 +17,8 @@
package com.android.phone;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA;
import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_GSM;
@@ -29,6 +31,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.PropertyInvalidatedCache;
@@ -36,6 +39,7 @@
import android.app.role.RoleManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -48,8 +52,10 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.DropBoxManager;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.LocaleList;
import android.os.Looper;
import android.os.Message;
@@ -69,6 +75,7 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Telephony;
+import android.service.carrier.CarrierIdentifier;
import android.sysprop.TelephonyProperties;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
@@ -78,9 +85,11 @@
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.DataActivityType;
import android.telephony.Annotation.ThermalMitigationResult;
+import android.telephony.AnomalyReporter;
import android.telephony.CallForwardingInfo;
import android.telephony.CarrierConfigManager;
import android.telephony.CarrierRestrictionRules;
+import android.telephony.CellBroadcastIdRange;
import android.telephony.CellIdentity;
import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
@@ -137,6 +146,15 @@
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.satellite.ISatelliteDatagramCallback;
+import android.telephony.satellite.ISatelliteProvisionStateCallback;
+import android.telephony.satellite.ISatelliteStateCallback;
+import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
+import android.telephony.satellite.SatelliteCapabilities;
+import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteDatagramCallback;
+import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteProvisionStateCallback;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.EventLog;
@@ -181,12 +199,13 @@
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.RadioInterfaceCapabilityController;
import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SmsApplication;
import com.android.internal.telephony.SmsController;
import com.android.internal.telephony.SmsPermissions;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.data.DataUtils;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.euicc.EuiccConnector;
import com.android.internal.telephony.ims.ImsResolver;
@@ -194,6 +213,7 @@
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
import com.android.internal.telephony.metrics.RcsStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.satellite.SatelliteController;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
@@ -207,6 +227,7 @@
import com.android.internal.telephony.uicc.UiccProfile;
import com.android.internal.telephony.uicc.UiccSlot;
import com.android.internal.telephony.util.LocaleUtils;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.telephony.util.VoicemailNotificationSettingsUtil;
import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.HexDump;
@@ -214,6 +235,8 @@
import com.android.phone.callcomposer.CallComposerPictureTransfer;
import com.android.phone.callcomposer.ImageData;
import com.android.phone.settings.PickSmsSubscriptionActivity;
+import com.android.phone.slice.SlicePurchaseController;
+import com.android.phone.utils.CarrierAllowListInfo;
import com.android.phone.vvm.PhoneAccountHandleConverter;
import com.android.phone.vvm.RemoteVvmTaskManager;
import com.android.phone.vvm.VisualVoicemailSettingsUtil;
@@ -239,6 +262,7 @@
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
@@ -362,6 +386,8 @@
private static final int EVENT_ENABLE_VONR_DONE = 114;
private static final int CMD_IS_VONR_ENABLED = 115;
private static final int EVENT_IS_VONR_ENABLED_DONE = 116;
+ private static final int CMD_PURCHASE_PREMIUM_CAPABILITY = 117;
+ private static final int EVENT_PURCHASE_PREMIUM_CAPABILITY_DONE = 118;
// Parameters of select command.
private static final int SELECT_COMMAND = 0xA4;
@@ -369,28 +395,30 @@
private static final int SELECT_P2 = 0;
private static final int SELECT_P3 = 0x10;
+ // Toggling null cipher and integrity support was added in IRadioNetwork 2.1
+ private static final int MIN_NULL_CIPHER_AND_INTEGRITY_VERSION = 201;
+
/** The singleton instance. */
private static PhoneInterfaceManager sInstance;
private static List<String> sThermalMitigationAllowlistedPackages = new ArrayList<>();
- private PhoneGlobals mApp;
- private CallManager mCM;
- private ImsResolver mImsResolver;
- private UserManager mUserManager;
- private AppOpsManager mAppOps;
- private PackageManager mPm;
- private MainThreadHandler mMainThreadHandler;
- private final SubscriptionController mSubscriptionController;
- private SharedPreferences mTelephonySharedPreferences;
- private PhoneConfigurationManager mPhoneConfigurationManager;
+ private final PhoneGlobals mApp;
+ private final CallManager mCM;
+ private final ImsResolver mImsResolver;
+
+ private final SatelliteController mSatelliteController;
+ private final UserManager mUserManager;
+ private final AppOpsManager mAppOps;
+ private final MainThreadHandler mMainThreadHandler;
+ private final SharedPreferences mTelephonySharedPreferences;
+ private final PhoneConfigurationManager mPhoneConfigurationManager;
private final RadioInterfaceCapabilityController mRadioInterfaceCapabilities;
- private final Telephony2gUpdater mTelephony2gUpdater;
/** User Activity */
- private AtomicBoolean mNotifyUserActivity;
+ private final AtomicBoolean mNotifyUserActivity;
private static final int USER_ACTIVITY_NOTIFICATION_DELAY = 200;
- private Set<Integer> mCarrierPrivilegeTestOverrideSubIds = new ArraySet<>();
+ private final Set<Integer> mCarrierPrivilegeTestOverrideSubIds = new ArraySet<>();
private static final String PREF_CARRIERS_ALPHATAG_PREFIX = "carrier_alphtag_";
private static final String PREF_CARRIERS_NUMBER_PREFIX = "carrier_number_";
@@ -411,6 +439,9 @@
private static final int SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS = -1;
private static final int MODEM_DOES_NOT_SUPPORT_DATA_THROTTLING_ERROR_CODE = -2;
+ private static final String PURCHASE_PREMIUM_CAPABILITY_ERROR_UUID =
+ "24bf97a6-e8a6-44d8-a6a4-255d7548733c";
+
/**
* Experiment flag to enable erase modem config on reset network, default value is false
*/
@@ -419,6 +450,8 @@
private static final int SET_NETWORK_SELECTION_MODE_AUTOMATIC_TIMEOUT_MS = 2000; // 2 seconds
+ private static final int MODEM_ACTIVITY_TIME_OFFSET_CORRECTION_MS = 50;
+
/**
* With support for MEP(multiple enabled profile) in Android T, a SIM card can have more than
* one ICCID active at the same time.
@@ -470,6 +503,17 @@
}
}
+ private static final class PurchasePremiumCapabilityArgument {
+ public @TelephonyManager.PremiumCapability int capability;
+ public @NonNull IIntegerConsumer callback;
+
+ PurchasePremiumCapabilityArgument(@TelephonyManager.PremiumCapability int capability,
+ @NonNull IIntegerConsumer callback) {
+ this.capability = capability;
+ this.callback = callback;
+ }
+ }
+
/**
* A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
* request after sending. The main thread will notify the request when it is complete.
@@ -795,7 +839,7 @@
if (uiccPort == null) {
loge("iccCloseLogicalChannel: No UICC");
request.result = new IllegalArgumentException(
- "iccCloseLogicalChannel: No UICC");
+ "iccCloseLogicalChannel: No UICC");
notifyRequester(request);
} else {
onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE, request);
@@ -1363,7 +1407,7 @@
int errorCode = CellNetworkScanResult.STATUS_UNKNOWN_ERROR;
if (ar.exception instanceof CommandException) {
CommandException.Error error =
- ((CommandException) (ar.exception)).getCommandError();
+ ((CommandException) (ar.exception)).getCommandError();
if (error == CommandException.Error.RADIO_NOT_AVAILABLE) {
errorCode = CellNetworkScanResult.STATUS_RADIO_NOT_AVAILABLE;
} else if (error == CommandException.Error.GENERIC_FAILURE) {
@@ -1437,6 +1481,8 @@
ModemActivityInfo info = (ModemActivityInfo) ar.result;
if (isModemActivityInfoValid(info)) {
mergeModemActivityInfo(info);
+ } else {
+ loge("queryModemActivityInfo: invalid response");
}
// This is needed to decouple ret from mLastModemActivityInfo
// We don't want to return mLastModemActivityInfo which is updated
@@ -1475,13 +1521,14 @@
break;
}
- case CMD_SET_ALLOWED_CARRIERS:
+ case CMD_SET_ALLOWED_CARRIERS: {
request = (MainThreadRequest) msg.obj;
CarrierRestrictionRules argument =
(CarrierRestrictionRules) request.argument;
onCompleted = obtainMessage(EVENT_SET_ALLOWED_CARRIERS_DONE, request);
defaultPhone.setAllowedCarriers(argument, onCompleted, request.workSource);
break;
+ }
case EVENT_SET_ALLOWED_CARRIERS_DONE:
ar = (AsyncResult) msg.obj;
@@ -1518,7 +1565,7 @@
request.result = ar.result;
} else {
request.result = new IllegalStateException(
- "Failed to get carrier restrictions");
+ "Failed to get carrier restrictions");
if (ar.result == null) {
loge("getAllowedCarriers: Empty response");
} else if (ar.exception instanceof CommandException) {
@@ -1528,7 +1575,39 @@
loge("getAllowedCarriers: Unknown exception");
}
}
- notifyRequester(request);
+ if (request.argument != null) {
+ // This is for the implementation of carrierRestrictionStatus.
+ CallerCallbackInfo callbackInfo = (CallerCallbackInfo) request.argument;
+ Consumer<Integer> callback = callbackInfo.getConsumer();
+ int callerCarrierId = callbackInfo.getCarrierId();
+ int lockStatus = TelephonyManager.CARRIER_RESTRICTION_STATUS_UNKNOWN;
+ if (ar.exception == null && ar.result instanceof CarrierRestrictionRules) {
+ CarrierRestrictionRules carrierRestrictionRules =
+ (CarrierRestrictionRules) ar.result;
+ int carrierId = -1;
+ try {
+ CarrierIdentifier carrierIdentifier =
+ carrierRestrictionRules.getAllowedCarriers().get(0);
+ carrierId = CarrierResolver.getCarrierIdFromIdentifier(mApp,
+ carrierIdentifier);
+ } catch (NullPointerException | IndexOutOfBoundsException ex) {
+ Rlog.e(LOG_TAG, "CarrierIdentifier exception = " + ex);
+ }
+ lockStatus = carrierRestrictionRules.getCarrierRestrictionStatus();
+ if (carrierId != -1 && callerCarrierId == carrierId && lockStatus
+ == TelephonyManager.CARRIER_RESTRICTION_STATUS_RESTRICTED) {
+ lockStatus = TelephonyManager
+ .CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER;
+ }
+ } else {
+ Rlog.e(LOG_TAG,
+ "getCarrierRestrictionStatus: exception ex = " + ar.exception);
+ }
+ callback.accept(lockStatus);
+ } else {
+ // This is for the implementation of getAllowedCarriers.
+ notifyRequester(request);
+ }
break;
case EVENT_GET_FORBIDDEN_PLMNS_DONE:
@@ -1572,7 +1651,7 @@
}
onCompleted = obtainMessage(EVENT_GET_FORBIDDEN_PLMNS_DONE, request);
((SIMRecords) uiccApp.getIccRecords()).getForbiddenPlmns(
- onCompleted);
+ onCompleted);
break;
case CMD_SWITCH_SLOTS:
@@ -1737,7 +1816,7 @@
case EVENT_CMD_MODEM_REBOOT_DONE:
handleNullReturnEvent(msg, "rebootModem");
break;
- case CMD_REQUEST_ENABLE_MODEM:
+ case CMD_REQUEST_ENABLE_MODEM: {
request = (MainThreadRequest) msg.obj;
boolean enable = (boolean) request.argument;
onCompleted = obtainMessage(EVENT_ENABLE_MODEM_DONE, request);
@@ -1745,6 +1824,7 @@
PhoneConfigurationManager.getInstance()
.enablePhone(request.phone, enable, onCompleted);
break;
+ }
case EVENT_ENABLE_MODEM_DONE: {
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
@@ -1986,7 +2066,7 @@
if (error == CommandException.Error.RADIO_NOT_AVAILABLE) {
request.result = TelephonyManager
- .THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
+ .THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
} else if (error == CommandException.Error.INVALID_ARGUMENTS) {
request.result = SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS;
} else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
@@ -2065,8 +2145,8 @@
onCompleted = obtainMessage(EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE,
request);
phone.getSignalStrengthController().setSignalStrengthUpdateRequest(
- request.subId, pair.first /*callingUid*/,
- pair.second /*request*/, onCompleted);
+ request.subId, pair.first /*callingUid*/,
+ pair.second /*request*/, onCompleted);
break;
}
case EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE: {
@@ -2093,8 +2173,8 @@
onCompleted = obtainMessage(EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE,
request);
phone.getSignalStrengthController().clearSignalStrengthUpdateRequest(
- request.subId, pair.first /*callingUid*/,
- pair.second /*request*/, onCompleted);
+ request.subId, pair.first /*callingUid*/,
+ pair.second /*request*/, onCompleted);
break;
}
case EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE: {
@@ -2141,11 +2221,44 @@
break;
}
+ case CMD_PURCHASE_PREMIUM_CAPABILITY: {
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_PURCHASE_PREMIUM_CAPABILITY_DONE, request);
+ PurchasePremiumCapabilityArgument arg =
+ (PurchasePremiumCapabilityArgument) request.argument;
+ SlicePurchaseController.getInstance(request.phone).purchasePremiumCapability(
+ arg.capability, onCompleted);
+ break;
+ }
+
+ case EVENT_PURCHASE_PREMIUM_CAPABILITY_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ PurchasePremiumCapabilityArgument arg =
+ (PurchasePremiumCapabilityArgument) request.argument;
+ try {
+ int result = (int) ar.result;
+ arg.callback.accept(result);
+ log("purchasePremiumCapability: capability="
+ + TelephonyManager.convertPremiumCapabilityToString(arg.capability)
+ + ", result="
+ + TelephonyManager.convertPurchaseResultToString(result));
+ } catch (RemoteException e) {
+ String logStr = "Purchase premium capability "
+ + TelephonyManager.convertPremiumCapabilityToString(arg.capability)
+ + " failed: " + e;
+ if (DBG) log(logStr);
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString(PURCHASE_PREMIUM_CAPABILITY_ERROR_UUID), logStr);
+ }
+ break;
+ }
+
case CMD_PREPARE_UNATTENDED_REBOOT:
request = (MainThreadRequest) msg.obj;
request.result =
UiccController.getInstance().getPinStorage()
- .prepareUnattendedReboot(request.workSource);
+ .prepareUnattendedReboot(request.workSource);
notifyRequester(request);
break;
@@ -2343,28 +2456,30 @@
mApp = app;
mCM = PhoneGlobals.getInstance().mCM;
mImsResolver = ImsResolver.getInstance();
+ mSatelliteController = SatelliteController.getInstance();
mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
- mPm = app.getSystemService(PackageManager.class);
mMainThreadHandler = new MainThreadHandler();
- if (!PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- mSubscriptionController = SubscriptionController.getInstance();
- } else {
- mSubscriptionController = null;
- }
- mTelephonySharedPreferences =
- PreferenceManager.getDefaultSharedPreferences(mApp);
+ mTelephonySharedPreferences = PreferenceManager.getDefaultSharedPreferences(mApp);
mNetworkScanRequestTracker = new NetworkScanRequestTracker();
mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
mRadioInterfaceCapabilities = RadioInterfaceCapabilityController.getInstance();
mNotifyUserActivity = new AtomicBoolean(false);
PropertyInvalidatedCache.invalidateCache(TelephonyManager.CACHE_KEY_PHONE_ACCOUNT_TO_SUBID);
- mTelephony2gUpdater = new Telephony2gUpdater(mApp);
- mTelephony2gUpdater.init();
publish();
+ CarrierAllowListInfo.loadInstance(mApp);
}
- private Phone getDefaultPhone() {
+ @VisibleForTesting
+ public SharedPreferences getSharedPreferences() {
+ return mTelephonySharedPreferences;
+ }
+
+ /**
+ * Get the default phone for this device.
+ */
+ @VisibleForTesting
+ public Phone getDefaultPhone() {
Phone thePhone = getPhone(getDefaultSubscription());
return (thePhone != null) ? thePhone : PhoneFactory.getDefaultPhone();
}
@@ -2391,6 +2506,21 @@
? getDefaultPhone() : getPhone(subId);
}
+ /**
+ * Get phone object associated with a subscription.
+ * Return default phone if phone object associated with subscription is null
+ * @param subId - subscriptionId
+ * @return phone object associated with a subscription or default phone if null.
+ */
+ private @NonNull Phone getPhoneFromSubIdOrDefault(int subId) {
+ Phone phone = getPhoneFromSubId(subId);
+ if (phone == null) {
+ loge("Called with invalid subId: " + subId + ". Retrying with default phone.");
+ phone = getDefaultPhone();
+ }
+ return phone;
+ }
+
@Nullable
private UiccPort getUiccPortFromRequest(@NonNull MainThreadRequest request) {
Phone phone = getPhoneFromRequest(request);
@@ -2398,8 +2528,13 @@
UiccController.getInstance().getUiccPort(phone.getPhoneId());
}
- // returns phone associated with the subId.
- private Phone getPhone(int subId) {
+ /**
+ * @param subId The sub Id that associates the phone. If the device has no active SIM, passing
+ * in {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} or any sub <=
+ * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} will return {@code null}.
+ * @return The Phone associated the sub Id
+ */
+ private @Nullable Phone getPhone(int subId) {
return PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId));
}
@@ -2575,14 +2710,16 @@
synchronized (UnlockSim.this) {
mRetryCount = msg.arg1;
if (ar.exception != null) {
- if (ar.exception instanceof CommandException &&
- ((CommandException)(ar.exception)).getCommandError()
- == CommandException.Error.PASSWORD_INCORRECT) {
+ CommandException.Error error = null;
+ if (ar.exception instanceof CommandException) {
+ error = ((CommandException) (ar.exception))
+ .getCommandError();
+ }
+ if (error == CommandException.Error.PASSWORD_INCORRECT) {
mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
- } //When UiccCardApp dispose,handle message and return exception
- else if (ar.exception instanceof CommandException &&
- ((CommandException) (ar.exception)).getCommandError()
- == CommandException.Error.ABORTED) {
+ } else if (error == CommandException.Error.ABORTED) {
+ /* When UiccCardApp dispose, handle message and return
+ exception */
mResult = PhoneConstants.PIN_OPERATION_ABORTED;
} else {
mResult = PhoneConstants.PIN_GENERAL_FAILURE;
@@ -2696,10 +2833,7 @@
WorkSource workSource = getWorkSource(Binder.getCallingUid());
final long identity = Binder.clearCallingIdentity();
try {
- final Phone phone = getPhone(getDefaultSubscription());
- if (phone != null) {
- phone.updateServiceLocation(workSource);
- }
+ getPhoneFromSubIdOrDefault(getDefaultSubscription()).updateServiceLocation(workSource);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2838,6 +2972,10 @@
public boolean setRadioPower(boolean turnOn) {
enforceModifyPermission();
+ if (!turnOn) {
+ log("setRadioPower off: callingPackage=" + getCurrentPackageName());
+ }
+
final long identity = Binder.clearCallingIdentity();
try {
final Phone defaultPhone = PhoneFactory.getDefaultPhone();
@@ -2856,6 +2994,10 @@
public boolean setRadioPowerForSubscriber(int subId, boolean turnOn) {
enforceModifyPermission();
+ if (!turnOn) {
+ log("setRadioPowerForSubscriber off: subId=" + subId
+ + ",callingPackage=" + getCurrentPackageName());
+ }
final long identity = Binder.clearCallingIdentity();
try {
final Phone phone = getPhone(subId);
@@ -2870,6 +3012,101 @@
}
}
+ /**
+ * Vote on powering off the radio for a reason. The radio will be turned on only when there is
+ * no reason to power it off. When any of the voters want to power it off, it will be turned
+ * off. In case of emergency, the radio will be turned on even if there are some reasons for
+ * powering it off, and these radio off votes will be cleared.
+ * Multiple apps can vote for the same reason and the last vote will take effect. Each app is
+ * responsible for its vote. A powering-off vote of a reason will be maintained until it is
+ * cleared by calling {@link clearRadioPowerOffForReason} for that reason, or an emergency call
+ * is made, or the device is rebooted. When an app comes backup from a crash, it needs to make
+ * sure if its vote is as expected. An app can use the API {@link getRadioPowerOffReasons} to
+ * check its vote.
+ *
+ * @param subId The subscription ID.
+ * @param reason The reason for powering off radio.
+ * @return true on success and false on failure.
+ */
+ public boolean requestRadioPowerOffForReason(int subId,
+ @TelephonyManager.RadioPowerReason int reason) {
+ enforceModifyPermission();
+
+ log("requestRadioPowerOffForReason: subId=" + subId
+ + ",reason=" + reason + ",callingPackage=" + getCurrentPackageName());
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Phone phone = getPhoneFromSubIdOrDefault(subId);
+ if (phone != null) {
+ phone.setRadioPowerForReason(false, reason);
+ return true;
+ } else {
+ loge("requestRadioPowerOffForReason: phone is null");
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Remove the vote on powering off the radio for a reason, as requested by
+ * {@link requestRadioPowerOffForReason}.
+ *
+ * @param subId The subscription ID.
+ * @param reason The reason for powering off radio.
+ * @return true on success and false on failure.
+ */
+ public boolean clearRadioPowerOffForReason(int subId,
+ @TelephonyManager.RadioPowerReason int reason) {
+ enforceModifyPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Phone phone = getPhoneFromSubIdOrDefault(subId);
+ if (phone != null) {
+ phone.setRadioPowerForReason(true, reason);
+ return true;
+ } else {
+ loge("clearRadioPowerOffForReason: phone is null");
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Get reasons for powering off radio, as requested by {@link requestRadioPowerOffForReason}.
+ *
+ * @param subId The subscription ID.
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ * @return List of reasons for powering off radio.
+ */
+ public List getRadioPowerOffReasons(int subId, String callingPackage, String callingFeatureId) {
+ enforceReadPrivilegedPermission("getRadioPowerOffReasons");
+
+ final long identity = Binder.clearCallingIdentity();
+ List result = new ArrayList();
+ try {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId,
+ callingPackage, callingFeatureId, "getRadioPowerOffReasons")) {
+ return result;
+ }
+
+ final Phone phone = getPhoneFromSubIdOrDefault(subId);
+ if (phone != null) {
+ result.addAll(phone.getRadioPowerOffReasons());
+ } else {
+ loge("getRadioPowerOffReasons: phone is null");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return result;
+ }
+
// FIXME: subId version needed
@Override
public boolean enableDataConnectivity(String callingPackage) {
@@ -2977,9 +3214,8 @@
}
final long identity = Binder.clearCallingIdentity();
try {
- Phone phone = getPhone(getDefaultSubscription());
- return phone == null ? TelephonyManager.CALL_STATE_IDLE :
- PhoneConstantConversions.convertCallState(phone.getState());
+ Phone phone = getPhoneFromSubIdOrDefault(getDefaultSubscription());
+ return PhoneConstantConversions.convertCallState(phone.getState());
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -3284,13 +3520,18 @@
}
@Override
- public void setCellInfoListRate(int rateInMillis) {
+ public void setCellInfoListRate(int rateInMillis, int subId) {
enforceModifyPermission();
WorkSource workSource = getWorkSource(Binder.getCallingUid());
final long identity = Binder.clearCallingIdentity();
try {
- getDefaultPhone().setCellInfoListRate(rateInMillis, workSource);
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ getDefaultPhone().setCellInfoListRate(rateInMillis, workSource);
+ } else {
+ phone.setCellInfoListRate(rateInMillis, workSource);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -3318,6 +3559,26 @@
}
@Override
+ public String getPrimaryImei(String callingPackage, String callingFeatureId) {
+ enforceCallingPackage(callingPackage, Binder.getCallingUid(), "getPrimaryImei");
+ if (!checkCallingOrSelfReadDeviceIdentifiersForAnySub(mApp, callingPackage,
+ callingFeatureId, "getPrimaryImei")) {
+ throw new SecurityException("Caller does not have permission");
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ for (Phone phone : PhoneFactory.getPhones()) {
+ if (phone.getImeiType() == Phone.IMEI_TYPE_PRIMARY) {
+ return phone.getImei();
+ }
+ }
+ throw new UnsupportedOperationException("Operation not supported");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public String getTypeAllocationCodeForSlot(int slotIndex) {
Phone phone = PhoneFactory.getPhone(slotIndex);
String tac = null;
@@ -3491,10 +3752,31 @@
*
* @throws SecurityException if the caller does not have the required permission
*/
- private void enforceModifyPermission() {
+ @VisibleForTesting
+ public void enforceModifyPermission() {
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
}
+ /**
+ * Make sure the caller has the READ_PHONE_STATE permission.
+ *
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ @VisibleForTesting
+ public void enforceReadPermission() {
+ enforceReadPermission(null);
+ }
+
+ /**
+ * Make sure the caller has the READ_PHONE_STATE permissions.
+ *
+ * @throws SecurityException if the caller does not have the READ_PHONE_STATE permission.
+ */
+ @VisibleForTesting
+ public void enforceReadPermission(String msg) {
+ mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, msg);
+ }
+
private void enforceActiveEmergencySessionPermission() {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
@@ -3517,6 +3799,15 @@
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
}
+ /**
+ * Make sure the caller has SATELLITE_COMMUNICATION permission.
+ * @param message - log message to print.
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ private void enforceSatelliteCommunicationPermission(String message) {
+ mApp.enforceCallingOrSelfPermission(permission.SATELLITE_COMMUNICATION, message);
+ }
+
private String createTelUrl(String number) {
if (TextUtils.isEmpty(number)) {
return null;
@@ -3526,15 +3817,15 @@
}
private static void log(String msg) {
- Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
+ Log.d(LOG_TAG, msg);
}
private static void logv(String msg) {
- Log.v(LOG_TAG, "[PhoneIntfMgr] " + msg);
+ Log.v(LOG_TAG, msg);
}
private static void loge(String msg) {
- Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
+ Log.e(LOG_TAG, msg);
}
@Override
@@ -3963,15 +4254,14 @@
}
/**
- * returns true, if the device is in a state where both voice and data
- * are supported simultaneously. This can change based on location or network condition.
+ * returns true, if the device is in a state where both voice and data
+ * are supported simultaneously. This can change based on location or network condition.
*/
@Override
public boolean isConcurrentVoiceAndDataAllowed(int subId) {
final long identity = Binder.clearCallingIdentity();
try {
- final Phone phone = getPhone(subId);
- return (phone == null ? false : phone.isConcurrentVoiceAndDataAllowed());
+ return getPhoneFromSubIdOrDefault(subId).isConcurrentVoiceAndDataAllowed();
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -4004,8 +4294,8 @@
@Override
public int getNetworkSelectionMode(int subId) {
TelephonyPermissions
- .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
- mApp, subId, "getNetworkSelectionMode");
+ .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "getNetworkSelectionMode");
final long identity = Binder.clearCallingIdentity();
try {
if (!isActiveSubscription(subId)) {
@@ -4052,7 +4342,18 @@
try {
int slotId = getSlotIndexOrException(subId);
verifyImsMmTelConfiguredOrThrow(slotId);
- ImsManager.getInstance(mApp, slotId).addRegistrationCallbackForSubscription(c, subId);
+
+ ImsStateCallbackController controller = ImsStateCallbackController.getInstance();
+ if (controller != null) {
+ ImsManager imsManager = controller.getImsManager(subId);
+ if (imsManager != null) {
+ imsManager.addRegistrationCallbackForSubscription(c, subId);
+ } else {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ } else {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
+ }
} catch (ImsException e) {
throw new ServiceSpecificException(e.getCode());
} finally {
@@ -4073,14 +4374,20 @@
throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
}
final long token = Binder.clearCallingIdentity();
+
try {
- ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
- .removeRegistrationCallbackForSubscription(c, subId);
- } catch (ImsException e) {
- Log.i(LOG_TAG, "unregisterImsRegistrationCallback: " + subId
- + "is inactive, ignoring unregister.");
- // If the subscription is no longer active, just return, since the callback
- // will already have been removed internally.
+ ImsStateCallbackController controller = ImsStateCallbackController.getInstance();
+ if (controller != null) {
+ ImsManager imsManager = controller.getImsManager(subId);
+ if (imsManager != null) {
+ imsManager.removeRegistrationCallbackForSubscription(c, subId);
+ } else {
+ Log.i(LOG_TAG, "unregisterImsRegistrationCallback: " + subId
+ + "is inactive, ignoring unregister.");
+ // If the ImsManager is not valid, just return, since the callback
+ // will already have been removed internally.
+ }
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -4173,7 +4480,18 @@
try {
int slotId = getSlotIndexOrException(subId);
verifyImsMmTelConfiguredOrThrow(slotId);
- ImsManager.getInstance(mApp, slotId).addCapabilitiesCallbackForSubscription(c, subId);
+
+ ImsStateCallbackController controller = ImsStateCallbackController.getInstance();
+ if (controller != null) {
+ ImsManager imsManager = controller.getImsManager(subId);
+ if (imsManager != null) {
+ imsManager.addCapabilitiesCallbackForSubscription(c, subId);
+ } else {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ } else {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
+ }
} catch (ImsException e) {
throw new ServiceSpecificException(e.getCode());
} finally {
@@ -4196,13 +4514,18 @@
final long token = Binder.clearCallingIdentity();
try {
- ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
- .removeCapabilitiesCallbackForSubscription(c, subId);
- } catch (ImsException e) {
- Log.i(LOG_TAG, "unregisterMmTelCapabilityCallback: " + subId
- + "is inactive, ignoring unregister.");
- // If the subscription is no longer active, just return, since the callback
- // will already have been removed internally.
+ ImsStateCallbackController controller = ImsStateCallbackController.getInstance();
+ if (controller != null) {
+ ImsManager imsManager = controller.getImsManager(subId);
+ if (imsManager != null) {
+ imsManager.removeCapabilitiesCallbackForSubscription(c, subId);
+ } else {
+ Log.i(LOG_TAG, "unregisterMmTelCapabilityCallback: " + subId
+ + " is inactive, ignoring unregister.");
+ // If the ImsManager is not valid, just return, since the callback
+ // will already have been removed internally.
+ }
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -4613,8 +4936,18 @@
}
int slotId = getSlotIndexOrException(subId);
verifyImsMmTelConfiguredOrThrow(slotId);
- ImsManager.getInstance(mApp, slotId)
- .addProvisioningCallbackForSubscription(callback, subId);
+
+ ImsStateCallbackController controller = ImsStateCallbackController.getInstance();
+ if (controller != null) {
+ ImsManager imsManager = controller.getImsManager(subId);
+ if (imsManager != null) {
+ imsManager.addProvisioningCallbackForSubscription(callback, subId);
+ } else {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ } else {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
+ }
} catch (ImsException e) {
throw new ServiceSpecificException(e.getCode());
} finally {
@@ -4631,13 +4964,18 @@
throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
}
try {
- ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
- .removeProvisioningCallbackForSubscription(callback, subId);
- } catch (ImsException e) {
- Log.i(LOG_TAG, "unregisterImsProvisioningChangedCallback: " + subId
- + "is inactive, ignoring unregister.");
- // If the subscription is no longer active, just return, since the callback will already
- // have been removed internally.
+ ImsStateCallbackController controller = ImsStateCallbackController.getInstance();
+ if (controller != null) {
+ ImsManager imsManager = controller.getImsManager(subId);
+ if (imsManager != null) {
+ imsManager.removeProvisioningCallbackForSubscription(callback, subId);
+ } else {
+ Log.i(LOG_TAG, "unregisterImsProvisioningChangedCallback: " + subId
+ + " is inactive, ignoring unregister.");
+ // If the ImsManager is not valid, just return, since the callback will already
+ // have been removed internally.
+ }
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -4995,7 +5333,7 @@
return getDataNetworkTypeForSubscriber(subId, callingPackage, callingFeatureId);
} else if (targetSdk == android.os.Build.VERSION_CODES.Q
&& !TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow(
- mApp, subId, callingPackage, callingFeatureId,
+ mApp, subId, callingPackage, callingFeatureId,
"getNetworkTypeForSubscriber")) {
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
@@ -5160,11 +5498,8 @@
}
private boolean isActiveSubscription(int subId) {
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- return SubscriptionManagerService.getInstance().isActiveSubId(subId,
- mApp.getOpPackageName(), mApp.getFeatureId());
- }
- return mSubscriptionController.isActiveSubId(subId);
+ return getSubscriptionManagerService().isActiveSubId(subId,
+ mApp.getOpPackageName(), mApp.getFeatureId());
}
/**
@@ -5205,7 +5540,7 @@
portIndex);
if (phoneId == -1) {
throw new IllegalArgumentException("Given slot index: " + slotIndex + " port index: "
- + portIndex + " does not correspond to an active phone");
+ + portIndex + " does not correspond to an active phone");
}
return PhoneFactory.getPhone(phoneId);
}
@@ -5324,12 +5659,12 @@
@Override
public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel,
- int cla, int command, int p1, int p2, int p3, String data) {
+ int cla, int command, int p1, int p2, int p3, String data) {
enforceModifyPermission();
if (DBG) {
log("iccTransmitApduLogicalChannelByPort: slotIndex=" + slotIndex + " portIndex="
- + portIndex + " chnl=" + channel + " cla=" + cla + " cmd=" + command + " p1="
- + p1 + " p2=" + p2 + " p3=" + p3 + " data=" + data);
+ + portIndex + " chnl=" + channel + " cla=" + cla + " cmd=" + command + " p1="
+ + p1 + " p2=" + p2 + " p3=" + p3 + " data=" + data);
}
return iccTransmitApduLogicalChannelWithPermission(
getPhoneFromSlotPortIndexOrThrowException(slotIndex, portIndex), channel, cla,
@@ -5377,13 +5712,13 @@
@Override
public String iccTransmitApduBasicChannelByPort(int slotIndex, int portIndex,
- String callingPackage, int cla, int command, int p1, int p2, int p3, String data) {
+ String callingPackage, int cla, int command, int p1, int p2, int p3, String data) {
enforceModifyPermission();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
if (DBG) {
log("iccTransmitApduBasicChannelByPort: slotIndex=" + slotIndex + " portIndex="
- + portIndex + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2="
- + p2 + " p3=" + p3 + " data=" + data);
+ + portIndex + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2="
+ + p2 + " p3=" + p3 + " data=" + data);
}
return iccTransmitApduBasicChannelWithPermission(
@@ -5699,8 +6034,7 @@
// may happen if the does not support IMS.
return;
}
- mImsResolver.disableIms(slotIndex);
- mImsResolver.enableIms(slotIndex);
+ mImsResolver.resetIms(slotIndex);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -6020,7 +6354,7 @@
Binder.restoreCallingIdentity(identity);
}
}
- /**
+ /**
* Get the manual network selection
*
* @param subId the id of the subscription.
@@ -6030,8 +6364,8 @@
@Override
public String getManualNetworkSelectionPlmn(int subId) {
TelephonyPermissions
- .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
- mApp, subId, "getManualNetworkSelectionPlmn");
+ .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "getManualNetworkSelectionPlmn");
final long identity = Binder.clearCallingIdentity();
try {
@@ -6045,7 +6379,7 @@
}
OperatorInfo networkSelection = phone.getSavedNetworkSelection();
return TextUtils.isEmpty(networkSelection.getOperatorNumeric())
- ? phone.getManualNetworkSelectionPlmn() : networkSelection.getOperatorNumeric();
+ ? phone.getManualNetworkSelectionPlmn() : networkSelection.getOperatorNumeric();
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -6203,7 +6537,7 @@
String newUssdCommand = "";
try {
newUssdCommand = carrierXmlParser.getFeature(
- CarrierXmlParser.FEATURE_CALL_WAITING)
+ CarrierXmlParser.FEATURE_CALL_WAITING)
.makeCommand(CarrierXmlParser.SsEntry.SSAction.QUERY, null);
} catch (NullPointerException e) {
loge("Failed to generate USSD number" + e);
@@ -6260,7 +6594,7 @@
String newUssdCommand = "";
try {
newUssdCommand = carrierXmlParser.getFeature(
- CarrierXmlParser.FEATURE_CALL_WAITING)
+ CarrierXmlParser.FEATURE_CALL_WAITING)
.makeCommand(ssAction, null);
} catch (NullPointerException e) {
loge("Failed to generate USSD number" + e);
@@ -6304,16 +6638,16 @@
LocationAccessPolicy.LocationPermissionResult.DENIED_HARD;
if (!renounceFineLocationAccess) {
locationResult = LocationAccessPolicy.checkLocationPermission(mApp,
- new LocationAccessPolicy.LocationPermissionQuery.Builder()
- .setCallingPackage(callingPackage)
- .setCallingFeatureId(callingFeatureId)
- .setCallingPid(Binder.getCallingPid())
- .setCallingUid(Binder.getCallingUid())
- .setMethod("requestNetworkScan")
- .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
- .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
- .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q)
- .build());
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingFeatureId(callingFeatureId)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("requestNetworkScan")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
+ .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q)
+ .build());
}
if (locationResult != LocationAccessPolicy.LocationPermissionResult.ALLOWED) {
SecurityException e = checkNetworkRequestForSanitizedLocationAccess(
@@ -6332,7 +6666,8 @@
final long identity = Binder.clearCallingIdentity();
try {
return mNetworkScanRequestTracker.startNetworkScan(
- renounceFineLocationAccess, request, messenger, binder, getPhone(subId),
+ renounceFineLocationAccess, request, messenger, binder,
+ getPhoneFromSubIdOrDefault(subId),
callingUid, callingPid, callingPackage);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -6351,7 +6686,7 @@
}
boolean hasNetworkScanPermission =
mApp.checkCallingOrSelfPermission(android.Manifest.permission.NETWORK_SCAN)
- == PERMISSION_GRANTED;
+ == PERMISSION_GRANTED;
if (!hasCarrierPriv && !hasNetworkScanPermission) {
return new SecurityException("permission.NETWORK_SCAN or carrier privileges is needed"
@@ -6427,7 +6762,7 @@
mApp, subId, "getAllowedNetworkTypesForReason");
final long identity = Binder.clearCallingIdentity();
try {
- return getPhoneFromSubId(subId).getAllowedNetworkTypes(reason);
+ return getPhoneFromSubIdOrDefault(subId).getAllowedNetworkTypes(reason);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -6513,12 +6848,10 @@
// any network types which were set for security reasons.
if (mApp.checkCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE)
!= PERMISSION_GRANTED
- && (reason == TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G
- || reason == TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS)) {
+ && reason == TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G) {
throw new SecurityException(
"setAllowedNetworkTypesForReason cannot be called with carrier privileges for"
- + " reason "
- + reason);
+ + " reason " + reason);
}
if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) {
loge("setAllowedNetworkTypesForReason: Invalid allowed network type reason: " + reason);
@@ -6611,10 +6944,10 @@
try {
mApp.enforceCallingOrSelfPermission(permission.READ_BASIC_PHONE_STATE,
functionName);
- } catch (Exception e) {
+ } catch (SecurityException e) {
mApp.enforceCallingOrSelfPermission(permission.ACCESS_NETWORK_STATE, functionName);
}
- } catch (Exception e) {
+ } catch (SecurityException e) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, functionName);
@@ -6653,17 +6986,17 @@
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE,
functionName);
- } catch (Exception e) {
+ } catch (SecurityException e) {
try {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PHONE_STATE,
functionName);
- } catch (Exception e2) {
+ } catch (SecurityException e2) {
mApp.enforceCallingOrSelfPermission(
permission.READ_BASIC_PHONE_STATE, functionName);
}
}
- } catch (Exception e) {
+ } catch (SecurityException e) {
enforceReadPrivilegedPermission(functionName);
}
@@ -6672,8 +7005,7 @@
int phoneId = SubscriptionManager.getPhoneId(subId);
Phone phone = PhoneFactory.getPhone(phoneId);
if (phone != null) {
- boolean retVal;
- retVal = phone.getDataSettingsManager().isDataEnabled();
+ boolean retVal = phone.getDataSettingsManager().isDataEnabled();
if (DBG) log("isDataEnabled: " + retVal + ", subId=" + subId);
return retVal;
} else {
@@ -6700,15 +7032,15 @@
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE,
functionName);
- } catch (Exception e) {
+ } catch (SecurityException e) {
mApp.enforceCallingOrSelfPermission(permission.READ_BASIC_PHONE_STATE,
functionName);
}
- } catch (Exception e) {
+ } catch (SecurityException e) {
try {
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
functionName);
- } catch (Exception e2) {
+ } catch (SecurityException e2) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, functionName);
}
@@ -7129,16 +7461,9 @@
return null;
}
- ParcelUuid groupUuid;
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- final SubscriptionInfo info = SubscriptionManagerService.getInstance()
- .getSubscriptionInfo(subId);
- groupUuid = info.getGroupUuid();
- } else {
- final SubscriptionInfo info = mSubscriptionController
- .getSubscriptionInfo(subId);
- groupUuid = info.getGroupUuid();
- }
+ final SubscriptionInfo info = getSubscriptionManagerService()
+ .getSubscriptionInfo(subId);
+ ParcelUuid groupUuid = info.getGroupUuid();
// If it doesn't belong to any group, return just subscriberId of itself.
if (groupUuid == null) {
return new String[]{subscriberId};
@@ -7146,16 +7471,9 @@
// Get all subscriberIds from the group.
final List<String> mergedSubscriberIds = new ArrayList<>();
- List<SubscriptionInfo> groupInfos;
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- groupInfos = SubscriptionManagerService.getInstance()
- .getSubscriptionsInGroup(groupUuid, mApp.getOpPackageName(),
- mApp.getAttributionTag());
- } else {
- groupInfos = mSubscriptionController
- .getSubscriptionsInGroup(groupUuid, mApp.getOpPackageName(),
- mApp.getAttributionTag());
- }
+ List<SubscriptionInfo> groupInfos = getSubscriptionManagerService()
+ .getSubscriptionsInGroup(groupUuid, mApp.getOpPackageName(),
+ mApp.getAttributionTag());
for (SubscriptionInfo subInfo : groupInfos) {
subscriberId = telephonyManager.getSubscriberId(subInfo.getSubscriptionId());
if (subscriberId != null) {
@@ -7239,7 +7557,12 @@
@Override
public int getRadioAccessFamily(int phoneId, String callingPackage) {
+ int raf = RadioAccessFamily.RAF_UNKNOWN;
Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone == null) {
+ return raf;
+ }
+
try {
TelephonyPermissions
.enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
@@ -7248,15 +7571,9 @@
EventLog.writeEvent(0x534e4554, "150857259", -1, "Missing Permission");
throw e;
}
- int raf = RadioAccessFamily.RAF_UNKNOWN;
- if (phone == null) {
- return raf;
- }
+
final long identity = Binder.clearCallingIdentity();
try {
- TelephonyPermissions
- .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
- mApp, phone.getSubId(), "getRadioAccessFamily");
raf = ProxyController.getInstance().getRadioAccessFamily(phoneId);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -7566,9 +7883,10 @@
public @Nullable PhoneAccountHandle getPhoneAccountHandleForSubscriptionId(int subscriptionId) {
TelephonyPermissions
.enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
- mApp,
- subscriptionId,
- "getPhoneAccountHandleForSubscriptionId, " + "subscriptionId: " + subscriptionId);
+ mApp,
+ subscriptionId,
+ "getPhoneAccountHandleForSubscriptionId, " + "subscriptionId: "
+ + subscriptionId);
final long identity = Binder.clearCallingIdentity();
try {
Phone phone = getPhone(subscriptionId);
@@ -7715,23 +8033,12 @@
}
final long identity = Binder.clearCallingIdentity();
try {
- SubscriptionInfo info;
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- info = SubscriptionManagerService.getInstance().getActiveSubscriptionInfo(subId,
- phone.getContext().getOpPackageName(),
- phone.getContext().getAttributionTag());
- if (info == null) {
- log("getSimLocaleForSubscriber, inactive subId: " + subId);
- return null;
- }
- } else {
- info = mSubscriptionController.getActiveSubscriptionInfo(subId,
- phone.getContext().getOpPackageName(),
- phone.getContext().getAttributionTag());
- if (info == null) {
- log("getSimLocaleForSubscriber, inactive subId: " + subId);
- return null;
- }
+ SubscriptionInfo info = getSubscriptionManagerService().getActiveSubscriptionInfo(subId,
+ phone.getContext().getOpPackageName(),
+ phone.getContext().getAttributionTag());
+ if (info == null) {
+ log("getSimLocaleForSubscriber, inactive subId: " + subId);
+ return null;
}
// Try and fetch the locale from the carrier properties or from the SIM language
// preferences (EF-PL and EF-LI)...
@@ -7783,12 +8090,8 @@
* NOTE: this method assumes permission checks are done and caller identity has been cleared.
*/
private List<SubscriptionInfo> getActiveSubscriptionInfoListPrivileged() {
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- return SubscriptionManagerService.getInstance().getActiveSubscriptionInfoList(
- mApp.getOpPackageName(), mApp.getAttributionTag());
- }
- return mSubscriptionController.getActiveSubscriptionInfoList(mApp.getOpPackageName(),
- mApp.getAttributionTag());
+ return getSubscriptionManagerService().getActiveSubscriptionInfoList(
+ mApp.getOpPackageName(), mApp.getAttributionTag());
}
private ActivityStatsTechSpecificInfo[] mLastModemActivitySpecificInfo = null;
@@ -7815,7 +8118,7 @@
}
}
- // Checks that ModemActivityInfo is valid. Sleep time, Idle time, Rx time and Tx time should be
+ // Checks that ModemActivityInfo is valid. Sleep time and Idle time should be
// less than total activity duration.
private boolean isModemActivityInfoValid(ModemActivityInfo info) {
if (info == null) {
@@ -7823,13 +8126,13 @@
}
int activityDurationMs =
(int) (info.getTimestampMillis() - mLastModemActivityInfo.getTimestampMillis());
+ activityDurationMs += MODEM_ACTIVITY_TIME_OFFSET_CORRECTION_MS;
+
int totalTxTimeMs = Arrays.stream(info.getTransmitTimeMillis()).sum();
return (info.isValid()
- && (info.getSleepTimeMillis() <= activityDurationMs)
- && (info.getIdleTimeMillis() <= activityDurationMs)
- && (info.getReceiveTimeMillis() <= activityDurationMs)
- && (totalTxTimeMs <= activityDurationMs));
+ && (info.getSleepTimeMillis() <= activityDurationMs)
+ && (info.getIdleTimeMillis() <= activityDurationMs));
}
private void updateLastModemActivityInfo(ModemActivityInfo info, int rat, int freq) {
@@ -7863,10 +8166,10 @@
info.getReceiveTimeMillis(rat) + mLastModemActivityInfo.getReceiveTimeMillis(rat));
}
- /**
- * Merge this ModemActivityInfo with mLastModemActivitySpecificInfo
- * @param info recent ModemActivityInfo
- */
+ /**
+ * Merge this ModemActivityInfo with mLastModemActivitySpecificInfo
+ * @param info recent ModemActivityInfo
+ */
private void mergeModemActivityInfo(ModemActivityInfo info) {
List<ActivityStatsTechSpecificInfo> merged = new ArrayList<>();
ActivityStatsTechSpecificInfo deltaSpecificInfo;
@@ -7911,17 +8214,17 @@
mLastModemActivityInfo.setTimestamp(info.getTimestampMillis());
mLastModemActivityInfo.setSleepTimeMillis(
info.getSleepTimeMillis()
- + mLastModemActivityInfo.getSleepTimeMillis());
+ + mLastModemActivityInfo.getSleepTimeMillis());
mLastModemActivityInfo.setIdleTimeMillis(
info.getIdleTimeMillis()
- + mLastModemActivityInfo.getIdleTimeMillis());
+ + mLastModemActivityInfo.getIdleTimeMillis());
mLastModemActivityInfo =
- new ModemActivityInfo(
- mLastModemActivityInfo.getTimestampMillis(),
- mLastModemActivityInfo.getSleepTimeMillis(),
- mLastModemActivityInfo.getIdleTimeMillis(),
- mLastModemActivitySpecificInfo);
+ new ModemActivityInfo(
+ mLastModemActivityInfo.getTimestampMillis(),
+ mLastModemActivityInfo.getSleepTimeMillis(),
+ mLastModemActivityInfo.getIdleTimeMillis(),
+ mLastModemActivitySpecificInfo);
}
private ActivityStatsTechSpecificInfo[] deepCopyModemActivitySpecificInfo(
@@ -7998,21 +8301,12 @@
.contains(callingPackage);
try {
// isActiveSubId requires READ_PHONE_STATE, which we already check for above
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
- .getSubscriptionInfoInternal(subId);
- if (subInfo == null || !subInfo.isActive()) {
- Rlog.d(LOG_TAG, "getServiceStateForSubscriber returning null for inactive "
- + "subId=" + subId);
- return null;
- }
- } else {
- if (!mSubscriptionController.isActiveSubId(subId, callingPackage,
- callingFeatureId)) {
- Rlog.d(LOG_TAG, "getServiceStateForSubscriber returning null for inactive "
- + "subId=" + subId);
- return null;
- }
+ SubscriptionInfoInternal subInfo = getSubscriptionManagerService()
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo == null || !subInfo.isActive()) {
+ Rlog.d(LOG_TAG, "getServiceStateForSubscriber returning null for inactive "
+ + "subId=" + subId);
+ return null;
}
ServiceState ss = phone.getServiceState();
@@ -8147,7 +8441,8 @@
*
* @throws SecurityException if the caller does not have the required permission
*/
- private void enforceReadPrivilegedPermission(String message) {
+ @VisibleForTesting
+ public void enforceReadPrivilegedPermission(String message) {
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
message);
}
@@ -8163,6 +8458,16 @@
}
/**
+ * Make sure either called from same process as self (phone) or IPC caller has interact across
+ * users permission.
+ *
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ private void enforceInteractAcrossUsersPermission(String message) {
+ mApp.enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS, message);
+ }
+
+ /**
* Make sure called from the package in charge of visual voicemail.
*
* @throws SecurityException if the caller is not the visual voicemail package.
@@ -8347,6 +8652,45 @@
}
/**
+ * Fetches the carrier restriction status of the device and sends the status to the caller
+ * through the callback.
+ *
+ * @param callback The callback that will be used to send the result.
+ * @throws SecurityException if the caller does not have the required permission/privileges or
+ * the caller is not allowlisted.
+ */
+ @Override
+ public void getCarrierRestrictionStatus(IIntegerConsumer callback, String packageName) {
+ enforceReadPermission("getCarrierRestrictionStatus");
+ int carrierId = validateCallerAndGetCarrierId(packageName);
+ if (carrierId == CarrierAllowListInfo.INVALID_CARRIER_ID) {
+ Rlog.e(LOG_TAG, "getCarrierRestrictionStatus: caller is not registered");
+ throw new SecurityException("Not an authorized caller");
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Consumer<Integer> consumer = FunctionalUtils.ignoreRemoteException(callback::accept);
+ CallerCallbackInfo callbackInfo = new CallerCallbackInfo(consumer, carrierId);
+ sendRequestAsync(CMD_GET_ALLOWED_CARRIERS, callbackInfo);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public List<String> getShaIdFromAllowList(String pkgName, int carrierId) {
+ enforceReadPrivilegedPermission("checkCarrierRestrictionFileForNoChange");
+ CarrierAllowListInfo allowListInfo = CarrierAllowListInfo.loadInstance(mApp);
+ return allowListInfo.getShaIdList(pkgName, carrierId);
+ }
+
+ @VisibleForTesting
+ public int validateCallerAndGetCarrierId(String packageName) {
+ CarrierAllowListInfo allowListInfo = CarrierAllowListInfo.loadInstance(mApp);
+ return allowListInfo.validateCallerAndGetCarrierId(packageName);
+ }
+
+ /**
* Action set from carrier signalling broadcast receivers to enable/disable radio
* @param subId the subscription ID that this action applies to.
* @param enabled control enable or disable radio.
@@ -8500,7 +8844,7 @@
@NonNull String[] args) {
return new TelephonyShellCommand(this, getDefaultPhone().getContext()).exec(
this, in.getFileDescriptor(), out.getFileDescriptor(),
- err.getFileDescriptor(), args);
+ err.getFileDescriptor(), args);
}
/**
@@ -8655,22 +8999,16 @@
}
/**
- * Check if phone is in emergency callback mode
+ * Check if phone is in emergency callback mode.
* @return true if phone is in emergency callback mode
- * @param subId sub id
+ * @param subId sub Id, but the check is in fact irrlevant to sub Id.
*/
@Override
public boolean getEmergencyCallbackMode(int subId) {
enforceReadPrivilegedPermission("getEmergencyCallbackMode");
- final Phone phone = getPhone(subId);
-
final long identity = Binder.clearCallingIdentity();
try {
- if (phone != null) {
- return phone.isInEcm();
- } else {
- return false;
- }
+ return getPhoneFromSubIdOrDefault(subId).isInEcm();
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -8745,11 +9083,11 @@
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE,
functionName);
- } catch (Exception e) {
+ } catch (SecurityException e) {
mApp.enforceCallingOrSelfPermission(
permission.READ_BASIC_PHONE_STATE, functionName);
}
- } catch (Exception e) {
+ } catch (SecurityException e) {
TelephonyPermissions.enforceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
mApp, subId, functionName);
}
@@ -8796,7 +9134,7 @@
public boolean isManualNetworkSelectionAllowed(int subId) {
TelephonyPermissions
.enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
- mApp, subId, "isManualNetworkSelectionAllowed");
+ mApp, subId, "isManualNetworkSelectionAllowed");
boolean isAllowed = true;
final long identity = Binder.clearCallingIdentity();
@@ -8840,7 +9178,7 @@
// even without READ_PRIVILEGED_PHONE_STATE, we allow the call to continue if the caller
// has carrier privileges on an active UICC
if (checkCarrierPrivilegesForPackageAnyPhoneWithPermission(callingPackage)
- != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
throw new SecurityException("Caller does not have permission.");
}
}
@@ -9253,7 +9591,7 @@
public int getCdmaRoamingMode(int subId) {
TelephonyPermissions
.enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
- mApp, subId, "getCdmaRoamingMode");
+ mApp, subId, "getCdmaRoamingMode");
final long identity = Binder.clearCallingIdentity();
try {
@@ -9339,9 +9677,11 @@
final long identity = Binder.clearCallingIdentity();
try {
for (Phone phone: PhoneFactory.getPhones()) {
+ //Note: we ignore passed in param exactMatch. We can remove it once
+ // TelephonyManager#isPotentialEmergencyNumber is removed completely
if (phone.getEmergencyNumberTracker() != null
&& phone.getEmergencyNumberTracker()
- .isEmergencyNumber(number, exactMatch)) {
+ .isEmergencyNumber(number)) {
return true;
}
}
@@ -9726,12 +10066,22 @@
/**
* Get the IRadio HAL Version
+ * @deprecated use getHalVersion instead
*/
+ @Deprecated
@Override
public int getRadioHalVersion() {
+ return getHalVersion(HAL_SERVICE_RADIO);
+ }
+
+ /**
+ * Get the HAL Version of a specific service
+ */
+ @Override
+ public int getHalVersion(int service) {
Phone phone = getDefaultPhone();
if (phone == null) return -1;
- HalVersion hv = phone.getHalVersion();
+ HalVersion hv = phone.getHalVersion(service);
if (hv.equals(HalVersion.UNKNOWN)) return -1;
return hv.major * 100 + hv.minor;
}
@@ -9881,6 +10231,26 @@
}
@Override
+ public void showSwitchToManagedProfileDialog() {
+ enforceModifyPermission();
+ try {
+ // Note: This intent is constructed to ensure that the IntentForwarderActivity is
+ // shown in accordance with the intent filters in DefaultCrossProfileIntentFilterUtils
+ // for work telephony.
+ Intent intent = new Intent(Intent.ACTION_SENDTO);
+ intent.setData(Uri.parse("smsto:"));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mApp.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(LOG_TAG, "Unable to show intent forwarder, try showing error dialog instead");
+ Intent intent = new Intent();
+ intent.setClass(mApp, ErrorDialogActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mApp.startActivity(intent);
+ }
+ }
+
+ @Override
public String getMmsUAProfUrl(int subId) {
//TODO investigate if this API should require proper permission check in R b/133791609
final long identity = Binder.clearCallingIdentity();
@@ -9989,8 +10359,9 @@
throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
}
if (!isImsAvailableOnDevice()) {
- throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
- "IMS not available on device.");
+ // ProvisioningManager can not handle ServiceSpecificException.
+ // Throw the IllegalStateException and annotate ProvisioningManager.
+ throw new IllegalStateException("IMS not available on device.");
}
final long identity = Binder.clearCallingIdentity();
@@ -10194,7 +10565,7 @@
try {
getGbaManager(subId).bootstrapAuthenticationRequest(
new GbaAuthRequest(subId, appType, nafUrl, securityProtocol.toByteArray(),
- forceBootStrapping, callback));
+ forceBootStrapping, callback));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -10215,7 +10586,7 @@
for (int i = 0; i < TelephonyManager.getDefault().getActiveModemCount(); i++) {
Phone phone = PhoneFactory.getPhone(i);
if (phone != null) {
- phone.setRadioPowerForReason(enable, Phone.RADIO_POWER_REASON_THERMAL);
+ phone.setRadioPowerForReason(enable, TelephonyManager.RADIO_POWER_REASON_THERMAL);
isPhoneAvailable = true;
}
}
@@ -10245,7 +10616,7 @@
if (isDataThrottlingSupported) {
int thermalMitigationResult =
- (int) sendRequest(CMD_SET_DATA_THROTTLING, dataThrottlingRequest, subId);
+ (int) sendRequest(CMD_SET_DATA_THROTTLING, dataThrottlingRequest, subId);
if (thermalMitigationResult == SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS) {
throw new IllegalArgumentException("modem returned INVALID_ARGUMENTS");
} else if (thermalMitigationResult
@@ -10280,8 +10651,8 @@
for (Phone phone : PhoneFactory.getPhones()) {
if (phone.isInEmergencySmsMode() || phone.isInEcm()) {
Log.e(LOG_TAG, "Phone state is not valid. isInEmergencySmsMode = "
- + phone.isInEmergencySmsMode() + " isInEmergencyCallbackMode = "
- + phone.isInEcm());
+ + phone.isInEmergencySmsMode() + " isInEmergencyCallbackMode = "
+ + phone.isInEcm());
return true;
}
}
@@ -10342,9 +10713,9 @@
switch (thermalMitigationAction) {
case ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_DATA_THROTTLING:
thermalMitigationResult =
- handleDataThrottlingRequest(subId,
- thermalMitigationRequest.getDataThrottlingRequest(),
- callingPackage);
+ handleDataThrottlingRequest(subId,
+ thermalMitigationRequest.getDataThrottlingRequest(),
+ callingPackage);
break;
case ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_VOICE_ONLY:
if (thermalMitigationRequest.getDataThrottlingRequest() != null) {
@@ -10375,7 +10746,7 @@
Phone phone = getPhone(subId);
if (phone == null) {
thermalMitigationResult =
- TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
+ TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
break;
}
@@ -10388,7 +10759,7 @@
break;
} else if (isAnyPhoneInEmergencyState()) {
thermalMitigationResult =
- TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE;
+ TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE;
break;
}
} else {
@@ -10405,7 +10776,7 @@
break;
}
thermalMitigationResult =
- TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS;
+ TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS;
break;
default:
throw new IllegalArgumentException("the requested thermalMitigationAction does "
@@ -10589,8 +10960,9 @@
throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
}
if (!isImsAvailableOnDevice()) {
- throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
- "IMS not available on device.");
+ // operation failed silently
+ Rlog.w(LOG_TAG, "IMS not available on device.");
+ return;
}
final long identity = Binder.clearCallingIdentity();
@@ -10614,8 +10986,9 @@
throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
}
if (!isImsAvailableOnDevice()) {
- throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
- "IMS not available on device.");
+ // ProvisioningManager can not handle ServiceSpecificException.
+ // Throw the IllegalStateException and annotate ProvisioningManager.
+ throw new IllegalStateException("IMS not available on device.");
}
final long identity = Binder.clearCallingIdentity();
@@ -11113,9 +11486,8 @@
}
for (SignalThresholdInfo info : request.getSignalThresholdInfos()) {
- // Only system caller can set mHysteresisMs/mHysteresisDb/mIsEnabled.
+ // Only system caller can set mHysteresisMs/mIsEnabled.
if (info.getHysteresisMs() != SignalThresholdInfo.HYSTERESIS_MS_DISABLED
- || info.getHysteresisDb() != SignalThresholdInfo.HYSTERESIS_DB_DISABLED
|| info.isEnabled()) {
throw new IllegalArgumentException(
"Only system can set hide fields in SignalThresholdInfo");
@@ -11194,6 +11566,129 @@
}
/**
+ * Check whether the given premium capability is available for purchase from the carrier.
+ *
+ * @param capability The premium capability to check.
+ * @param subId The subId to check the premium capability for.
+ *
+ * @return Whether the given premium capability is available to purchase.
+ */
+ @Override
+ public boolean isPremiumCapabilityAvailableForPurchase(int capability, int subId) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadNonDangerousPhoneStateNoThrow(
+ mApp, "isPremiumCapabilityAvailableForPurchase")) {
+ log("Premium capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability)
+ + " is not available for purchase due to missing permissions.");
+ throw new SecurityException("isPremiumCapabilityAvailableForPurchase requires "
+ + "permission READ_BASIC_PHONE_STATE.");
+ }
+
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ loge("isPremiumCapabilityAvailableForPurchase: phone is null, subId=" + subId);
+ return false;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return SlicePurchaseController.getInstance(phone)
+ .isPremiumCapabilityAvailableForPurchase(capability);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Purchase the given premium capability from the carrier.
+ *
+ * @param capability The premium capability to purchase.
+ * @param callback The result of the purchase request.
+ * @param subId The subId to purchase the premium capability for.
+ */
+ @Override
+ public void purchasePremiumCapability(int capability, IIntegerConsumer callback, int subId) {
+ log("purchasePremiumCapability: capability="
+ + TelephonyManager.convertPremiumCapabilityToString(capability) + ", caller="
+ + getCurrentPackageName());
+
+ if (!TelephonyPermissions.checkCallingOrSelfReadNonDangerousPhoneStateNoThrow(
+ mApp, "purchasePremiumCapability")) {
+ log("purchasePremiumCapability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability)
+ + " failed due to missing permissions.");
+ throw new SecurityException("purchasePremiumCapability requires permission "
+ + "READ_BASIC_PHONE_STATE.");
+ } else if (!TelephonyPermissions.checkInternetPermissionNoThrow(
+ mApp, "purchasePremiumCapability")) {
+ log("purchasePremiumCapability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability)
+ + " failed due to missing permissions.");
+ throw new SecurityException("purchasePremiumCapability requires permission INTERNET.");
+ }
+
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ try {
+ int result = TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED;
+ callback.accept(result);
+ loge("purchasePremiumCapability: phone is null, subId=" + subId);
+ } catch (RemoteException e) {
+ String logStr = "Purchase premium capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability)
+ + " failed due to RemoteException handling null phone: " + e;
+ if (DBG) log(logStr);
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString(PURCHASE_PREMIUM_CAPABILITY_ERROR_UUID), logStr);
+ }
+ return;
+ }
+
+ String callingProcess;
+ try {
+ callingProcess = mApp.getPackageManager().getApplicationInfo(
+ getCurrentPackageName(), 0).processName;
+ } catch (PackageManager.NameNotFoundException e) {
+ callingProcess = getCurrentPackageName();
+ }
+
+ boolean isVisible = false;
+ ActivityManager am = mApp.getSystemService(ActivityManager.class);
+ if (am != null) {
+ List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
+ if (processes != null) {
+ for (ActivityManager.RunningAppProcessInfo process : processes) {
+ log("purchasePremiumCapability: process " + process.processName
+ + " has importance " + process.importance);
+ if (process.processName.equals(callingProcess) && process.importance
+ <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
+ isVisible = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!isVisible) {
+ try {
+ int result = TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND;
+ callback.accept(result);
+ loge("purchasePremiumCapability: " + callingProcess + " is not in the foreground.");
+ } catch (RemoteException e) {
+ String logStr = "Purchase premium capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability)
+ + " failed due to RemoteException handling background application: " + e;
+ if (DBG) log(logStr);
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString(PURCHASE_PREMIUM_CAPABILITY_ERROR_UUID), logStr);
+ }
+ return;
+ }
+
+ sendRequestAsync(CMD_PURCHASE_PREMIUM_CAPABILITY,
+ new PurchasePremiumCapabilityArgument(capability, callback), phone, null);
+ }
+
+ /**
* Register an IMS connection state callback
*/
@Override
@@ -11302,9 +11797,7 @@
final long identity = Binder.clearCallingIdentity();
try {
- Phone phone = getPhone(subId);
- if (phone == null) return null;
- ServiceStateTracker sst = phone.getServiceStateTracker();
+ ServiceStateTracker sst = getPhoneFromSubIdOrDefault(subId).getServiceStateTracker();
if (sst == null) return null;
return sst.getLastKnownCellIdentity();
} finally {
@@ -11322,8 +11815,8 @@
Log.d(LOG_TAG, "setModemService - " + serviceName);
TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setModemService");
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID,
- "setModemService");
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ "setModemService");
return mPhoneConfigurationManager.setModemService(serviceName);
}
@@ -11337,7 +11830,7 @@
Log.d(LOG_TAG, "getModemService");
TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getModemService");
TelephonyPermissions
- .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
mApp, SubscriptionManager.INVALID_SUBSCRIPTION_ID,
"getModemService");
result = mPhoneConfigurationManager.getModemService();
@@ -11356,6 +11849,8 @@
try {
Phone phone = getPhone(subId);
if (phone == null) return;
+ Log.i(LOG_TAG, "setVoiceServiceStateOverride: subId=" + subId + ", phone=" + phone
+ + ", hasService=" + hasService + ", callingPackage=" + callingPackage);
phone.setVoiceServiceStateOverride(hasService);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -11399,6 +11894,87 @@
}
/**
+ * Get the component name of the default app to direct respond-via-message intent for the
+ * user associated with this subscription, update the cache if there is no respond-via-message
+ * application currently configured for this user.
+ * @return component name of the app and class to direct Respond Via Message intent to, or
+ * {@code null} if the functionality is not supported.
+ * @hide
+ */
+ @Override
+ public @Nullable ComponentName getDefaultRespondViaMessageApplication(int subId,
+ boolean updateIfNeeded) {
+ enforceInteractAcrossUsersPermission("getDefaultRespondViaMessageApplication");
+
+ Context context = getPhoneFromSubIdOrDefault(subId).getContext();
+
+ UserHandle userHandle = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ userHandle = TelephonyUtils.getSubscriptionUserHandle(context, subId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return SmsApplication.getDefaultRespondViaMessageApplicationAsUser(context,
+ updateIfNeeded, userHandle);
+ }
+
+ /**
+ * Set whether the device is able to connect with null ciphering or integrity
+ * algorithms. This is a global setting and will apply to all active subscriptions
+ * and all new subscriptions after this.
+ *
+ * @param enabled when true, null cipher and integrity algorithms are allowed.
+ * @hide
+ */
+ @Override
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setNullCipherAndIntegrityEnabled(boolean enabled) {
+ enforceModifyPermission();
+ checkForNullCipherAndIntegritySupport();
+
+ // Persist the state of our preference. Each GsmCdmaPhone instance is responsible
+ // for listening to these preference changes and applying them immediately.
+ SharedPreferences.Editor editor = mTelephonySharedPreferences.edit();
+ editor.putBoolean(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED, enabled);
+ editor.apply();
+
+ for (Phone phone: PhoneFactory.getPhones()) {
+ phone.handleNullCipherEnabledChange();
+ }
+ }
+
+
+ /**
+ * Get whether the device is able to connect with null ciphering or integrity
+ * algorithms. Note that this retrieves the phone-global preference and not
+ * the state of the radio.
+ *
+ * @throws SecurityException if {@link permission#MODIFY_PHONE_STATE} is not satisfied
+ * @throws UnsupportedOperationException if the device does not support the minimum HAL
+ * version for this feature.
+ * @hide
+ */
+ @Override
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public boolean isNullCipherAndIntegrityPreferenceEnabled() {
+ enforceReadPermission();
+ checkForNullCipherAndIntegritySupport();
+ return getDefaultPhone().getNullCipherAndIntegrityEnabledPreference();
+ }
+
+ private void checkForNullCipherAndIntegritySupport() {
+ if (getHalVersion(HAL_SERVICE_NETWORK) < MIN_NULL_CIPHER_AND_INTEGRITY_VERSION) {
+ throw new UnsupportedOperationException(
+ "Null cipher and integrity operations require HAL 2.1 or above");
+ }
+ if (!getDefaultPhone().isNullCipherAndIntegritySupported()) {
+ throw new UnsupportedOperationException(
+ "Null cipher and integrity operations unsupported by modem");
+ }
+ }
+
+ /**
* Get the SIM state for the slot index.
* For Remote-SIMs, this method returns {@link IccCardConstants.State#UNKNOWN}
*
@@ -11430,4 +12006,627 @@
}
return simState.ordinal();
}
+
+ private void persistEmergencyCallDiagnosticDataInternal(@NonNull String dropboxTag,
+ boolean enableLogcat,
+ long logcatStartTimestampMillis, boolean enableTelecomDump,
+ boolean enableTelephonyDump) {
+ DropBoxManager db = mApp.getSystemService(DropBoxManager.class);
+ TelephonyManager.EmergencyCallDiagnosticParams edp =
+ new TelephonyManager.EmergencyCallDiagnosticParams();
+ edp.setLogcatCollection(enableLogcat, logcatStartTimestampMillis);
+ edp.setTelephonyDumpSysCollection(enableTelephonyDump);
+ edp.setTelecomDumpSysCollection(enableTelecomDump);
+ Log.d(LOG_TAG, "persisting with Params " + edp.toString());
+ DiagnosticDataCollector ddc = new DiagnosticDataCollector(Runtime.getRuntime(),
+ Executors.newCachedThreadPool(), db,
+ mApp.getSystemService(ActivityManager.class).isLowRamDevice());
+ ddc.persistEmergencyDianosticData(new DataCollectorConfig.Adapter(), edp, dropboxTag);
+ }
+
+ /**
+ * Request telephony to persist state for debugging emergency call failures.
+ *
+ * @param dropBoxTag Tag to use when persisting data to dropbox service.
+ * @param enableLogcat whether to collect logcat output
+ * @param logcatStartTimestampMillis timestamp from when logcat buffers would be persisted
+ * @param enableTelecomDump whether to collect telecom dumpsys
+ * @param enableTelephonyDump whether to collect telephony dumpsys
+ */
+ @Override
+ @RequiresPermission(android.Manifest.permission.DUMP)
+ public void persistEmergencyCallDiagnosticData(@NonNull String dropboxTag, boolean enableLogcat,
+ long logcatStartTimestampMillis, boolean enableTelecomDump,
+ boolean enableTelephonyDump) {
+ mApp.enforceCallingPermission(android.Manifest.permission.DUMP,
+ "persistEmergencyCallDiagnosticData");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ persistEmergencyCallDiagnosticDataInternal(dropboxTag, enableLogcat,
+ logcatStartTimestampMillis, enableTelecomDump, enableTelephonyDump);
+
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Get current cell broadcast ranges.
+ */
+ @Override
+ @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+ public List<CellBroadcastIdRange> getCellBroadcastIdRanges(int subId) {
+ mApp.enforceCallingPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS,
+ "getCellBroadcastIdRanges");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return getPhone(subId).getCellBroadcastIdRanges();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set reception of cell broadcast messages with the list of the given ranges
+ *
+ * @param ranges the list of {@link CellBroadcastIdRange} to be enabled
+ */
+ @Override
+ @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+ public void setCellBroadcastIdRanges(int subId, @NonNull List<CellBroadcastIdRange> ranges,
+ @Nullable IIntegerConsumer callback) {
+ mApp.enforceCallingPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS,
+ "setCellBroadcastIdRanges");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Phone phone = getPhoneFromSubId(subId);
+ if (DBG) {
+ log("setCellBroadcastIdRanges for subId :" + subId + ", phone:" + phone);
+ }
+ phone.setCellBroadcastIdRanges(ranges, result -> {
+ if (callback != null) {
+ try {
+ callback.accept(result);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "setCellBroadcastIdRanges: callback not available.");
+ }
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Returns whether the device supports the domain selection service.
+ *
+ * @return {@code true} if the device supports the domain selection service.
+ */
+ @Override
+ public boolean isDomainSelectionSupported() {
+ mApp.enforceCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "isDomainSelectionSupported");
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return DomainSelectionResolver.getInstance().isDomainSelectionSupported();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Request to enable or disable the satellite modem and demo mode. If the satellite modem is
+ * enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
+ * this may also re-enable the cellular modem.
+ *
+ * @param subId The subId of the subscription to set satellite enabled for.
+ * @param enableSatellite {@code true} to enable the satellite modem and
+ * {@code false} to disable.
+ * @param enableDemoMode {@code true} to enable demo mode and {@code false} to disable.
+ * @param callback The callback to get the result of the request.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void requestSatelliteEnabled(int subId, boolean enableSatellite, boolean enableDemoMode,
+ @NonNull IIntegerConsumer callback) {
+ enforceSatelliteCommunicationPermission("requestSatelliteEnabled");
+ mSatelliteController.requestSatelliteEnabled(subId, enableSatellite, enableDemoMode,
+ callback);
+ }
+
+ /**
+ * Request to get whether the satellite modem is enabled.
+ *
+ * @param subId The subId of the subscription to check whether satellite is enabled for.
+ * @param result The result receiver that returns whether the satellite modem is enabled
+ * if the request is successful or an error code if the request failed.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void requestIsSatelliteEnabled(int subId, @NonNull ResultReceiver result) {
+ enforceSatelliteCommunicationPermission("requestIsSatelliteEnabled");
+ mSatelliteController.requestIsSatelliteEnabled(subId, result);
+ }
+
+ /**
+ * Request to get whether the satellite service demo mode is enabled.
+ *
+ * @param subId The subId of the subscription to check whether the satellite demo mode
+ * is enabled for.
+ * @param result The result receiver that returns whether the satellite demo mode is enabled
+ * if the request is successful or an error code if the request failed.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void requestIsDemoModeEnabled(int subId, @NonNull ResultReceiver result) {
+ enforceSatelliteCommunicationPermission("requestIsDemoModeEnabled");
+ mSatelliteController.requestIsDemoModeEnabled(subId, result);
+ }
+
+ /**
+ * Request to get whether the satellite service is supported on the device.
+ *
+ * @param subId The subId of the subscription to check satellite service support for.
+ * @param result The result receiver that returns whether the satellite service is supported on
+ * the device if the request is successful or an error code if the request failed.
+ */
+ @Override
+ public void requestIsSatelliteSupported(int subId, @NonNull ResultReceiver result) {
+ mSatelliteController.requestIsSatelliteSupported(subId, result);
+ }
+
+ /**
+ * Request to get the {@link SatelliteCapabilities} of the satellite service.
+ *
+ * @param subId The subId of the subscription to get the satellite capabilities for.
+ * @param result The result receiver that returns the {@link SatelliteCapabilities}
+ * if the request is successful or an error code if the request failed.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ */
+ @Override
+ public void requestSatelliteCapabilities(int subId, @NonNull ResultReceiver result) {
+ enforceSatelliteCommunicationPermission("requestSatelliteCapabilities");
+ mSatelliteController.requestSatelliteCapabilities(subId, result);
+ }
+
+ /**
+ * Start receiving satellite transmission updates.
+ * This can be called by the pointing UI when the user starts pointing to the satellite.
+ * Modem should continue to report the pointing input as the device or satellite moves.
+ *
+ * @param subId The subId of the subscription to start satellite transmission updates for.
+ * @param resultCallback The callback to get the result of the request.
+ * @param callback The callback to notify of satellite transmission updates.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void startSatelliteTransmissionUpdates(int subId,
+ @NonNull IIntegerConsumer resultCallback,
+ @NonNull ISatelliteTransmissionUpdateCallback callback) {
+ enforceSatelliteCommunicationPermission("startSatelliteTransmissionUpdates");
+ mSatelliteController.startSatelliteTransmissionUpdates(subId, resultCallback, callback);
+ }
+
+ /**
+ * Stop receiving satellite transmission updates.
+ * This can be called by the pointing UI when the user stops pointing to the satellite.
+ *
+ * @param subId The subId of the subscription to stop satellite transmission updates for.
+ * @param resultCallback The callback to get the result of the request.
+ * @param callback The callback that was passed to {@link #startSatelliteTransmissionUpdates(
+ * int, IIntegerConsumer, ISatelliteTransmissionUpdateCallback)}.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void stopSatelliteTransmissionUpdates(int subId,
+ @NonNull IIntegerConsumer resultCallback,
+ @NonNull ISatelliteTransmissionUpdateCallback callback) {
+ enforceSatelliteCommunicationPermission("stopSatelliteTransmissionUpdates");
+ mSatelliteController.stopSatelliteTransmissionUpdates(subId, resultCallback, callback);
+ }
+
+ /**
+ * Register the subscription with a satellite provider.
+ * This is needed to register the subscription if the provider allows dynamic registration.
+ *
+ * @param subId The subId of the subscription to be provisioned.
+ * @param token The token to be used as a unique identifier for provisioning with satellite
+ * gateway.
+ * @param provisionData Data from the provisioning app that can be used by provisioning server
+ * @param callback The callback to get the result of the request.
+ *
+ * @return The signal transport used by the caller to cancel the provision request,
+ * or {@code null} if the request failed.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ @Nullable public ICancellationSignal provisionSatelliteService(int subId,
+ @NonNull String token, @NonNull byte[] provisionData,
+ @NonNull IIntegerConsumer callback) {
+ enforceSatelliteCommunicationPermission("provisionSatelliteService");
+ return mSatelliteController.provisionSatelliteService(subId, token, provisionData,
+ callback);
+ }
+
+ /**
+ * Unregister the device/subscription with the satellite provider.
+ * This is needed if the provider allows dynamic registration. Once deprovisioned,
+ * {@link SatelliteProvisionStateCallback#onSatelliteProvisionStateChanged(boolean)}
+ * should report as deprovisioned.
+ *
+ * @param subId The subId of the subscription to be deprovisioned.
+ * @param token The token of the device/subscription to be deprovisioned.
+ * @param callback The callback to get the result of the request.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void deprovisionSatelliteService(int subId,
+ @NonNull String token, @NonNull IIntegerConsumer callback) {
+ enforceSatelliteCommunicationPermission("deprovisionSatelliteService");
+ mSatelliteController.deprovisionSatelliteService(subId, token, callback);
+ }
+
+ /**
+ * Registers for the satellite provision state changed.
+ *
+ * @param subId The subId of the subscription to register for provision state changed.
+ * @param callback The callback to handle the satellite provision state changed event.
+ *
+ * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ @SatelliteManager.SatelliteError public int registerForSatelliteProvisionStateChanged(int subId,
+ @NonNull ISatelliteProvisionStateCallback callback) {
+ enforceSatelliteCommunicationPermission("registerForSatelliteProvisionStateChanged");
+ return mSatelliteController.registerForSatelliteProvisionStateChanged(subId, callback);
+ }
+
+ /**
+ * Unregisters for the satellite provision state changed.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param subId The subId of the subscription to unregister for provision state changed.
+ * @param callback The callback that was passed to
+ * {@link #registerForSatelliteProvisionStateChanged(int, ISatelliteProvisionStateCallback)}.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void unregisterForSatelliteProvisionStateChanged(
+ int subId, @NonNull ISatelliteProvisionStateCallback callback) {
+ enforceSatelliteCommunicationPermission("unregisterForSatelliteProvisionStateChanged");
+ mSatelliteController.unregisterForSatelliteProvisionStateChanged(subId, callback);
+ }
+
+ /**
+ * Request to get whether the device is provisioned with a satellite provider.
+ *
+ * @param subId The subId of the subscription to get whether the device is provisioned for.
+ * @param result The result receiver that returns whether the device is provisioned with a
+ * satellite provider if the request is successful or an error code if the
+ * request failed.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void requestIsSatelliteProvisioned(int subId, @NonNull ResultReceiver result) {
+ enforceSatelliteCommunicationPermission("requestIsSatelliteProvisioned");
+ mSatelliteController.requestIsSatelliteProvisioned(subId, result);
+ }
+
+ /**
+ * Registers for modem state changed from satellite modem.
+ *
+ * @param subId The subId of the subscription to register for satellite modem state changed.
+ * @param callback The callback to handle the satellite modem state changed event.
+ *
+ * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ @SatelliteManager.SatelliteError public int registerForSatelliteModemStateChanged(int subId,
+ @NonNull ISatelliteStateCallback callback) {
+ enforceSatelliteCommunicationPermission("registerForSatelliteModemStateChanged");
+ return mSatelliteController.registerForSatelliteModemStateChanged(subId, callback);
+ }
+
+ /**
+ * Unregisters for modem state changed from satellite modem.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param subId The subId of the subscription to unregister for satellite modem state changed.
+ * @param callback The callback that was passed to
+ * {@link #registerForSatelliteModemStateChanged(int, ISatelliteStateCallback)}.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void unregisterForSatelliteModemStateChanged(int subId,
+ @NonNull ISatelliteStateCallback callback) {
+ enforceSatelliteCommunicationPermission("unregisterForSatelliteModemStateChanged");
+ mSatelliteController.unregisterForSatelliteModemStateChanged(subId, callback);
+ }
+
+ /**
+ * Register to receive incoming datagrams over satellite.
+ *
+ * @param subId The subId of the subscription to register for incoming satellite datagrams.
+ * @param callback The callback to handle incoming datagrams over satellite.
+ *
+ * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ @SatelliteManager.SatelliteError public int registerForSatelliteDatagram(int subId,
+ @NonNull ISatelliteDatagramCallback callback) {
+ enforceSatelliteCommunicationPermission("registerForSatelliteDatagram");
+ return mSatelliteController.registerForSatelliteDatagram(subId, callback);
+ }
+
+ /**
+ * Unregister to stop receiving incoming datagrams over satellite.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param subId The subId of the subscription to unregister for incoming satellite datagrams.
+ * @param callback The callback that was passed to
+ * {@link #registerForSatelliteDatagram(int, ISatelliteDatagramCallback)}.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void unregisterForSatelliteDatagram(int subId,
+ @NonNull ISatelliteDatagramCallback callback) {
+ enforceSatelliteCommunicationPermission("unregisterForSatelliteDatagram");
+ mSatelliteController.unregisterForSatelliteDatagram(subId, callback);
+ }
+
+ /**
+ * Poll pending satellite datagrams over satellite.
+ *
+ * This method requests modem to check if there are any pending datagrams to be received over
+ * satellite. If there are any incoming datagrams, they will be received via
+ * {@link SatelliteDatagramCallback#onSatelliteDatagramReceived(long, SatelliteDatagram, int, Consumer)})}
+ *
+ * @param subId The subId of the subscription used for receiving datagrams.
+ * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ */
+ @Override
+ public void pollPendingSatelliteDatagrams(int subId, IIntegerConsumer callback) {
+ enforceSatelliteCommunicationPermission("pollPendingSatelliteDatagrams");
+ mSatelliteController.pollPendingSatelliteDatagrams(subId, callback);
+ }
+
+ /**
+ * Send datagram over satellite.
+ *
+ * Gateway encodes SOS message or location sharing message into a datagram and passes it as
+ * input to this method. Datagram received here will be passed down to modem without any
+ * encoding or encryption.
+ *
+ * @param subId The subId of the subscription to send satellite datagrams for.
+ * @param datagramType datagram type indicating whether the datagram is of type
+ * SOS_SMS or LOCATION_SHARING.
+ * @param datagram encoded gateway datagram which is encrypted by the caller.
+ * Datagram will be passed down to modem without any encoding or encryption.
+ * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
+ * full screen mode.
+ * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ */
+ @Override
+ public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
+ @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
+ @NonNull IIntegerConsumer callback) {
+ enforceSatelliteCommunicationPermission("sendSatelliteDatagram");
+ mSatelliteController.sendSatelliteDatagram(subId, datagramType, datagram,
+ needFullScreenPointingUI, callback);
+ }
+
+ /**
+ * Request to get whether satellite communication is allowed for the current location.
+ *
+ * @param subId The subId of the subscription to check whether satellite communication is
+ * allowed for the current location for.
+ * @param result The result receiver that returns whether satellite communication is allowed
+ * for the current location if the request is successful or an error code
+ * if the request failed.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void requestIsSatelliteCommunicationAllowedForCurrentLocation(int subId,
+ @NonNull ResultReceiver result) {
+ enforceSatelliteCommunicationPermission(
+ "requestIsSatelliteCommunicationAllowedForCurrentLocation");
+ mSatelliteController.requestIsSatelliteCommunicationAllowedForCurrentLocation(subId,
+ result);
+ }
+
+ /**
+ * Request to get the time after which the satellite will be visible.
+ *
+ * @param subId The subId to get the time after which the satellite will be visible for.
+ * @param result The result receiver that returns the time after which the satellite will
+ * be visible if the request is successful or an error code if the request failed.
+ *
+ * @throws SecurityException if the caller doesn't have the required permission.
+ */
+ @Override
+ public void requestTimeForNextSatelliteVisibility(int subId, @NonNull ResultReceiver result) {
+ enforceSatelliteCommunicationPermission("requestTimeForNextSatelliteVisibility");
+ mSatelliteController.requestTimeForNextSatelliteVisibility(subId, result);
+ }
+
+ /**
+ * Inform that Device is aligned to satellite for demo mode.
+ *
+ * @param subId The subId to get the time after which the satellite will be visible for.
+ * @param isAligned {@code true} Device is aligned with the satellite for demo mode
+ * {@code false} Device fails to align with the satellite for demo mode.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+
+ public void onDeviceAlignedWithSatellite(int subId, @NonNull boolean isAligned) {
+ enforceSatelliteCommunicationPermission("informDeviceAlignedToSatellite");
+ mSatelliteController.onDeviceAlignedWithSatellite(subId, isAligned);
+ }
+
+ /**
+ * This API can be used by only CTS to update satellite vendor service package name.
+ *
+ * @param servicePackageName The package name of the satellite vendor service.
+ * @return {@code true} if the satellite vendor service is set successfully,
+ * {@code false} otherwise.
+ */
+ public boolean setSatelliteServicePackageName(String servicePackageName) {
+ Log.d(LOG_TAG, "setSatelliteServicePackageName - " + servicePackageName);
+ TelephonyPermissions.enforceShellOnly(
+ Binder.getCallingUid(), "setSatelliteServicePackageName");
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ "setSatelliteServicePackageName");
+ return mSatelliteController.setSatelliteServicePackageName(servicePackageName);
+ }
+
+ /**
+ * This API can be used by only CTS to update satellite gateway service package name.
+ *
+ * @param servicePackageName The package name of the satellite gateway service.
+ * @return {@code true} if the satellite gateway service is set successfully,
+ * {@code false} otherwise.
+ */
+ public boolean setSatelliteGatewayServicePackageName(@Nullable String servicePackageName) {
+ Log.d(LOG_TAG, "setSatelliteGatewayServicePackageName - " + servicePackageName);
+ TelephonyPermissions.enforceShellOnly(
+ Binder.getCallingUid(), "setSatelliteGatewayServicePackageName");
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ "setSatelliteGatewayServicePackageName");
+ return mSatelliteController.setSatelliteGatewayServicePackageName(servicePackageName);
+ }
+
+ /**
+ * This API can be used by only CTS to update satellite pointing UI app package and class names.
+ *
+ * @param packageName The package name of the satellite pointing UI app.
+ * @param className The class name of the satellite pointing UI app.
+ * @return {@code true} if the satellite pointing UI app package and class is set successfully,
+ * {@code false} otherwise.
+ */
+ public boolean setSatellitePointingUiClassName(
+ @Nullable String packageName, @Nullable String className) {
+ Log.d(LOG_TAG, "setSatellitePointingUiClassName: packageName=" + packageName
+ + ", className=" + className);
+ TelephonyPermissions.enforceShellOnly(
+ Binder.getCallingUid(), "setSatellitePointingUiClassName");
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ "setSatelliteGatewayServicePackageName");
+ return mSatelliteController.setSatellitePointingUiClassName(packageName, className);
+ }
+
+ /**
+ * This API can be used by only CTS to update the timeout duration in milliseconds that
+ * satellite should stay at listening mode to wait for the next incoming page before disabling
+ * listening mode.
+ *
+ * @param timeoutMillis The timeout duration in millisecond.
+ * @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
+ */
+ public boolean setSatelliteListeningTimeoutDuration(long timeoutMillis) {
+ Log.d(LOG_TAG, "setSatelliteListeningTimeoutDuration - " + timeoutMillis);
+ TelephonyPermissions.enforceShellOnly(
+ Binder.getCallingUid(), "setSatelliteListeningTimeoutDuration");
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ "setSatelliteListeningTimeoutDuration");
+ return mSatelliteController.setSatelliteListeningTimeoutDuration(timeoutMillis);
+ }
+
+ /**
+ * This API can be used by only CTS to update the timeout duration in milliseconds whether
+ * the device is aligned with the satellite for demo mode
+ *
+ * @param timeoutMillis The timeout duration in millisecond.
+ * @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
+ */
+ public boolean setSatelliteDeviceAlignedTimeoutDuration(long timeoutMillis) {
+ Log.d(LOG_TAG, "setDeviceAlignedTimeoutDuration - " + timeoutMillis);
+ TelephonyPermissions.enforceShellOnly(
+ Binder.getCallingUid(), "setDeviceAlignedTimeoutDuration");
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ "setDeviceAlignedTimeoutDuration");
+ return mSatelliteController.setSatelliteDeviceAlignedTimeoutDuration(timeoutMillis);
+ }
+
+ /**
+ * Check whether the caller (or self, if not processing an IPC) can read device identifiers.
+ *
+ * <p>This method behaves in one of the following ways:
+ * <ul>
+ * <li>return true : if the calling package has the appop permission {@link
+ * Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} in the manifest </>
+ * <li>return true : if any one subscription has the READ_PRIVILEGED_PHONE_STATE
+ * permission, the calling package passes a DevicePolicyManager Device Owner / Profile
+ * Owner device identifier access check, or the calling package has carrier privileges</>
+ * <li>throw SecurityException: if the caller does not meet any of the requirements.
+ * </ul>
+ */
+ private static boolean checkCallingOrSelfReadDeviceIdentifiersForAnySub(Context context,
+ String callingPackage, @Nullable String callingFeatureId, String message) {
+ for (Phone phone : PhoneFactory.getPhones()) {
+ if (TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(context,
+ phone.getSubId(), callingPackage, callingFeatureId, message)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return The subscription manager service instance.
+ */
+ public SubscriptionManagerService getSubscriptionManagerService() {
+ return SubscriptionManagerService.getInstance();
+ }
+
+ /**
+ * Class binds the consumer[callback] and carrierId.
+ */
+ private static class CallerCallbackInfo {
+ private final Consumer<Integer> mConsumer;
+ private final int mCarrierId;
+
+ public CallerCallbackInfo(Consumer<Integer> consumer, int carrierId) {
+ mConsumer = consumer;
+ mCarrierId = carrierId;
+ }
+
+ public Consumer<Integer> getConsumer() {
+ return mConsumer;
+ }
+
+ public int getCarrierId() {
+ return mCarrierId;
+ }
+ }
}
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index d0aad4a..4826d2b 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -31,6 +31,7 @@
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
+import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.telecom.PhoneAccount;
@@ -701,31 +702,28 @@
Log.d(LOG_TAG, msg);
}
- public static PhoneAccountHandle makePstnPhoneAccountHandle(String id) {
- return makePstnPhoneAccountHandleWithPrefix(id, "", false);
- }
-
- public static PhoneAccountHandle makePstnPhoneAccountHandle(int phoneId) {
- return makePstnPhoneAccountHandle(PhoneFactory.getPhone(phoneId));
- }
-
public static PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) {
- return makePstnPhoneAccountHandleWithPrefix(phone, "", false);
+ return makePstnPhoneAccountHandleWithPrefix(phone, "",
+ false, phone.getUserHandle());
}
public static PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(
- Phone phone, String prefix, boolean isEmergency) {
+ Phone phone, String prefix, boolean isEmergency, UserHandle userHandle) {
// TODO: Should use some sort of special hidden flag to decorate this account as
// an emergency-only account
String id = isEmergency ? EMERGENCY_ACCOUNT_HANDLE_ID : prefix +
String.valueOf(phone.getSubId());
- return makePstnPhoneAccountHandleWithPrefix(id, prefix, isEmergency);
+ return makePstnPhoneAccountHandleWithId(id, userHandle);
}
- public static PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(
- String id, String prefix, boolean isEmergency) {
+ public static PhoneAccountHandle makePstnPhoneAccountHandleWithId(
+ String id, UserHandle userHandle) {
ComponentName pstnConnectionServiceName = getPstnConnectionServiceName();
- return new PhoneAccountHandle(pstnConnectionServiceName, id);
+ // If user handle is null, resort to default constructor to use phone process's
+ // user handle
+ return userHandle == null
+ ? new PhoneAccountHandle(pstnConnectionServiceName, id)
+ : new PhoneAccountHandle(pstnConnectionServiceName, id, userHandle);
}
public static int getSubIdForPhoneAccount(PhoneAccount phoneAccount) {
diff --git a/src/com/android/phone/SubscriptionInfoHelper.java b/src/com/android/phone/SubscriptionInfoHelper.java
index 14faebc..f964f82 100644
--- a/src/com/android/phone/SubscriptionInfoHelper.java
+++ b/src/com/android/phone/SubscriptionInfoHelper.java
@@ -55,8 +55,8 @@
*/
public SubscriptionInfoHelper(Context context, Intent intent) {
mContext = context;
- PhoneAccountHandle phoneAccountHandle =
- intent.getParcelableExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+ PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
+ TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE, PhoneAccountHandle.class);
if (phoneAccountHandle != null) {
mSubId = PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle);
}
diff --git a/src/com/android/phone/Telephony2gUpdater.java b/src/com/android/phone/Telephony2gUpdater.java
deleted file mode 100644
index baaa684..0000000
--- a/src/com/android/phone/Telephony2gUpdater.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2022 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.phone;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.UserManager;
-import android.telephony.RadioAccessFamily;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.RILConstants;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
-/**
- * A {@link BroadcastReceiver} that ensures that user restrictions are correctly applied to
- * telephony.
- * This includes handling broadcasts from user restriction state changes, as well as ensuring that
- * SIM-specific settings are correctly applied when new subscriptions become active.
- *
- * <p>
- * Callers are expected to call {@code init()} and keep an instance of this class alive.
- * </p>
- */
-public class Telephony2gUpdater extends BroadcastReceiver {
- private static final String TAG = "Telephony2gUpdater";
-
- // We can't interact with the HAL on the main thread of the phone process (where
- // receivers are run by default), so we execute our logic from a separate thread.
- // The correctness of this implementation relies heavily on this executor ensuring
- // tasks are serially executed i.e. ExecutorService.newSingleThreadExecutor()
- private final Executor mExecutor;
- private final Context mContext;
- private final long mBaseAllowedNetworks;
-
- private UserManager mUserManager;
- private TelephonyManager mTelephonyManager;
- private SubscriptionManager mSubscriptionManager;
-
- // The current subscription ids
- // Ensure this value is never accessed concurrently
- private Set<Integer> mCurrentSubscriptions;
- // We keep track of the last value to avoid updating when unrelated user restrictions change
- // Ensure this value is never accessed concurrently
- private boolean mDisallowCellular2gRestriction;
-
- public Telephony2gUpdater(Context context) {
- this(Executors.newSingleThreadExecutor(), context,
- RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE));
- }
-
- @VisibleForTesting
- public Telephony2gUpdater(Executor executor, Context context, long baseAllowedNetworks) {
- mExecutor = executor;
- mContext = context;
- mBaseAllowedNetworks = baseAllowedNetworks;
-
- mUserManager = mContext.getSystemService(UserManager.class);
- mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
- mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
-
- // All user restrictions are false by default
- mDisallowCellular2gRestriction = false;
- mCurrentSubscriptions = new HashSet<>();
- }
-
- /**
- * Register the given instance as a {@link BroadcastReceiver} and a {@link
- * SubscriptionManager.OnSubscriptionsChangedListener}.
- */
- public void init() {
- mContext.getSystemService(SubscriptionManager.class).addOnSubscriptionsChangedListener(
- mExecutor, new SubscriptionListener());
- IntentFilter filter = new IntentFilter();
- filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
- mContext.registerReceiver(this, filter);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (context == null || intent == null) return;
- Log.i(TAG, "Received callback for action " + intent.getAction());
- final PendingResult result = goAsync();
- mExecutor.execute(() -> {
- boolean disallow2g = mUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G);
- if (mDisallowCellular2gRestriction == disallow2g) {
- Log.i(TAG, "No update to DISALLOW_CELLULAR_2G restriction.");
- return;
- }
-
- mDisallowCellular2gRestriction = disallow2g;
-
- Log.i(TAG, "Running handler for all subscriptions based on DISALLOW_CELLULAR_2G change."
- + " Restriction value: " + mDisallowCellular2gRestriction);
- handleUserRestrictionsChanged(mCurrentSubscriptions);
- if (result != null) {
- result.finish();
- }
- });
- }
-
- /**
- * Update subscriptions with allowed network types depending on the current state
- * of the {@link UserManager#DISALLOW_CELLULAR_2G}.
- *
- * @param subIds A list of subIds to update.
- */
- private void handleUserRestrictionsChanged(Collection<Integer> subIds) {
- final long twoGBitmask = TelephonyManager.NETWORK_CLASS_BITMASK_2G;
-
- long allowedNetworkTypes = mBaseAllowedNetworks;
-
- // 2G device admin controls are global
- for (Integer subId : subIds) {
- TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(subId);
- if (mDisallowCellular2gRestriction) {
- Log.i(TAG, "Disabling 2g based on user restriction for subId: " + subId);
- allowedNetworkTypes &= ~twoGBitmask;
- } else {
- Log.i(TAG, "Enabling 2g based on user restriction for subId: " + subId);
- allowedNetworkTypes |= twoGBitmask;
- }
- telephonyManager.setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS,
- allowedNetworkTypes);
- }
- }
-
- private class SubscriptionListener extends SubscriptionManager.OnSubscriptionsChangedListener {
- @Override
- public void onSubscriptionsChanged() {
- // Note that this entire callback gets invoked in the single threaded executor
- List<SubscriptionInfo> allSubscriptions =
- mSubscriptionManager.getCompleteActiveSubscriptionInfoList();
-
- HashSet<Integer> updatedSubIds = new HashSet<>(allSubscriptions.size());
- List<Integer> newSubIds = new ArrayList<>();
-
- for (SubscriptionInfo info : allSubscriptions) {
- updatedSubIds.add(info.getSubscriptionId());
- if (!mCurrentSubscriptions.contains(info.getSubscriptionId())) {
- newSubIds.add(info.getSubscriptionId());
- }
- }
-
- mCurrentSubscriptions = updatedSubIds;
-
- if (newSubIds.isEmpty()) {
- Log.d(TAG, "No new subIds. Skipping update.");
- return;
- }
-
- Log.i(TAG, "New subscriptions found. Running handler to update 2g restrictions with "
- + "subIds " + newSubIds.toString());
- handleUserRestrictionsChanged(newSubIds);
- }
- }
-
-}
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 70912c1..498e1ea 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -57,6 +57,8 @@
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.modules.utils.BasicShellCommandHandler;
import com.android.phone.callcomposer.CallComposerPictureManager;
+import com.android.phone.euicc.EuiccUiDispatcherActivity;
+import com.android.phone.utils.CarrierAllowListInfo;
import java.io.IOException;
import java.io.PrintWriter;
@@ -64,6 +66,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
@@ -96,10 +99,12 @@
private static final String ENABLE = "enable";
private static final String DISABLE = "disable";
private static final String QUERY = "query";
+ private static final String CARRIER_RESTRICTION_STATUS_TEST = "carrier_restriction_status_test";
private static final String SET_CARRIER_SERVICE_PACKAGE_OVERRIDE =
"set-carrier-service-package-override";
private static final String CLEAR_CARRIER_SERVICE_PACKAGE_OVERRIDE =
"clear-carrier-service-package-override";
+ private final String QUOTES = "\"";
private static final String CALL_COMPOSER_TEST_MODE = "test-mode";
private static final String CALL_COMPOSER_SIMULATE_CALL = "simulate-outgoing-call";
@@ -121,6 +126,9 @@
private static final String CC_SET_VALUES_FROM_XML = "set-values-from-xml";
private static final String CC_CLEAR_VALUES = "clear-values";
+ private static final String EUICC_SUBCOMMAND = "euicc";
+ private static final String EUICC_SET_UI_COMPONENT = "set-euicc-uicomponent";
+
private static final String GBA_SUBCOMMAND = "gba";
private static final String GBA_SET_SERVICE = "set-service";
private static final String GBA_GET_SERVICE = "get-service";
@@ -171,6 +179,23 @@
private static final String THERMAL_MITIGATION_COMMAND = "thermal-mitigation";
private static final String ALLOW_THERMAL_MITIGATION_PACKAGE_SUBCOMMAND = "allow-package";
private static final String DISALLOW_THERMAL_MITIGATION_PACKAGE_SUBCOMMAND = "disallow-package";
+ private static final String SET_SATELLITE_SERVICE_PACKAGE_NAME =
+ "set-satellite-service-package-name";
+ private static final String SET_SATELLITE_GATEWAY_SERVICE_PACKAGE_NAME =
+ "set-satellite-gateway-service-package-name";
+ private static final String SET_SATELLITE_LISTENING_TIMEOUT_DURATION =
+ "set-satellite-listening-timeout-duration";
+ private static final String SET_SATELLITE_POINTING_UI_CLASS_NAME =
+ "set-satellite-pointing-ui-class-name";
+ private static final String SET_SATELLITE_DEVICE_ALIGNED_TIMEOUT_DURATION =
+ "set-satellite-device-aligned-timeout-duration";
+
+ private static final String INVALID_ENTRY_ERROR = "An emergency number (only allow '0'-'9', "
+ + "'*', '#' or '+') needs to be specified after -a in the command ";
+
+ private static final int[] ROUTING_TYPES = {EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL};
private static final String GET_ALLOWED_NETWORK_TYPES_FOR_USER =
"get-allowed-network-types-for-users";
@@ -306,6 +331,8 @@
return handleDataTestModeCommand();
case END_BLOCK_SUPPRESSION:
return handleEndBlockSuppressionCommand();
+ case EUICC_SUBCOMMAND:
+ return handleEuiccCommand();
case GBA_SUBCOMMAND:
return handleGbaCommand();
case D2D_SUBCOMMAND:
@@ -337,10 +364,22 @@
return handleGetSimSlotsMapping();
case RADIO_SUBCOMMAND:
return handleRadioCommand();
+ case CARRIER_RESTRICTION_STATUS_TEST:
+ return handleCarrierRestrictionStatusCommand();
case SET_CARRIER_SERVICE_PACKAGE_OVERRIDE:
return setCarrierServicePackageOverride();
case CLEAR_CARRIER_SERVICE_PACKAGE_OVERRIDE:
return clearCarrierServicePackageOverride();
+ case SET_SATELLITE_SERVICE_PACKAGE_NAME:
+ return handleSetSatelliteServicePackageNameCommand();
+ case SET_SATELLITE_GATEWAY_SERVICE_PACKAGE_NAME:
+ return handleSetSatelliteGatewayServicePackageNameCommand();
+ case SET_SATELLITE_LISTENING_TIMEOUT_DURATION:
+ return handleSetSatelliteListeningTimeoutDuration();
+ case SET_SATELLITE_POINTING_UI_CLASS_NAME:
+ return handleSetSatellitePointingUiClassNameCommand();
+ case SET_SATELLITE_DEVICE_ALIGNED_TIMEOUT_DURATION:
+ return handleSettSatelliteDeviceAlignedTimeoutDuration();
default: {
return handleDefaultCommands(cmd);
}
@@ -394,6 +433,7 @@
onHelpAllowedNetworkTypes();
onHelpRadio();
onHelpImei();
+ onHelpSatellite();
}
private void onHelpD2D() {
@@ -603,6 +643,15 @@
pw.println(" is specified, it will choose the default voice SIM slot.");
}
+ private void onHelpEuicc() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Euicc Commands:");
+ pw.println(" euicc set-euicc-uicomponent COMPONENT_NAME PACKAGE_NAME");
+ pw.println(" Sets the Euicc Ui-Component which handles EuiccService Actions.");
+ pw.println(" COMPONENT_NAME: The component name which handles UI Actions.");
+ pw.println(" PACKAGE_NAME: THe package name in which ui component belongs.");
+ }
+
private void onHelpGba() {
PrintWriter pw = getOutPrintWriter();
pw.println("Gba Commands:");
@@ -705,6 +754,33 @@
pw.println(" the result would be 'unknown'.");
}
+ private void onHelpSatellite() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Satellite Commands:");
+ pw.println(" set-satellite-service-package-name [-s SERVICE_PACKAGE_NAME]");
+ pw.println(" Sets the package name of satellite service defined in");
+ pw.println(" SERVICE_PACKAGE_NAME to be bound. Options are:");
+ pw.println(" -s: the satellite service package name that Telephony will bind to.");
+ pw.println(" If no option is specified, it will bind to the default.");
+ pw.println(" set-satellite-gateway-service-package-name [-s SERVICE_PACKAGE_NAME]");
+ pw.println(" Sets the package name of satellite gateway service defined in");
+ pw.println(" SERVICE_PACKAGE_NAME to be bound. Options are:");
+ pw.println(" -s: the satellite gateway service package name that Telephony will bind");
+ pw.println(" to. If no option is specified, it will bind to the default.");
+ pw.println(" set-satellite-listening-timeout-duration [-t TIMEOUT_MILLIS]");
+ pw.println(" Sets the timeout duration in millis that satellite will stay at listening");
+ pw.println(" mode. Options are:");
+ pw.println(" -t: the timeout duration in milliseconds.");
+ pw.println(" If no option is specified, it will use the default values.");
+ pw.println(" set-satellite-pointing-ui-class-name [-p PACKAGE_NAME -c CLASS_NAME]");
+ pw.println(" Sets the package and class name of satellite pointing UI app defined in");
+ pw.println(" PACKAGE_NAME and CLASS_NAME to be launched. Options are:");
+ pw.println(" -p: the satellite pointing UI app package name that Telephony will");
+ pw.println(" launch. If no option is specified, it will launch the default.");
+ pw.println(" -c: the satellite pointing UI app class name that Telephony will");
+ pw.println(" launch.");
+ }
+
private void onHelpImei() {
PrintWriter pw = getOutPrintWriter();
pw.println("IMEI Commands:");
@@ -793,6 +869,24 @@
return 0;
}
+ private void removeEmergencyNumberTestMode(String emergencyNumber) {
+ PrintWriter errPw = getErrPrintWriter();
+ for (int routingType : ROUTING_TYPES) {
+ try {
+ mInterface.updateEmergencyNumberListTestMode(
+ EmergencyNumberTracker.REMOVE_EMERGENCY_NUMBER_TEST_MODE,
+ new EmergencyNumber(emergencyNumber, "", "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
+ routingType));
+ } catch (RemoteException ex) {
+ Log.w(LOG_TAG, "emergency-number-test-mode " + "error " + ex.getMessage());
+ errPw.println("Exception: " + ex.getMessage());
+ }
+ }
+ }
+
private int handleEmergencyNumberTestModeCommand() {
PrintWriter errPw = getErrPrintWriter();
String opt = getNextOption();
@@ -800,26 +894,52 @@
onHelpEmergencyNumber();
return 0;
}
-
switch (opt) {
case "-a": {
String emergencyNumberCmd = getNextArgRequired();
- if (emergencyNumberCmd == null
- || !EmergencyNumber.validateEmergencyNumberAddress(emergencyNumberCmd)) {
- errPw.println("An emergency number (only allow '0'-'9', '*', '#' or '+') needs"
- + " to be specified after -a in the command ");
+ if (emergencyNumberCmd == null){
+ errPw.println(INVALID_ENTRY_ERROR);
return -1;
}
+ String[] params = emergencyNumberCmd.split(":");
+ String emergencyNumber;
+ if (params[0] == null ||
+ !EmergencyNumber.validateEmergencyNumberAddress(params[0])){
+ errPw.println(INVALID_ENTRY_ERROR);
+ return -1;
+ } else {
+ emergencyNumber = params[0];
+ }
+ removeEmergencyNumberTestMode(emergencyNumber);
+ int emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+ if (params.length > 1) {
+ switch (params[1].toLowerCase(Locale.ROOT)) {
+ case "emergency":
+ emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY;
+ break;
+ case "normal":
+ emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL;
+ break;
+ case "unknown":
+ break;
+ default:
+ errPw.println("\"" + params[1] + "\" is not a valid specification for "
+ + "emergency call routing. Please enter either \"normal\", "
+ + "\"unknown\", or \"emergency\" for call routing. "
+ + "(-a 1234:normal)");
+ return -1;
+ }
+ }
try {
mInterface.updateEmergencyNumberListTestMode(
EmergencyNumberTracker.ADD_EMERGENCY_NUMBER_TEST_MODE,
- new EmergencyNumber(emergencyNumberCmd, "", "",
+ new EmergencyNumber(emergencyNumber, "", "",
EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
new ArrayList<String>(),
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
- EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+ emergencyCallRouting));
} catch (RemoteException ex) {
- Log.w(LOG_TAG, "emergency-number-test-mode -a " + emergencyNumberCmd
+ Log.w(LOG_TAG, "emergency-number-test-mode -a " + emergencyNumber
+ ", error " + ex.getMessage());
errPw.println("Exception: " + ex.getMessage());
return -1;
@@ -845,20 +965,7 @@
+ " to be specified after -r in the command ");
return -1;
}
- try {
- mInterface.updateEmergencyNumberListTestMode(
- EmergencyNumberTracker.REMOVE_EMERGENCY_NUMBER_TEST_MODE,
- new EmergencyNumber(emergencyNumberCmd, "", "",
- EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
- new ArrayList<String>(),
- EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
- EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
- } catch (RemoteException ex) {
- Log.w(LOG_TAG, "emergency-number-test-mode -r " + emergencyNumberCmd
- + ", error " + ex.getMessage());
- errPw.println("Exception: " + ex.getMessage());
- return -1;
- }
+ removeEmergencyNumberTestMode(emergencyNumberCmd);
break;
}
case "-p": {
@@ -1147,7 +1254,7 @@
return 0;
}
- boolean isEnabled = "true".equals(arg.toLowerCase());
+ boolean isEnabled = "true".equals(arg.toLowerCase(Locale.ROOT));
try {
mInterface.setDeviceToDeviceForceEnabled(isEnabled);
} catch (RemoteException e) {
@@ -2022,6 +2129,35 @@
return 0;
}
+ private int handleEuiccCommand() {
+ String arg = getNextArg();
+ if (arg == null) {
+ onHelpEuicc();
+ return 0;
+ }
+
+ switch (arg) {
+ case EUICC_SET_UI_COMPONENT: {
+ return handleEuiccServiceCommand();
+ }
+ }
+ return -1;
+ }
+
+ private int handleEuiccServiceCommand() {
+ String uiComponent = getNextArg();
+ String packageName = getNextArg();
+ if (packageName == null || uiComponent == null) {
+ return -1;
+ }
+ EuiccUiDispatcherActivity.setTestEuiccUiComponent(packageName, uiComponent);
+ if (VDBG) {
+ Log.v(LOG_TAG, "euicc set-euicc-uicomponent " + uiComponent +" "
+ + packageName);
+ }
+ return 0;
+ }
+
private int handleRestartModemCommand() {
// Verify that the user is allowed to run the command. Only allowed in rooted device in a
// non user build.
@@ -2981,6 +3117,212 @@
return -1;
}
+ private int handleSetSatelliteServicePackageNameCommand() {
+ PrintWriter errPw = getErrPrintWriter();
+ String serviceName = null;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-s": {
+ serviceName = getNextArgRequired();
+ break;
+ }
+ }
+ }
+ Log.d(LOG_TAG, "handleSetSatelliteServicePackageNameCommand: serviceName="
+ + serviceName);
+
+ try {
+ boolean result = mInterface.setSatelliteServicePackageName(serviceName);
+ if (VDBG) {
+ Log.v(LOG_TAG, "SetSatelliteServicePackageName " + serviceName
+ + ", result = " + result);
+ }
+ getOutPrintWriter().println(result);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "SetSatelliteServicePackageName: " + serviceName
+ + ", error = " + e.getMessage());
+ errPw.println("Exception: " + e.getMessage());
+ return -1;
+ }
+ return 0;
+ }
+
+ private int handleSetSatelliteGatewayServicePackageNameCommand() {
+ PrintWriter errPw = getErrPrintWriter();
+ String serviceName = null;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-s": {
+ serviceName = getNextArgRequired();
+ break;
+ }
+ }
+ }
+ Log.d(LOG_TAG, "handleSetSatelliteGatewayServicePackageNameCommand: serviceName="
+ + serviceName);
+
+ try {
+ boolean result = mInterface.setSatelliteGatewayServicePackageName(serviceName);
+ if (VDBG) {
+ Log.v(LOG_TAG, "setSatelliteGatewayServicePackageName " + serviceName
+ + ", result = " + result);
+ }
+ getOutPrintWriter().println(result);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "setSatelliteGatewayServicePackageName: " + serviceName
+ + ", error = " + e.getMessage());
+ errPw.println("Exception: " + e.getMessage());
+ return -1;
+ }
+ return 0;
+ }
+
+ private int handleSetSatellitePointingUiClassNameCommand() {
+ PrintWriter errPw = getErrPrintWriter();
+ String packageName = null;
+ String className = null;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-p": {
+ packageName = getNextArgRequired();
+ break;
+ }
+ case "-c": {
+ className = getNextArgRequired();
+ break;
+ }
+ }
+ }
+ Log.d(LOG_TAG, "handleSetSatellitePointingUiClassNameCommand: packageName="
+ + packageName + ", className=" + className);
+
+ try {
+ boolean result = mInterface.setSatellitePointingUiClassName(packageName, className);
+ if (VDBG) {
+ Log.v(LOG_TAG, "setSatellitePointingUiClassName result =" + result);
+ }
+ getOutPrintWriter().println(result);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "setSatellitePointingUiClassName: " + packageName
+ + ", error = " + e.getMessage());
+ errPw.println("Exception: " + e.getMessage());
+ return -1;
+ }
+ return 0;
+ }
+
+ private int handleSetSatelliteListeningTimeoutDuration() {
+ PrintWriter errPw = getErrPrintWriter();
+ long timeoutMillis = 0;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-t": {
+ timeoutMillis = Long.parseLong(getNextArgRequired());
+ break;
+ }
+ }
+ }
+ Log.d(LOG_TAG, "handleSetSatelliteListeningTimeoutDuration: timeoutMillis="
+ + timeoutMillis);
+
+ try {
+ boolean result = mInterface.setSatelliteListeningTimeoutDuration(timeoutMillis);
+ if (VDBG) {
+ Log.v(LOG_TAG, "setSatelliteListeningTimeoutDuration " + timeoutMillis
+ + ", result = " + result);
+ }
+ getOutPrintWriter().println(result);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "setSatelliteListeningTimeoutDuration: " + timeoutMillis
+ + ", error = " + e.getMessage());
+ errPw.println("Exception: " + e.getMessage());
+ return -1;
+ }
+ return 0;
+ }
+
+ private int handleSettSatelliteDeviceAlignedTimeoutDuration() {
+ PrintWriter errPw = getErrPrintWriter();
+ long timeoutMillis = 0;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-t": {
+ timeoutMillis = Long.parseLong(getNextArgRequired());
+ break;
+ }
+ }
+ }
+ Log.d(LOG_TAG, "handleSettSatelliteDeviceAlignedTimeoutDuration: timeoutMillis="
+ + timeoutMillis);
+
+ try {
+ boolean result = mInterface.setSatelliteDeviceAlignedTimeoutDuration(timeoutMillis);
+ if (VDBG) {
+ Log.v(LOG_TAG, "setSatelliteDeviceAlignedTimeoutDuration " + timeoutMillis
+ + ", result = " + result);
+ }
+ getOutPrintWriter().println(result);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "setSatelliteDeviceAlignedTimeoutDuration: " + timeoutMillis
+ + ", error = " + e.getMessage());
+ errPw.println("Exception: " + e.getMessage());
+ return -1;
+ }
+ return 0;
+ }
+
+ private int handleCarrierRestrictionStatusCommand() {
+ try {
+ String MOCK_MODEM_SERVICE_NAME = "android.telephony.mockmodem.MockModemService";
+ if (!(checkShellUid() && MOCK_MODEM_SERVICE_NAME.equalsIgnoreCase(
+ mInterface.getModemService()))) {
+ Log.v(LOG_TAG,
+ "handleCarrierRestrictionStatusCommand, MockModem service check fails or "
+ + " checkShellUid fails");
+ return -1;
+ }
+ } catch (RemoteException ex) {
+ ex.printStackTrace();
+ }
+ String callerInfo = getNextOption();
+ CarrierAllowListInfo allowListInfo = CarrierAllowListInfo.loadInstance(mContext);
+ if (TextUtils.isEmpty(callerInfo)) {
+ // reset the Json content after testing
+ allowListInfo.updateJsonForTest(null);
+ return 0;
+ }
+ if (callerInfo.startsWith("--")) {
+ callerInfo = callerInfo.replace("--", "");
+ }
+ String params[] = callerInfo.split(",");
+ StringBuffer jsonStrBuffer = new StringBuffer();
+ String tokens;
+ for (int index = 0; index < params.length; index++) {
+ tokens = convertToJsonString(index, params[index]);
+ if (TextUtils.isEmpty(tokens)) {
+ // received wrong format from CTS
+ if (VDBG) {
+ Log.v(LOG_TAG,
+ "handleCarrierRestrictionStatusCommand, Shell command parsing error");
+ }
+ return -1;
+ }
+ jsonStrBuffer.append(tokens);
+ }
+ int result = allowListInfo.updateJsonForTest(jsonStrBuffer.toString());
+ return result;
+ }
+
// set-carrier-service-package-override
private int setCarrierServicePackageOverride() {
PrintWriter errPw = getErrPrintWriter();
@@ -3071,4 +3413,31 @@
}
return 0;
}
+
+ /**
+ * Building the string that can be used to build the JsonObject which supports to stub the data
+ * in CarrierAllowListInfo for CTS testing. sample format is like
+ * {"com.android.example":{"carrierId":"10000","callerSHA1Id":["XXXXXXXXXXXXXX"]}}
+ */
+ private String convertToJsonString(int index, String param) {
+
+ String token[] = param.split(":");
+ String jSonString;
+ switch (index) {
+ case 0:
+ jSonString = "{" + QUOTES + token[1] + QUOTES + ":";
+ break;
+ case 1:
+ jSonString =
+ "{" + QUOTES + token[0] + QUOTES + ":" + QUOTES + token[1] + QUOTES + ",";
+ break;
+ case 2:
+ jSonString =
+ QUOTES + token[0] + QUOTES + ":" + "[" + QUOTES + token[1] + QUOTES + "]}}";
+ break;
+ default:
+ jSonString = null;
+ }
+ return jSonString;
+ }
}
diff --git a/src/com/android/phone/callcomposer/DigestAuthUtils.java b/src/com/android/phone/callcomposer/DigestAuthUtils.java
index 2f081f7..770aadf 100644
--- a/src/com/android/phone/callcomposer/DigestAuthUtils.java
+++ b/src/com/android/phone/callcomposer/DigestAuthUtils.java
@@ -30,6 +30,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.ParseException;
+import java.util.Locale;
public class DigestAuthUtils {
private static final String TAG = DigestAuthUtils.class.getSimpleName();
@@ -54,12 +55,12 @@
public static String generateAuthorizationHeader(WWWAuthenticate parsedHeader,
GbaCredentials credentials, String method, String uri) {
if (!TextUtils.isEmpty(parsedHeader.getAlgorithm())
- && !MD5_ALGORITHM.equals(parsedHeader.getAlgorithm().toLowerCase())) {
+ && !MD5_ALGORITHM.equals(parsedHeader.getAlgorithm().toLowerCase(Locale.ROOT))) {
Log.e(TAG, "This client only supports MD5 auth");
return "";
}
if (!TextUtils.isEmpty(parsedHeader.getQop())
- && !AUTH_QOP.equals(parsedHeader.getQop().toLowerCase())) {
+ && !AUTH_QOP.equals(parsedHeader.getQop().toLowerCase(Locale.ROOT))) {
Log.e(TAG, "This client only supports the auth qop");
return "";
}
@@ -137,7 +138,7 @@
}
private static String base16(byte[] input) {
- return BaseEncoding.base16().encode(input).toLowerCase();
+ return BaseEncoding.base16().encode(input).toLowerCase(Locale.ROOT);
}
private static MessageDigest getMd5Digest() {
diff --git a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
index 804611f..5da52d6 100644
--- a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
+++ b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
@@ -29,6 +29,7 @@
import android.permission.LegacyPermissionManager;
import android.service.euicc.EuiccService;
import android.telephony.euicc.EuiccManager;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -58,6 +59,8 @@
private LegacyPermissionManager mPermissionManager;
private boolean mGrantPermissionDone = false;
private ThreadPoolExecutor mExecutor;
+ // Used for CTS EuiccManager action verification
+ private static ComponentName mTestEuiccUiComponentName;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -94,6 +97,18 @@
}
}
+ /**
+ * This API used to set the Test EuiccUiComponent for CTS
+ * @param packageName package which handles the intent
+ * @param componentName ui component to be launched for testing
+ */
+ public static void setTestEuiccUiComponent(String packageName, String componentName) {
+ mTestEuiccUiComponentName = null;
+ if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(componentName)) {
+ mTestEuiccUiComponentName = new ComponentName(packageName, componentName);
+ }
+ }
+
@VisibleForTesting
@Nullable
Intent resolveEuiccUiIntent() {
@@ -109,6 +124,13 @@
return null;
}
+ if (mTestEuiccUiComponentName != null) {
+ Log.i(TAG, "Test mode");
+ euiccUiIntent.setComponent(mTestEuiccUiComponentName);
+ mTestEuiccUiComponentName = null;
+ return euiccUiIntent;
+ }
+
revokePermissionFromLuiApps(euiccUiIntent);
ActivityInfo activityInfo = findBestActivity(euiccUiIntent);
@@ -148,6 +170,12 @@
case EuiccManager.ACTION_PROVISION_EMBEDDED_SUBSCRIPTION:
intent.setAction(EuiccService.ACTION_PROVISION_EMBEDDED_SUBSCRIPTION);
break;
+ case EuiccManager.ACTION_TRANSFER_EMBEDDED_SUBSCRIPTIONS:
+ intent.setAction(EuiccService.ACTION_TRANSFER_EMBEDDED_SUBSCRIPTIONS);
+ break;
+ case EuiccManager.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION:
+ intent.setAction(EuiccService.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION);
+ break;
default:
Log.w(TAG, "Unsupported action: " + action);
return null;
diff --git a/src/com/android/phone/settings/AccessibilitySettingsFragment.java b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
index 4c29e65..8c4a343 100644
--- a/src/com/android/phone/settings/AccessibilitySettingsFragment.java
+++ b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
@@ -39,7 +39,6 @@
import com.android.ims.ImsManager;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.phone.PhoneGlobals;
import com.android.phone.R;
@@ -184,12 +183,8 @@
// Update RTT config with IMS Manager if the always-on carrier config isn't set to true.
CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(
Context.CARRIER_CONFIG_SERVICE);
- int[] activeSubIds;
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- activeSubIds = SubscriptionManagerService.getInstance().getActiveSubIdList(true);
- } else {
- activeSubIds = SubscriptionController.getInstance().getActiveSubIdList(true);
- }
+ int[] activeSubIds = SubscriptionManagerService.getInstance().getActiveSubIdList(true);
+
for (int subId : activeSubIds) {
if (!configManager.getConfigForSubId(subId).getBoolean(
CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL, false)) {
@@ -271,15 +266,7 @@
private boolean shouldShowRttSetting() {
// Go through all the subs -- if we want to display the RTT setting for any of them, do
// display it.
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- for (int subId : SubscriptionManagerService.getInstance().getActiveSubIdList(true)) {
- if (PhoneGlobals.getInstance().phoneMgr.isRttSupported(subId)) {
- return true;
- }
- }
- return false;
- }
- for (int subId : SubscriptionController.getInstance().getActiveSubIdList(true)) {
+ for (int subId : SubscriptionManagerService.getInstance().getActiveSubIdList(true)) {
if (PhoneGlobals.getInstance().phoneMgr.isRttSupported(subId)) {
return true;
}
diff --git a/src/com/android/phone/settings/BandMode.java b/src/com/android/phone/settings/BandMode.java
deleted file mode 100644
index 853075a..0000000
--- a/src/com/android/phone/settings/BandMode.java
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2019 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.phone.settings;
-
-import android.app.Activity;
-import android.content.DialogInterface;
-import android.os.AsyncResult;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-import android.view.View;
-import android.view.Window;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-import androidx.appcompat.app.AlertDialog;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.phone.R;
-
-/**
- * Radio Band Mode Selection Class
- *
- * It will query baseband about all available band modes and display them
- * in screen. It will display all six band modes if the query failed.
- *
- * After user select one band, it will send the selection to baseband.
- *
- * It will alter user the result of select operation and exit, no matter success
- * or not.
- *
- */
-public class BandMode extends Activity {
- private static final String LOG_TAG = "phone";
- private static final boolean DBG = false;
-
- private static final int EVENT_BAND_SCAN_COMPLETED = 100;
- private static final int EVENT_BAND_SELECTION_DONE = 200;
-
- //Directly maps to RIL_RadioBandMode from ril.h
- private static final String[] BAND_NAMES = new String[] {
- "Automatic",
- "Europe",
- "United States",
- "Japan",
- "Australia",
- "Australia 2",
- "Cellular 800",
- "PCS",
- "Class 3 (JTACS)",
- "Class 4 (Korea-PCS)",
- "Class 5",
- "Class 6 (IMT2000)",
- "Class 7 (700Mhz-Upper)",
- "Class 8 (1800Mhz-Upper)",
- "Class 9 (900Mhz)",
- "Class 10 (800Mhz-Secondary)",
- "Class 11 (Europe PAMR 400Mhz)",
- "Class 15 (US-AWS)",
- "Class 16 (US-2500Mhz)"
- };
-
- private ListView mBandList;
- private ArrayAdapter mBandListAdapter;
- private BandListItem mTargetBand = null;
- private DialogInterface mProgressPanel;
-
- private Phone mPhone = null;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setContentView(R.layout.band_mode);
-
- mPhone = PhoneFactory.getDefaultPhone();
-
- mBandList = (ListView) findViewById(R.id.band);
- mBandListAdapter = new ArrayAdapter<BandListItem>(this,
- android.R.layout.simple_list_item_1);
- mBandList.setAdapter(mBandListAdapter);
- mBandList.setOnItemClickListener(mBandSelectionHandler);
-
- loadBandList();
- }
-
- private AdapterView.OnItemClickListener mBandSelectionHandler =
- new AdapterView.OnItemClickListener() {
- public void onItemClick(AdapterView parent, View v,
- int position, long id) {
-
- getWindow().setFeatureInt(
- Window.FEATURE_INDETERMINATE_PROGRESS,
- Window.PROGRESS_VISIBILITY_ON);
-
- mTargetBand = (BandListItem) parent.getAdapter().getItem(position);
-
- if (DBG) log("Select band : " + mTargetBand.toString());
-
- Message msg =
- mHandler.obtainMessage(EVENT_BAND_SELECTION_DONE);
- mPhone.setBandMode(mTargetBand.getBand(), msg);
- }
- };
-
- private static class BandListItem {
- private int mBandMode = Phone.BM_UNSPECIFIED;
-
- BandListItem(int bm) {
- mBandMode = bm;
- }
-
- public int getBand() {
- return mBandMode;
- }
-
- public String toString() {
- if (mBandMode >= BAND_NAMES.length) return "Band mode " + mBandMode;
- return BAND_NAMES[mBandMode];
- }
- }
-
- private void loadBandList() {
- String str = getString(R.string.band_mode_loading);
-
- if (DBG) log(str);
-
-
- //ProgressDialog.show(this, null, str, true, true, null);
- mProgressPanel = new AlertDialog.Builder(this)
- .setMessage(str)
- .show();
-
- Message msg = mHandler.obtainMessage(EVENT_BAND_SCAN_COMPLETED);
- mPhone.queryAvailableBandMode(msg);
-
- }
-
- private void bandListLoaded(AsyncResult result) {
- if (DBG) log("network list loaded");
-
- if (mProgressPanel != null) mProgressPanel.dismiss();
-
- clearList();
-
- boolean addBandSuccess = false;
- BandListItem item;
-
- if (result.result != null) {
- int [] bands = (int []) result.result;
-
- if (bands.length == 0) {
- Log.wtf(LOG_TAG, "No Supported Band Modes");
- return;
- }
-
- int size = bands[0];
-
- if (size > 0) {
- mBandListAdapter.add(
- new BandListItem(Phone.BM_UNSPECIFIED)); //Always include AUTOMATIC
- for (int i = 1; i <= size; i++) {
- if (bands[i] == Phone.BM_UNSPECIFIED) {
- continue;
- }
- item = new BandListItem(bands[i]);
- mBandListAdapter.add(item);
- if (DBG) log("Add " + item.toString());
- }
- addBandSuccess = true;
- }
- }
-
- if (!addBandSuccess) {
- if (DBG) log("Error in query, add default list");
- for (int i = 0; i < Phone.BM_NUM_BAND_MODES; i++) {
- item = new BandListItem(i);
- mBandListAdapter.add(item);
- if (DBG) log("Add default " + item.toString());
- }
- }
- mBandList.requestFocus();
- }
-
- private void displayBandSelectionResult(Throwable ex) {
- String status = getString(R.string.band_mode_set)
- + " [" + mTargetBand.toString() + "] ";
-
- if (ex != null) {
- status = status + getString(R.string.band_mode_failed);
- } else {
- status = status + getString(R.string.band_mode_succeeded);
- }
-
- mProgressPanel = new AlertDialog.Builder(this)
- .setMessage(status)
- .setPositiveButton(android.R.string.ok, null).show();
- }
-
- private void clearList() {
- while (mBandListAdapter.getCount() > 0) {
- mBandListAdapter.remove(
- mBandListAdapter.getItem(0));
- }
- }
-
- private void log(String msg) {
- Log.d(LOG_TAG, "[BandsList] " + msg);
- }
-
- private Handler mHandler = new Handler() {
- public void handleMessage(Message msg) {
- AsyncResult ar;
- switch (msg.what) {
- case EVENT_BAND_SCAN_COMPLETED:
- ar = (AsyncResult) msg.obj;
-
- bandListLoaded(ar);
- break;
-
- case EVENT_BAND_SELECTION_DONE:
- ar = (AsyncResult) msg.obj;
-
- getWindow().setFeatureInt(
- Window.FEATURE_INDETERMINATE_PROGRESS,
- Window.PROGRESS_VISIBILITY_OFF);
-
- if (!isFinishing()) {
- displayBandSelectionResult(ar.exception);
- }
- break;
- }
- }
- };
-
-
-}
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsActivity.java b/src/com/android/phone/settings/PhoneAccountSettingsActivity.java
index e15be39..12cc667 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsActivity.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsActivity.java
@@ -18,9 +18,10 @@
import android.app.ActionBar;
import android.os.Bundle;
+import android.os.UserManager;
import android.preference.PreferenceActivity;
import android.view.MenuItem;
-import android.view.WindowManager;
+import android.widget.Toast;
import com.android.phone.R;
@@ -29,6 +30,16 @@
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
+
+ // Make sure we are running as an admin/work user.
+ UserManager userManager = getSystemService(UserManager.class);
+ if (!userManager.isAdminUser() && !userManager.isManagedProfile()) {
+ Toast.makeText(this, R.string.phone_account_settings_user_restriction,
+ Toast.LENGTH_SHORT).show();
+ finish();
+ return;
+ }
+
getWindow().addSystemFlags(
android.view.WindowManager.LayoutParams
.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index 49e1379..7cc9235 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -6,7 +6,9 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
+import android.os.Binder;
import android.os.Bundle;
+import android.os.UserHandle;
import android.os.UserManager;
import android.preference.Preference;
import android.preference.PreferenceCategory;
@@ -364,6 +366,7 @@
mTelecomManager.getCallCapablePhoneAccounts(includeDisabledAccounts);
for (Iterator<PhoneAccountHandle> i = accountHandles.iterator(); i.hasNext();) {
PhoneAccountHandle handle = i.next();
+ UserHandle userHandle = handle.getUserHandle();
if (handle.equals(emergencyAccountHandle)) {
// never include emergency call accounts in this piece of code.
i.remove();
@@ -376,6 +379,11 @@
} else if (!includeSims &&
account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
i.remove();
+ } else if (!userHandle.equals(Binder.getCallingUserHandle())
+ && !account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
+ // Only show accounts for the current user (unless account has
+ // CAPABILITY_MULTI_USER).
+ i.remove();
}
}
return accountHandles;
@@ -387,7 +395,7 @@
private PhoneAccountHandle getEmergencyPhoneAccount() {
return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
- (Phone) null, "" /* prefix */, true /* isEmergency */);
+ (Phone) null, "" /* prefix */, true /* isEmergency */, null /* userHandle */);
}
public static Intent buildPhoneAccountConfigureIntent(
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index 3cf0c93..124badf 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -29,6 +29,7 @@
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Typeface;
+import android.hardware.radio.modem.ImeiInfo;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -102,6 +103,7 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.euicc.EuiccConnector;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.phone.R;
import java.io.IOException;
@@ -202,12 +204,10 @@
Log.d(TAG, s);
}
- private static final int EVENT_CFI_CHANGED = 302;
private static final int EVENT_QUERY_SMSC_DONE = 1005;
private static final int EVENT_UPDATE_SMSC_DONE = 1006;
private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 1007;
- private static final int MENU_ITEM_SELECT_BAND = 0;
private static final int MENU_ITEM_VIEW_ADN = 1;
private static final int MENU_ITEM_VIEW_FDN = 2;
private static final int MENU_ITEM_VIEW_SDN = 3;
@@ -234,6 +234,9 @@
private TextView mGprsState;
private TextView mVoiceNetwork;
private TextView mDataNetwork;
+ private TextView mVoiceRawReg;
+ private TextView mDataRawReg;
+ private TextView mWlanDataRawReg;
private TextView mOverrideNetwork;
private TextView mDBm;
private TextView mMwi;
@@ -256,7 +259,7 @@
private TextView mNetworkSlicingConfig;
private EditText mSmsc;
private Switch mRadioPowerOnSwitch;
- private Button mCellInfoRefreshRateButton;
+ private Switch mSimulateOutOfServiceSwitch;
private Button mDnsCheckToggleButton;
private Button mPingTestButton;
private Button mUpdateSmscButton;
@@ -292,6 +295,7 @@
private boolean mCfiValue = false;
private List<CellInfo> mCellInfoResult = null;
+ private final boolean[] mSimulateOos = new boolean[2];
private int mPreferredNetworkTypeResult;
private int mCellInfoRefreshRateIndex;
@@ -373,6 +377,7 @@
updateServiceState(serviceState);
updateRadioPowerState();
updateNetworkType();
+ updateRawRegistrationState(serviceState);
updateImsProvisionedState();
updateNrStats(serviceState);
}
@@ -410,7 +415,7 @@
private void updatePhoneIndex(int phoneIndex, int subId) {
// unregister listeners on the old subId
unregisterPhoneStateListener();
- mTelephonyManager.setCellInfoListRate(sCellInfoListRateDisabled);
+ mTelephonyManager.setCellInfoListRate(sCellInfoListRateDisabled, mPhone.getSubId());
if (phoneIndex == SubscriptionManager.INVALID_PHONE_INDEX) {
log("Invalid phone index " + phoneIndex + ", subscription ID " + subId);
@@ -510,6 +515,9 @@
mGprsState = (TextView) findViewById(R.id.gprs);
mVoiceNetwork = (TextView) findViewById(R.id.voice_network);
mDataNetwork = (TextView) findViewById(R.id.data_network);
+ mVoiceRawReg = (TextView) findViewById(R.id.voice_raw_registration_state);
+ mDataRawReg = (TextView) findViewById(R.id.data_raw_registration_state);
+ mWlanDataRawReg = (TextView) findViewById(R.id.wlan_data_raw_registration_state);
mOverrideNetwork = (TextView) findViewById(R.id.override_network);
mDBm = (TextView) findViewById(R.id.dbm);
mMwi = (TextView) findViewById(R.id.mwi);
@@ -598,6 +606,11 @@
mRadioPowerOnSwitch = (Switch) findViewById(R.id.radio_power);
+ mSimulateOutOfServiceSwitch = (Switch) findViewById(R.id.simulate_out_of_service);
+ if (!TelephonyUtils.IS_DEBUGGABLE) {
+ mSimulateOutOfServiceSwitch.setVisibility(View.GONE);
+ }
+
mDownlinkKbps = (TextView) findViewById(R.id.dl_kbps);
mUplinkKbps = (TextView) findViewById(R.id.ul_kbps);
updateBandwidths(0, 0);
@@ -690,7 +703,8 @@
//set selection after registering listener to force update
mCellInfoRefreshRateSpinner.setSelection(mCellInfoRefreshRateIndex);
// Request cell information update from RIL.
- mTelephonyManager.setCellInfoListRate(CELL_INFO_REFRESH_RATES[mCellInfoRefreshRateIndex]);
+ mTelephonyManager.setCellInfoListRate(CELL_INFO_REFRESH_RATES[mCellInfoRefreshRateIndex],
+ mPhone.getSubId());
//set selection before registering to prevent update
mPreferredNetworkType.setSelection(mPreferredNetworkTypeResult, true);
@@ -707,6 +721,8 @@
mSelectPhoneIndex.setOnItemSelectedListener(mSelectPhoneIndexHandler);
mRadioPowerOnSwitch.setOnCheckedChangeListener(mRadioPowerOnChangeListener);
+ mSimulateOutOfServiceSwitch.setOnCheckedChangeListener(mSimulateOosOnChangeListener);
+ mSimulateOutOfServiceSwitch.setChecked(mSimulateOos[mPhone.getPhoneId()]);
mImsVolteProvisionedSwitch.setOnCheckedChangeListener(mImsVolteCheckedChangeListener);
mImsVtProvisionedSwitch.setOnCheckedChangeListener(mImsVtCheckedChangeListener);
mImsWfcProvisionedSwitch.setOnCheckedChangeListener(mImsWfcCheckedChangeListener);
@@ -735,7 +751,7 @@
log("onPause: unregister phone & data intents");
mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
- mTelephonyManager.setCellInfoListRate(sCellInfoListRateDisabled);
+ mTelephonyManager.setCellInfoListRate(sCellInfoListRateDisabled, mPhone.getSubId());
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
}
@@ -776,9 +792,7 @@
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, MENU_ITEM_SELECT_BAND, 0, R.string.radio_info_band_mode_label)
- .setOnMenuItemClickListener(mSelectBandCallback)
- .setAlphabeticShortcut('b');
+ // Removed "select Radio band". If need it back, use setSystemSelectionChannels()
menu.add(1, MENU_ITEM_VIEW_ADN, 0,
R.string.radioInfo_menu_viewADN).setOnMenuItemClickListener(mViewADNCallback);
menu.add(1, MENU_ITEM_VIEW_FDN, 0,
@@ -843,8 +857,11 @@
mOperatorName.setText("");
mGprsState.setText("");
mDataNetwork.setText("");
+ mDataRawReg.setText("");
mOverrideNetwork.setText("");
mVoiceNetwork.setText("");
+ mVoiceRawReg.setText("");
+ mWlanDataRawReg.setText("");
mSent.setText("");
mReceived.setText("");
mCallState.setText("");
@@ -1192,6 +1209,32 @@
}
}
+ private String getRawRegistrationStateText(ServiceState ss, int domain, int transportType) {
+ if (ss != null) {
+ NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(domain, transportType);
+ if (nri != null) {
+ return NetworkRegistrationInfo.registrationStateToString(
+ nri.getNetworkRegistrationState())
+ + (nri.isEmergencyEnabled() ? "_EM" : "");
+ }
+ }
+ return "";
+ }
+
+ private void updateRawRegistrationState(ServiceState serviceState) {
+ ServiceState ss = serviceState;
+ if (ss == null && mPhone != null) {
+ ss = mPhone.getServiceState();
+ }
+
+ mVoiceRawReg.setText(getRawRegistrationStateText(ss, NetworkRegistrationInfo.DOMAIN_CS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN));
+ mDataRawReg.setText(getRawRegistrationStateText(ss, NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN));
+ mWlanDataRawReg.setText(getRawRegistrationStateText(ss, NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN));
+ }
+
private void updateNrStats(ServiceState serviceState) {
if ((mTelephonyManager.getSupportedRadioAccessFamily()
& TelephonyManager.NETWORK_TYPE_BITMASK_NR) == 0) {
@@ -1234,11 +1277,16 @@
Resources r = getResources();
s = mPhone.getDeviceId();
- if (s == null) s = r.getString(R.string.radioInfo_unknown);
+ if (s == null) {
+ s = r.getString(R.string.radioInfo_unknown);
+ } else if (mPhone.getImeiType() == ImeiInfo.ImeiType.PRIMARY) {
+ s = s + " (" + r.getString(R.string.radioInfo_imei_primary) + ")";
+ }
mDeviceId.setText(s);
s = mPhone.getSubscriberId();
if (s == null) s = r.getString(R.string.radioInfo_unknown);
+
mSubscriberId.setText(s);
SubscriptionManager subMgr = getSystemService(SubscriptionManager.class);
@@ -1471,16 +1519,6 @@
}
};
- private MenuItem.OnMenuItemClickListener mSelectBandCallback =
- new MenuItem.OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
- Intent intent = new Intent();
- intent.setClass(RadioInfo.this, BandMode.class);
- startActivity(intent);
- return true;
- }
- };
-
private MenuItem.OnMenuItemClickListener mToggleData =
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
@@ -1635,6 +1673,22 @@
}
};
+ private final OnCheckedChangeListener mSimulateOosOnChangeListener =
+ (buttonView, isChecked) -> {
+ Intent intent = new Intent("com.android.internal.telephony.TestServiceState");
+ if (isChecked) {
+ log("Send OOS override broadcast intent.");
+ intent.putExtra("data_reg_state", 1);
+ mSimulateOos[mPhone.getPhoneId()] = true;
+ } else {
+ log("Remove OOS override.");
+ intent.putExtra("action", "reset");
+ mSimulateOos[mPhone.getPhoneId()] = false;
+ }
+
+ mPhone.getTelephonyTester().setServiceStateTestIntent(intent);
+ };
+
private boolean isImsVolteProvisioned() {
return getImsConfigProvisionedState(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
@@ -1889,7 +1943,7 @@
public void onItemSelected(AdapterView parent, View v, int pos, long id) {
mCellInfoRefreshRateIndex = pos;
- mTelephonyManager.setCellInfoListRate(CELL_INFO_REFRESH_RATES[pos]);
+ mTelephonyManager.setCellInfoListRate(CELL_INFO_REFRESH_RATES[pos], mPhone.getSubId());
updateAllCellInfo();
}
diff --git a/src/com/android/phone/settings/VoicemailSettingsActivity.java b/src/com/android/phone/settings/VoicemailSettingsActivity.java
index 02bf4b2..c940748 100644
--- a/src/com/android/phone/settings/VoicemailSettingsActivity.java
+++ b/src/com/android/phone/settings/VoicemailSettingsActivity.java
@@ -17,6 +17,7 @@
package com.android.phone.settings;
import android.app.Dialog;
+import android.content.ContentProvider;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
@@ -25,6 +26,8 @@
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.UserHandle;
import android.os.UserManager;
import android.preference.Preference;
import android.preference.PreferenceActivity;
@@ -520,6 +523,17 @@
Cursor cursor = null;
try {
+ // check if the URI returned by the user belongs to the user
+ final int currentUser = UserHandle.getUserId(Process.myUid());
+ if (currentUser
+ != ContentProvider.getUserIdFromUri(data.getData(), currentUser)) {
+
+ if (DBG) {
+ log("onActivityResult: Contact data of different user, "
+ + "cannot access");
+ }
+ return;
+ }
cursor = getContentResolver().query(data.getData(),
new String[] { CommonDataKinds.Phone.NUMBER }, null, null, null);
if ((cursor == null) || (!cursor.moveToFirst())) {
diff --git a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
index 468d38f..0884e12 100644
--- a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
@@ -19,6 +19,7 @@
import static android.app.Activity.RESULT_OK;
+import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Intent;
import android.content.res.Resources;
@@ -26,6 +27,8 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.UserHandle;
import android.provider.ContactsContract.CommonDataKinds;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
@@ -137,6 +140,14 @@
}
Cursor cursor = null;
try {
+ // check if the URI returned by the user belongs to the user
+ final int currentUser = UserHandle.getUserId(Process.myUid());
+ if (currentUser
+ != ContentProvider.getUserIdFromUri(intent.getData(), currentUser)) {
+ Log.w(LOG_TAG, "onActivityResult: Contact data of different user, "
+ + "cannot access");
+ return;
+ }
cursor = getContentResolver().query(intent.getData(),
NUM_PROJECTION, null, null, null);
if ((cursor == null) || (!cursor.moveToFirst())) {
diff --git a/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java b/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
new file mode 100644
index 0000000..d5ef816
--- /dev/null
+++ b/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2022 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.phone.slice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+import android.provider.DeviceConfig;
+import android.telephony.AnomalyReporter;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telephony.Phone;
+import com.android.libraries.entitlement.CarrierConfig;
+import com.android.libraries.entitlement.ServiceEntitlement;
+import com.android.libraries.entitlement.ServiceEntitlementException;
+import com.android.libraries.entitlement.ServiceEntitlementRequest;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.UUID;
+
+/**
+ * Premium network entitlement API class to check the premium network slice entitlement result
+ * from carrier API over the network.
+ */
+public class PremiumNetworkEntitlementApi {
+ private static final String TAG = "PremiumNwEntitlementApi";
+ private static final String ENTITLEMENT_STATUS_KEY = "EntitlementStatus";
+ private static final String PROVISION_STATUS_KEY = "ProvStatus";
+ private static final String SERVICE_FLOW_URL_KEY = "ServiceFlow_URL";
+ private static final String SERVICE_FLOW_USERDATA_KEY = "ServiceFlow_UserData";
+ private static final String DEFAULT_EAP_AKA_RESPONSE = "Default EAP AKA response";
+ /**
+ * UUID to report an anomaly if an unexpected error is received during entitlement check.
+ */
+ private static final String UUID_ENTITLEMENT_CHECK_UNEXPECTED_ERROR =
+ "f2b0661a-9114-4b1b-9add-a8d338f9c054";
+
+ /**
+ * Experiment flag to enable bypassing EAP-AKA authentication for Slice Purchase activities.
+ * The device will accept any challenge from the entitlement server and return a predefined
+ * string as a response.
+ *
+ * This flag should be enabled for testing only.
+ */
+ public static final String BYPASS_EAP_AKA_AUTH_FOR_SLICE_PURCHASE_ENABLED =
+ "bypass_eap_aka_auth_for_slice_purchase_enabled";
+
+ @NonNull private final Phone mPhone;
+ @NonNull private final ServiceEntitlement mServiceEntitlement;
+
+ public PremiumNetworkEntitlementApi(@NonNull Phone phone,
+ @NonNull PersistableBundle carrierConfig) {
+ mPhone = phone;
+ if (isBypassEapAkaAuthForSlicePurchaseEnabled()) {
+ mServiceEntitlement =
+ new ServiceEntitlement(
+ mPhone.getContext(),
+ getEntitlementServerCarrierConfig(carrierConfig),
+ mPhone.getSubId(),
+ true,
+ DEFAULT_EAP_AKA_RESPONSE);
+ } else {
+ mServiceEntitlement =
+ new ServiceEntitlement(
+ mPhone.getContext(),
+ getEntitlementServerCarrierConfig(carrierConfig),
+ mPhone.getSubId());
+ }
+ }
+
+ /**
+ * Returns premium network slice entitlement check result from carrier API (over network),
+ * or {@code null} on unrecoverable network issue or malformed server response.
+ * This is blocking call sending HTTP request and should not be called on main thread.
+ */
+ @Nullable public PremiumNetworkEntitlementResponse checkEntitlementStatus(
+ @TelephonyManager.PremiumCapability int capability) {
+ Log.d(TAG, "checkEntitlementStatus subId=" + mPhone.getSubId());
+ ServiceEntitlementRequest.Builder requestBuilder = ServiceEntitlementRequest.builder();
+ // Set fake device info to avoid leaking
+ requestBuilder.setTerminalVendor("vendorX");
+ requestBuilder.setTerminalModel("modelY");
+ requestBuilder.setTerminalSoftwareVersion("versionZ");
+ requestBuilder.setAcceptContentType(ServiceEntitlementRequest.ACCEPT_CONTENT_TYPE_JSON);
+ requestBuilder.setBoostType(getBoostTypeFromPremiumCapability(capability));
+ ServiceEntitlementRequest request = requestBuilder.build();
+ PremiumNetworkEntitlementResponse premiumNetworkEntitlementResponse =
+ new PremiumNetworkEntitlementResponse();
+
+ String response = null;
+ try {
+ response = mServiceEntitlement.queryEntitlementStatus(
+ ServiceEntitlement.APP_DATA_PLAN_BOOST,
+ request);
+ } catch (ServiceEntitlementException e) {
+ Log.e(TAG, "queryEntitlementStatus failed", e);
+ reportAnomaly(UUID_ENTITLEMENT_CHECK_UNEXPECTED_ERROR,
+ "checkEntitlementStatus failed with ServiceEntitlementException");
+ }
+ if (response == null) {
+ return null;
+ }
+ try {
+ JSONObject jsonAuthResponse = new JSONObject(response);
+ String entitlementStatus = null;
+ String provisionStatus = null;
+ if (jsonAuthResponse.has(ServiceEntitlement.APP_DATA_PLAN_BOOST)) {
+ JSONObject jsonToken = jsonAuthResponse.getJSONObject(
+ ServiceEntitlement.APP_DATA_PLAN_BOOST);
+ if (jsonToken.has(ENTITLEMENT_STATUS_KEY)) {
+ entitlementStatus = jsonToken.getString(ENTITLEMENT_STATUS_KEY);
+ if (entitlementStatus == null) {
+ return null;
+ }
+ premiumNetworkEntitlementResponse.mEntitlementStatus =
+ Integer.parseInt(entitlementStatus);
+ }
+ if (jsonToken.has(PROVISION_STATUS_KEY)) {
+ provisionStatus = jsonToken.getString(PROVISION_STATUS_KEY);
+ if (provisionStatus != null) {
+ premiumNetworkEntitlementResponse.mProvisionStatus =
+ Integer.parseInt(provisionStatus);
+ }
+ }
+ if (jsonToken.has(SERVICE_FLOW_URL_KEY)) {
+ premiumNetworkEntitlementResponse.mServiceFlowURL =
+ jsonToken.getString(SERVICE_FLOW_URL_KEY);
+ }
+ if (jsonToken.has(SERVICE_FLOW_USERDATA_KEY)) {
+ premiumNetworkEntitlementResponse.mServiceFlowUserData =
+ jsonToken.getString(SERVICE_FLOW_USERDATA_KEY);
+ }
+ } else {
+ Log.e(TAG, "queryEntitlementStatus failed with no app");
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "queryEntitlementStatus failed", e);
+ reportAnomaly(UUID_ENTITLEMENT_CHECK_UNEXPECTED_ERROR,
+ "checkEntitlementStatus failed with JSONException");
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "queryEntitlementStatus failed", e);
+ reportAnomaly(UUID_ENTITLEMENT_CHECK_UNEXPECTED_ERROR,
+ "checkEntitlementStatus failed with NumberFormatException");
+ }
+ Log.d(TAG, "queryEntitlementStatus succeeded with response: "
+ + premiumNetworkEntitlementResponse);
+ return premiumNetworkEntitlementResponse;
+ }
+
+ private void reportAnomaly(@NonNull String uuid, @NonNull String log) {
+ AnomalyReporter.reportAnomaly(UUID.fromString(uuid), log);
+ }
+
+ /**
+ * Returns entitlement server url from the given carrier configs or a default empty string
+ * if it is not available.
+ */
+ @NonNull public static String getEntitlementServerUrl(
+ @NonNull PersistableBundle carrierConfig) {
+ return carrierConfig.getString(
+ CarrierConfigManager.ImsServiceEntitlement.KEY_ENTITLEMENT_SERVER_URL_STRING,
+ "");
+ }
+
+ @NonNull private CarrierConfig getEntitlementServerCarrierConfig(
+ @NonNull PersistableBundle carrierConfig) {
+ String entitlementServiceUrl = getEntitlementServerUrl(carrierConfig);
+ return CarrierConfig.builder().setServerUrl(entitlementServiceUrl).build();
+ }
+
+ private boolean isBypassEapAkaAuthForSlicePurchaseEnabled() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TELEPHONY,
+ BYPASS_EAP_AKA_AUTH_FOR_SLICE_PURCHASE_ENABLED, false);
+ }
+
+ @NonNull private String getBoostTypeFromPremiumCapability(
+ @TelephonyManager.PremiumCapability int capability) {
+ if (capability == TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY) {
+ return "0" /* REALTIME_INTERACTIVE_TRAFFIC */;
+ }
+ return "";
+ }
+}
diff --git a/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java b/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java
new file mode 100644
index 0000000..ba44581
--- /dev/null
+++ b/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 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.phone.slice;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+/**
+ * Response class containing the entitlement status, provisioning status, and service flow URL
+ * for premium network entitlement checks.
+ *
+ * The relationship between entitlement status (left column) and provision status (top row)
+ * is defined in the table below:
+ * +--------------+-----------------+-------------------+-------------------+---------------+
+ * | | Not Provisioned | Provisioned | Not Available | In Progress |
+ * +--------------+-----------------+-------------------+-------------------+---------------+
+ * | Disabled | Check failed | Check failed | Check failed | Check failed |
+ * +--------------+-----------------+-------------------+-------------------+---------------+
+ * | Enabled | Carrier error | Display webview | Display webview | Carrier error |
+ * +--------------+-----------------+-------------------+-------------------+---------------+
+ * | Incompatible | Check failed | Check failed | Check failed | Check failed |
+ * +--------------+-----------------+-------------------+-------------------+---------------+
+ * | Provisioning | Carrier error | Carrier error | In Progress | In Progress |
+ * +--------------+-----------------+-------------------+-------------------+---------------+
+ * | Included | Carrier error | Already purchased | Already purchased | Carrier error |
+ * +--------------+-----------------+-------------------+-------------------+---------------+
+ */
+public class PremiumNetworkEntitlementResponse {
+ public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_DISABLED = 0;
+ public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED = 1;
+ public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCOMPATIBLE = 2;
+ public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING = 3;
+ public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED = 4;
+
+ @IntDef(prefix = {"PREMIUM_NETWORK_ENTITLEMENT_STATUS_"}, value = {
+ PREMIUM_NETWORK_ENTITLEMENT_STATUS_DISABLED,
+ PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED,
+ PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCOMPATIBLE,
+ PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING,
+ PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED
+ })
+ public @interface PremiumNetworkEntitlementStatus {}
+
+ public static final int PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED = 0;
+ public static final int PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED = 1;
+ public static final int PREMIUM_NETWORK_PROVISION_STATUS_NOT_AVAILABLE = 2;
+ public static final int PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS = 3;
+
+ @IntDef(prefix = {"PREMIUM_NETWORK_PROVISION_STATUS_"}, value = {
+ PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED,
+ PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED,
+ PREMIUM_NETWORK_PROVISION_STATUS_NOT_AVAILABLE,
+ PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS
+ })
+ public @interface PremiumNetworkProvisionStatus {}
+
+ @PremiumNetworkEntitlementStatus public int mEntitlementStatus;
+ @PremiumNetworkProvisionStatus public int mProvisionStatus;
+ @NonNull public String mServiceFlowURL;
+ @NonNull public String mServiceFlowUserData;
+
+ /**
+ * @return {@code true} if the premium network is provisioned and {@code false} otherwise.
+ */
+ public boolean isProvisioned() {
+ return !isInvalidResponse()
+ && mEntitlementStatus == PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED;
+ }
+
+ /**
+ * @return {@code true} if provisioning the premium network is in progress and
+ * {@code false} otherwise.
+ */
+ public boolean isProvisioningInProgress() {
+ return !isInvalidResponse()
+ && mEntitlementStatus == PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING;
+ }
+
+ /**
+ * @return {@code true} if the premium network capability is allowed and
+ * {@code false} otherwise.
+ */
+ public boolean isPremiumNetworkCapabilityAllowed() {
+ switch (mEntitlementStatus) {
+ case PREMIUM_NETWORK_ENTITLEMENT_STATUS_DISABLED:
+ case PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCOMPATIBLE:
+ return false;
+ }
+ return !isInvalidResponse();
+ }
+
+ /**
+ * @return {@code true} if the response is invalid and {@code false} if it is valid.
+ */
+ public boolean isInvalidResponse() {
+ switch (mEntitlementStatus) {
+ case PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED:
+ case PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED:
+ return mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED
+ || mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS;
+ case PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING:
+ return mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED
+ || mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED;
+ }
+ return false;
+ }
+
+ @Override
+ @NonNull public String toString() {
+ return "PremiumNetworkEntitlementResponse{mEntitlementStatus=" + mEntitlementStatus
+ + ", mProvisionStatus=" + mProvisionStatus + ", mServiceFlowURL=" + mServiceFlowURL
+ + ", mServiceFlowUserData" + mServiceFlowUserData + "}";
+ }
+}
diff --git a/src/com/android/phone/slice/SlicePurchaseController.java b/src/com/android/phone/slice/SlicePurchaseController.java
new file mode 100644
index 0000000..b1abe56
--- /dev/null
+++ b/src/com/android/phone/slice/SlicePurchaseController.java
@@ -0,0 +1,1117 @@
+/*
+ * Copyright (C) 2022 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.phone.slice;
+
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.provider.DeviceConfig;
+import android.sysprop.TelephonyProperties;
+import android.telephony.AnomalyReporter;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.NetworkSlicingConfig;
+import android.text.TextUtils;
+import android.util.Log;
+import android.webkit.URLUtil;
+import android.webkit.WebView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * The SlicePurchaseController controls the purchase and availability of all cellular premium
+ * capabilities. Applications can check whether premium capabilities are available by calling
+ * {@link TelephonyManager#isPremiumCapabilityAvailableForPurchase(int)}. If this returns true,
+ * they can then call {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+ * to purchase the premium capability. If all conditions are met, a notification will be displayed
+ * to the user prompting them to purchase the premium capability. If the user confirms on the
+ * notification, a {@link WebView} will open that allows the user to purchase the premium capability
+ * from the carrier. If the purchase is successful, the premium capability will be available for
+ * all applications to request through {@link ConnectivityManager#requestNetwork}.
+ */
+public class SlicePurchaseController extends Handler {
+ @NonNull private static final String TAG = "SlicePurchaseController";
+
+ /** Unknown failure code. */
+ public static final int FAILURE_CODE_UNKNOWN = 0;
+ /** Performance boost purchase failed because the carrier URL is unavailable. */
+ public static final int FAILURE_CODE_CARRIER_URL_UNAVAILABLE = 1;
+ /** Performance boost purchase failed because the server is unreachable. */
+ public static final int FAILURE_CODE_SERVER_UNREACHABLE = 2;
+ /** Performance boost purchase failed because user authentication failed. */
+ public static final int FAILURE_CODE_AUTHENTICATION_FAILED = 3;
+ /** Performance boost purchase failed because the payment failed. */
+ public static final int FAILURE_CODE_PAYMENT_FAILED = 4;
+
+ /**
+ * Failure codes that the carrier website can return when a premium capability purchase fails.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "FAILURE_CODE_" }, value = {
+ FAILURE_CODE_UNKNOWN,
+ FAILURE_CODE_CARRIER_URL_UNAVAILABLE,
+ FAILURE_CODE_SERVER_UNREACHABLE,
+ FAILURE_CODE_AUTHENTICATION_FAILED,
+ FAILURE_CODE_PAYMENT_FAILED})
+ public @interface FailureCode {}
+
+ /** Value for an invalid premium capability. */
+ public static final int PREMIUM_CAPABILITY_INVALID = -1;
+
+ /** Asset URL for the slice_purchase_test.html file. */
+ public static final String SLICE_PURCHASE_TEST_FILE =
+ "file:///android_asset/slice_purchase_test.html";
+
+ /** Purchasing the premium capability is no longer throttled. */
+ private static final int EVENT_PURCHASE_UNTHROTTLED = 1;
+ /** Slicing config changed. */
+ private static final int EVENT_SLICING_CONFIG_CHANGED = 2;
+ /** Start slice purchase application. */
+ private static final int EVENT_START_SLICE_PURCHASE_APP = 3;
+ /**
+ * Premium capability was not purchased within the timeout specified by
+ * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG}.
+ */
+ private static final int EVENT_PURCHASE_TIMEOUT = 4;
+ /**
+ * Network did not set up the slicing configuration within the timeout specified by
+ * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG}.
+ */
+ private static final int EVENT_SETUP_TIMEOUT = 5;
+ /** Device config changed. */
+ private static final int EVENT_DEVICE_CONFIG_CHANGED = 6;
+
+ /** UUID to report an anomaly when a premium capability is throttled twice in a row. */
+ private static final String UUID_CAPABILITY_THROTTLED_TWICE =
+ "15574927-e2e2-4593-99d4-2f340d22b383";
+ /** UUID to report an anomaly when receiving an invalid phone ID. */
+ private static final String UUID_INVALID_PHONE_ID = "ced79f1a-8ac0-4260-8cf3-08b54c0494f3";
+ /** UUID to report an anomaly when receiving an unknown action. */
+ private static final String UUID_UNKNOWN_ACTION = "0197efb0-dab1-4b0a-abaf-ac9336ec7923";
+ /** UUID to report an anomaly when receiving an unknown failure code with a non-empty reason. */
+ private static final String UUID_UNKNOWN_FAILURE_CODE = "76943b23-4415-400c-9855-b534fc4fc62c";
+ /**
+ * UUID to report an anomaly when the network fails to set up a slicing configuration after
+ * the user purchases a premium capability.
+ */
+ private static final String UUID_NETWORK_SETUP_FAILED = "12eeffbf-08f8-40ed-9a00-d344199552fc";
+
+ /**
+ * Action to start the slice purchase application and display the
+ * performance boost notification.
+ */
+ public static final String ACTION_START_SLICE_PURCHASE_APP =
+ "com.android.phone.slice.action.START_SLICE_PURCHASE_APP";
+ /** Action indicating the premium capability purchase was not completed in time. */
+ public static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT =
+ "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_TIMEOUT";
+ /** Action indicating the performance boost notification or WebView was canceled. */
+ private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED =
+ "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_CANCELED";
+ /** Action indicating a carrier error prevented premium capability purchase. */
+ private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR =
+ "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR";
+ /**
+ * Action indicating a Telephony or slice purchase application error prevented premium
+ * capability purchase.
+ */
+ private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED =
+ "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED";
+ /** Action indicating the purchase request was not made on the default data subscription. */
+ private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION =
+ "com.android.phone.slice.action."
+ + "SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION";
+ /** Action indicating the purchase request was successful. */
+ private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS =
+ "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_SUCCESS";
+ /**
+ * Action indicating the slice purchase application showed the performance boost notification.
+ */
+ private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN =
+ "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN";
+
+ /** Extra for the phone index to send to the slice purchase application. */
+ public static final String EXTRA_PHONE_ID = "com.android.phone.slice.extra.PHONE_ID";
+ /** Extra for the subscription ID to send to the slice purchase application. */
+ public static final String EXTRA_SUB_ID = "com.android.phone.slice.extra.SUB_ID";
+ /**
+ * Extra for the requested premium capability to purchase from the slice purchase application.
+ */
+ public static final String EXTRA_PREMIUM_CAPABILITY =
+ "com.android.phone.slice.extra.PREMIUM_CAPABILITY";
+ /** Extra for the carrier URL to display to the user to allow premium capability purchase. */
+ public static final String EXTRA_PURCHASE_URL = "com.android.phone.slice.extra.PURCHASE_URL";
+ /** Extra for the duration of the purchased premium capability. */
+ public static final String EXTRA_PURCHASE_DURATION =
+ "com.android.phone.slice.extra.PURCHASE_DURATION";
+ /** Extra for the {@link FailureCode} why the premium capability purchase failed. */
+ public static final String EXTRA_FAILURE_CODE = "com.android.phone.slice.extra.FAILURE_CODE";
+ /** Extra for the human-readable reason why the premium capability purchase failed. */
+ public static final String EXTRA_FAILURE_REASON =
+ "com.android.phone.slice.extra.FAILURE_REASON";
+ /** Extra for the user's carrier. */
+ public static final String EXTRA_CARRIER = "com.android.phone.slice.extra.CARRIER";
+ /** Extra for the user data received from the entitlement service to send to the webapp. */
+ public static final String EXTRA_USER_DATA = "com.android.phone.slice.extra.USER_DATA";
+ /**
+ * Extra for the canceled PendingIntent that the slice purchase application can send as a
+ * response if the performance boost notification or WebView was canceled by the user.
+ * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED}.
+ */
+ public static final String EXTRA_INTENT_CANCELED =
+ "com.android.phone.slice.extra.INTENT_CANCELED";
+ /**
+ * Extra for the carrier error PendingIntent that the slice purchase application can send as a
+ * response if the premium capability purchase request failed due to a carrier error.
+ * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR}.
+ * Sender can modify the intent to specify the failure code and reason for failure with
+ * {@link #EXTRA_FAILURE_CODE} and {@link #EXTRA_FAILURE_REASON}.
+ */
+ public static final String EXTRA_INTENT_CARRIER_ERROR =
+ "com.android.phone.slice.extra.INTENT_CARRIER_ERROR";
+ /**
+ * Extra for the request failed PendingIntent that the slice purchase application can send as a
+ * response if the premium capability purchase request failed due to an error in Telephony or
+ * the slice purchase application.
+ * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED}.
+ */
+ public static final String EXTRA_INTENT_REQUEST_FAILED =
+ "com.android.phone.slice.extra.INTENT_REQUEST_FAILED";
+ /**
+ * Extra for the not-default data subscription ID PendingIntent that the slice purchase
+ * application can send as a response if the premium capability purchase request failed because
+ * it was not requested on the default data subscription.
+ * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION}.
+ */
+ public static final String EXTRA_INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION =
+ "com.android.phone.slice.extra.INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION";
+ /**
+ * Extra for the success PendingIntent that the slice purchase application can send as a
+ * response if the premium capability purchase request was successful.
+ * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS}.
+ * Sender can modify the intent to specify a purchase duration with
+ * {@link #EXTRA_PURCHASE_DURATION}.
+ */
+ public static final String EXTRA_INTENT_SUCCESS =
+ "com.android.phone.slice.extra.INTENT_SUCCESS";
+ /**
+ * Extra for the PendingIntent that the slice purchase application can send to indicate
+ * that it displayed the performance boost notification to the user.
+ * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN}.
+ */
+ public static final String EXTRA_INTENT_NOTIFICATION_SHOWN =
+ "com.android.phone.slice.extra.NOTIFICATION_SHOWN";
+
+ /** Component name for the SlicePurchaseBroadcastReceiver. */
+ private static final ComponentName SLICE_PURCHASE_APP_COMPONENT_NAME =
+ ComponentName.unflattenFromString(
+ "com.android.carrierdefaultapp/.SlicePurchaseBroadcastReceiver");
+
+ /** Shared preference name for performance boost notification preferences. */
+ private static final String PERFORMANCE_BOOST_NOTIFICATION_PREFERENCES =
+ "performance_boost_notification_preferences";
+ /** Shared preference key for daily count of performance boost notifications. */
+ private static final String KEY_DAILY_NOTIFICATION_COUNT = "daily_notification_count";
+ /** Shared preference key for monthly count of performance boost notifications. */
+ private static final String KEY_MONTHLY_NOTIFICATION_COUNT = "monthly_notification_count";
+ /** DeviceConfig key for whether the slicing upsell feature is enabled. */
+ private static final String KEY_ENABLE_SLICING_UPSELL = "enable_slicing_upsell";
+ /**
+ * Shared preference key for the date the daily or monthly counts of performance boost
+ * notifications were last reset.
+ * A String with ISO-8601 format {@code YYYY-MM-DD}, from {@link LocalDate#toString}.
+ * For example, if the count was last updated on December 25, 2020, this would be `2020-12-25`.
+ */
+ private static final String KEY_NOTIFICATION_COUNT_LAST_RESET_DATE =
+ "notification_count_last_reset_date";
+
+ /** Map of phone ID -> SlicePurchaseController instances. */
+ @NonNull private static final Map<Integer, SlicePurchaseController> sInstances =
+ new HashMap<>();
+
+ /** The Phone instance used to create the SlicePurchaseController. */
+ @NonNull private final Phone mPhone;
+ /** The set of capabilities that are pending network setup. */
+ @NonNull private final Set<Integer> mPendingSetupCapabilities = new HashSet<>();
+ /** The set of throttled capabilities. */
+ @NonNull private final Set<Integer> mThrottledCapabilities = new HashSet<>();
+ /** A map of pending capabilities to the onComplete message for the purchase request. */
+ @NonNull private final Map<Integer, Message> mPendingPurchaseCapabilities = new HashMap<>();
+ /**
+ * A map of capabilities to the SlicePurchaseControllerBroadcastReceiver to handle
+ * slice purchase application responses.
+ */
+ @NonNull private final Map<Integer, SlicePurchaseControllerBroadcastReceiver>
+ mSlicePurchaseControllerBroadcastReceivers = new HashMap<>();
+ /** The current network slicing configuration. */
+ @Nullable private NetworkSlicingConfig mSlicingConfig;
+
+ /** LocalDate to use when resetting notification counts. {@code null} except when testing. */
+ @Nullable private LocalDate mLocalDate;
+ /** The number of times the performance boost notification has been shown today. */
+ private int mDailyCount;
+ /** The number of times the performance boost notification has been shown this month. */
+ private int mMonthlyCount;
+ /** {@code true} if the slicing upsell feature is enabled and {@code false} otherwise. */
+ private boolean mIsSlicingUpsellEnabled;
+
+ /**
+ * BroadcastReceiver to receive responses from the slice purchase application.
+ */
+ private class SlicePurchaseControllerBroadcastReceiver extends BroadcastReceiver {
+ @TelephonyManager.PremiumCapability private final int mCapability;
+
+ /**
+ * Create a SlicePurchaseControllerBroadcastReceiver for the given capability
+ *
+ * @param capability The requested capability to listen to response for.
+ */
+ SlicePurchaseControllerBroadcastReceiver(
+ @TelephonyManager.PremiumCapability int capability) {
+ mCapability = capability;
+ }
+
+ /**
+ * Process responses from the slice purchase application.
+ *
+ * @param context The Context in which the receiver is running.
+ * @param intent The Intent being received.
+ */
+ @Override
+ public void onReceive(@NonNull Context context, @NonNull Intent intent) {
+ String action = intent.getAction();
+ logd("SlicePurchaseControllerBroadcastReceiver("
+ + TelephonyManager.convertPremiumCapabilityToString(mCapability)
+ + ") received action: " + action);
+ int phoneId = intent.getIntExtra(EXTRA_PHONE_ID,
+ SubscriptionManager.INVALID_PHONE_INDEX);
+ int capability = intent.getIntExtra(EXTRA_PREMIUM_CAPABILITY,
+ PREMIUM_CAPABILITY_INVALID);
+ if (SlicePurchaseController.getInstance(phoneId) == null) {
+ reportAnomaly(UUID_INVALID_PHONE_ID, "SlicePurchaseControllerBroadcastReceiver( "
+ + TelephonyManager.convertPremiumCapabilityToString(mCapability)
+ + ") received invalid phoneId: " + phoneId);
+ return;
+ } else if (capability != mCapability) {
+ logd("SlicePurchaseControllerBroadcastReceiver("
+ + TelephonyManager.convertPremiumCapabilityToString(mCapability)
+ + ") ignoring intent for capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability));
+ return;
+ }
+ switch (action) {
+ case ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED: {
+ logd("Slice purchase application canceled for capability: "
+ + TelephonyManager.convertPremiumCapabilityToString(capability));
+ SlicePurchaseController.getInstance(phoneId)
+ .handlePurchaseResult(capability,
+ TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED,
+ true);
+ break;
+ }
+ case ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR: {
+ int failureCode = intent.getIntExtra(EXTRA_FAILURE_CODE, FAILURE_CODE_UNKNOWN);
+ String failureReason = intent.getStringExtra(EXTRA_FAILURE_REASON);
+ SlicePurchaseController.getInstance(phoneId).onCarrierError(
+ capability, failureCode, failureReason);
+ break;
+ }
+ case ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED: {
+ logd("Purchase premium capability request failed for capability: "
+ + TelephonyManager.convertPremiumCapabilityToString(capability));
+ SlicePurchaseController.getInstance(phoneId)
+ .handlePurchaseResult(capability,
+ TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED,
+ false);
+ break;
+ }
+ case ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION: {
+ logd("Purchase premium capability request was not made on the default data "
+ + "subscription for capability: "
+ + TelephonyManager.convertPremiumCapabilityToString(capability));
+ SlicePurchaseController.getInstance(phoneId)
+ .handlePurchaseResult(capability,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
+ false);
+ break;
+ }
+ case ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS: {
+ long duration = intent.getLongExtra(EXTRA_PURCHASE_DURATION, 0);
+ SlicePurchaseController.getInstance(phoneId).onCarrierSuccess(
+ capability, duration);
+ break;
+ }
+ case ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN: {
+ SlicePurchaseController.getInstance(phoneId).onNotificationShown();
+ break;
+ }
+ default:
+ reportAnomaly(UUID_UNKNOWN_ACTION, "SlicePurchaseControllerBroadcastReceiver("
+ + TelephonyManager.convertPremiumCapabilityToString(mCapability)
+ + ") received unknown action: " + action);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Get the static SlicePurchaseController instance for the given phone or create one if it
+ * doesn't exist.
+ *
+ * @param phone The Phone to get the SlicePurchaseController for.
+ * @return The static SlicePurchaseController instance.
+ */
+ @NonNull public static synchronized SlicePurchaseController getInstance(@NonNull Phone phone) {
+ // TODO: Add listeners for multi sim setting changed (maybe carrier config changed too)
+ // that dismiss notifications and update SlicePurchaseController instance
+ int phoneId = phone.getPhoneId();
+ if (sInstances.get(phoneId) == null) {
+ HandlerThread handlerThread = new HandlerThread("SlicePurchaseController");
+ handlerThread.start();
+ sInstances.put(phoneId, new SlicePurchaseController(phone, handlerThread.getLooper()));
+ }
+ return sInstances.get(phoneId);
+ }
+
+ /**
+ * Get the static SlicePurchaseController instance for the given phone ID if it exists.
+ *
+ * @param phoneId The phone ID to get the SlicePurchaseController for.
+ * @return The static SlicePurchaseController instance or
+ * {@code null} if it hasn't been created yet.
+ */
+ @Nullable private static SlicePurchaseController getInstance(int phoneId) {
+ return sInstances.get(phoneId);
+ }
+
+ /**
+ * Create a SlicePurchaseController for the given phone on the given looper.
+ *
+ * @param phone The Phone to create the SlicePurchaseController for.
+ * @param looper The Looper to run the SlicePurchaseController on.
+ */
+ @VisibleForTesting
+ public SlicePurchaseController(@NonNull Phone phone, @NonNull Looper looper) {
+ super(looper);
+ mPhone = phone;
+ // TODO: Create a cached value for slicing config in DataIndication and initialize here
+ mPhone.mCi.registerForSlicingConfigChanged(this, EVENT_SLICING_CONFIG_CHANGED, null);
+ mIsSlicingUpsellEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_SLICING_UPSELL, false);
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_TELEPHONY, this::post,
+ properties -> {
+ if (TextUtils.equals(DeviceConfig.NAMESPACE_TELEPHONY,
+ properties.getNamespace())) {
+ sendEmptyMessage(EVENT_DEVICE_CONFIG_CHANGED);
+ }
+ });
+ updateNotificationCounts();
+ }
+
+ /**
+ * Set the LocalDate to use for resetting daily and monthly notification counts.
+ *
+ * @param localDate The LocalDate instance to use.
+ */
+ @VisibleForTesting
+ public void setLocalDate(@NonNull LocalDate localDate) {
+ mLocalDate = localDate;
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case EVENT_PURCHASE_UNTHROTTLED: {
+ int capability = (int) msg.obj;
+ logd("EVENT_PURCHASE_UNTHROTTLED: for capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability));
+ mThrottledCapabilities.remove(capability);
+ break;
+ }
+ case EVENT_SLICING_CONFIG_CHANGED: {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ NetworkSlicingConfig config = (NetworkSlicingConfig) ar.result;
+ logd("EVENT_SLICING_CONFIG_CHANGED: from " + mSlicingConfig + " to " + config);
+ mSlicingConfig = config;
+ onSlicingConfigChanged();
+ break;
+ }
+ case EVENT_START_SLICE_PURCHASE_APP: {
+ int capability = (int) msg.obj;
+ logd("EVENT_START_SLICE_PURCHASE_APP: "
+ + TelephonyManager.convertPremiumCapabilityToString(capability));
+ onStartSlicePurchaseApplication(capability);
+ break;
+ }
+ case EVENT_PURCHASE_TIMEOUT: {
+ int capability = (int) msg.obj;
+ logd("EVENT_PURCHASE_TIMEOUT: for capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability));
+ onTimeout(capability);
+ break;
+ }
+ case EVENT_SETUP_TIMEOUT:
+ int capability = (int) msg.obj;
+ logd("EVENT_SETUP_TIMEOUT: for capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability));
+ onSetupTimeout(capability);
+ break;
+ case EVENT_DEVICE_CONFIG_CHANGED:
+ boolean isSlicingUpsellEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_SLICING_UPSELL, false);
+ if (isSlicingUpsellEnabled != mIsSlicingUpsellEnabled) {
+ logd("EVENT_DEVICE_CONFIG_CHANGED: from " + mIsSlicingUpsellEnabled + " to "
+ + isSlicingUpsellEnabled);
+ mIsSlicingUpsellEnabled = isSlicingUpsellEnabled;
+ }
+ break;
+ default:
+ loge("Unknown event: " + msg.obj);
+ }
+ }
+
+ /**
+ * Check whether the given premium capability is available for purchase from the carrier.
+ *
+ * @param capability The premium capability to check.
+ * @return Whether the given premium capability is available to purchase.
+ */
+ public boolean isPremiumCapabilityAvailableForPurchase(
+ @TelephonyManager.PremiumCapability int capability) {
+ if (!arePremiumCapabilitiesSupportedByDevice()) {
+ logd("Premium capabilities unsupported by the device.");
+ return false;
+ }
+ if (!isPremiumCapabilitySupportedByCarrier(capability)) {
+ logd("Premium capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability)
+ + " unsupported by the carrier.");
+ return false;
+ }
+ if (!isDefaultDataSub()) {
+ logd("Premium capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability)
+ + " unavailable on the non-default data subscription.");
+ return false;
+ }
+ logd("Premium capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability)
+ + " is available for purchase.");
+ return true;
+ }
+
+ /**
+ * Purchase the given premium capability from the carrier.
+ *
+ * @param capability The premium capability to purchase.
+ * @param onComplete The callback message to send when the purchase request is complete.
+ */
+ public synchronized void purchasePremiumCapability(
+ @TelephonyManager.PremiumCapability int capability, @NonNull Message onComplete) {
+ logd("purchasePremiumCapability: "
+ + TelephonyManager.convertPremiumCapabilityToString(capability));
+ // Check whether the premium capability can be purchased.
+ if (!arePremiumCapabilitiesSupportedByDevice()) {
+ sendPurchaseResult(capability,
+ TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
+ onComplete);
+ return;
+ }
+ if (!isPremiumCapabilitySupportedByCarrier(capability)) {
+ sendPurchaseResult(capability,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
+ onComplete);
+ return;
+ }
+ if (!isDefaultDataSub()) {
+ sendPurchaseResult(capability,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
+ onComplete);
+ return;
+ }
+ if (isSlicingConfigActive(capability)) {
+ sendPurchaseResult(capability,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
+ onComplete);
+ return;
+ }
+ if (mPendingSetupCapabilities.contains(capability)) {
+ sendPurchaseResult(capability,
+ TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP,
+ onComplete);
+ return;
+ }
+ if (mThrottledCapabilities.contains(capability)) {
+ sendPurchaseResult(capability,
+ TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED,
+ onComplete);
+ return;
+ }
+ if (!isNetworkAvailable()) {
+ sendPurchaseResult(capability,
+ TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
+ onComplete);
+ return;
+ }
+
+ if (mPendingPurchaseCapabilities.containsKey(capability)) {
+ sendPurchaseResult(capability,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
+ onComplete);
+ return;
+ }
+
+ // All state checks passed. Mark purchase pending and start the slice purchase application.
+ // Process through the handler since this method is synchronized.
+ mPendingPurchaseCapabilities.put(capability, onComplete);
+ sendMessage(obtainMessage(EVENT_START_SLICE_PURCHASE_APP, capability));
+ }
+
+ private void sendPurchaseResult(@TelephonyManager.PremiumCapability int capability,
+ @TelephonyManager.PurchasePremiumCapabilityResult int result,
+ @NonNull Message onComplete) {
+ // Send the onComplete message with the purchase result.
+ logd("Purchase result for capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability)
+ + ": " + TelephonyManager.convertPurchaseResultToString(result));
+ AsyncResult.forMessage(onComplete, result, null);
+ onComplete.sendToTarget();
+ }
+
+ private void handlePurchaseResult(
+ @TelephonyManager.PremiumCapability int capability,
+ @TelephonyManager.PurchasePremiumCapabilityResult int result, boolean throttle) {
+ SlicePurchaseControllerBroadcastReceiver receiver =
+ mSlicePurchaseControllerBroadcastReceivers.remove(capability);
+ if (receiver != null) {
+ mPhone.getContext().unregisterReceiver(receiver);
+ }
+ removeMessages(EVENT_PURCHASE_TIMEOUT, capability);
+ if (throttle) {
+ throttleCapability(capability, getThrottleDuration(result));
+ }
+ sendPurchaseResult(capability, result, mPendingPurchaseCapabilities.remove(capability));
+ }
+
+ private void throttleCapability(@TelephonyManager.PremiumCapability int capability,
+ long throttleDuration) {
+ // Throttle subsequent requests if necessary.
+ if (!mThrottledCapabilities.contains(capability)) {
+ if (throttleDuration > 0) {
+ logd("Throttle purchase requests for capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability) + " for "
+ + TimeUnit.MILLISECONDS.toMinutes(throttleDuration) + " minutes.");
+ mThrottledCapabilities.add(capability);
+ sendMessageDelayed(obtainMessage(EVENT_PURCHASE_UNTHROTTLED, capability),
+ throttleDuration);
+ }
+ } else {
+ reportAnomaly(UUID_CAPABILITY_THROTTLED_TWICE,
+ TelephonyManager.convertPremiumCapabilityToString(capability)
+ + " is already throttled.");
+ }
+ }
+
+ private void onSlicingConfigChanged() {
+ for (int capability : new int[] {TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY}) {
+ if (isSlicingConfigActive(capability) && hasMessages(EVENT_SETUP_TIMEOUT, capability)) {
+ logd("Successfully set up slicing configuration for "
+ + TelephonyManager.convertPremiumCapabilityToString(capability));
+ mPendingSetupCapabilities.remove(capability);
+ removeMessages(EVENT_SETUP_TIMEOUT, capability);
+ }
+ }
+ }
+
+ /**
+ * @return A new PremiumNetworkEntitlementApi object.
+ */
+ @VisibleForTesting
+ public PremiumNetworkEntitlementApi getPremiumNetworkEntitlementApi() {
+ return new PremiumNetworkEntitlementApi(mPhone, getCarrierConfigs());
+ }
+
+ private void onStartSlicePurchaseApplication(
+ @TelephonyManager.PremiumCapability int capability) {
+ final PremiumNetworkEntitlementApi premiumNetworkEntitlementApi =
+ getPremiumNetworkEntitlementApi();
+ PremiumNetworkEntitlementResponse premiumNetworkEntitlementResponse =
+ premiumNetworkEntitlementApi.checkEntitlementStatus(capability);
+
+ // invalid response for entitlement check
+ if (premiumNetworkEntitlementResponse == null
+ || premiumNetworkEntitlementResponse.isInvalidResponse()) {
+ loge("Invalid response for entitlement check: " + premiumNetworkEntitlementResponse);
+ handlePurchaseResult(capability,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR, true);
+ return;
+ }
+
+ if (!premiumNetworkEntitlementResponse.isPremiumNetworkCapabilityAllowed()) {
+ loge("Entitlement Check: Not allowed.");
+ handlePurchaseResult(capability,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED, true);
+ return;
+ }
+
+ if (premiumNetworkEntitlementResponse.isProvisioned()) {
+ logd("Entitlement Check: Already provisioned.");
+ handlePurchaseResult(capability,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED, true);
+ return;
+ }
+
+ if (premiumNetworkEntitlementResponse.isProvisioningInProgress()) {
+ logd("Entitlement Check: In progress.");
+ handlePurchaseResult(capability,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS, true);
+ return;
+ }
+
+ String userData = premiumNetworkEntitlementResponse.mServiceFlowUserData;
+ String purchaseUrl = getPurchaseUrl(premiumNetworkEntitlementResponse);
+ String carrier = getSimOperator();
+ if (TextUtils.isEmpty(purchaseUrl) || TextUtils.isEmpty(carrier)) {
+ handlePurchaseResult(capability,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED, false);
+ return;
+ }
+
+ updateNotificationCounts();
+ if (mMonthlyCount >= getCarrierConfigs().getInt(
+ CarrierConfigManager.KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT)
+ || mDailyCount >= getCarrierConfigs().getInt(
+ CarrierConfigManager.KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT)) {
+ logd("Reached maximum number of performance boost notifications.");
+ handlePurchaseResult(capability,
+ TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, false);
+ return;
+ }
+
+ // Start timeout for purchase completion.
+ long timeout = getCarrierConfigs().getLong(CarrierConfigManager
+ .KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG);
+ logd("Start purchase timeout for "
+ + TelephonyManager.convertPremiumCapabilityToString(capability) + " for "
+ + TimeUnit.MILLISECONDS.toMinutes(timeout) + " minutes.");
+ sendMessageDelayed(obtainMessage(EVENT_PURCHASE_TIMEOUT, capability), timeout);
+
+ // Broadcast start intent to start the slice purchase application
+ Intent intent = new Intent(ACTION_START_SLICE_PURCHASE_APP);
+ intent.setComponent(SLICE_PURCHASE_APP_COMPONENT_NAME);
+ intent.putExtra(EXTRA_PHONE_ID, mPhone.getPhoneId());
+ intent.putExtra(EXTRA_SUB_ID, mPhone.getSubId());
+ intent.putExtra(EXTRA_PREMIUM_CAPABILITY, capability);
+ intent.putExtra(EXTRA_PURCHASE_URL, purchaseUrl);
+ intent.putExtra(EXTRA_CARRIER, carrier);
+ if (!TextUtils.isEmpty(userData)) {
+ intent.putExtra(EXTRA_USER_DATA, userData);
+ }
+ intent.putExtra(EXTRA_INTENT_CANCELED, createPendingIntent(
+ ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED, capability, false));
+ intent.putExtra(EXTRA_INTENT_CARRIER_ERROR, createPendingIntent(
+ ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR, capability, true));
+ intent.putExtra(EXTRA_INTENT_REQUEST_FAILED, createPendingIntent(
+ ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED, capability, false));
+ intent.putExtra(EXTRA_INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION, createPendingIntent(
+ ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION, capability,
+ false));
+ intent.putExtra(EXTRA_INTENT_SUCCESS, createPendingIntent(
+ ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS, capability, true));
+ intent.putExtra(EXTRA_INTENT_NOTIFICATION_SHOWN, createPendingIntent(
+ ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN, capability, false));
+ logd("Broadcasting start intent to SlicePurchaseBroadcastReceiver.");
+ mPhone.getContext().sendBroadcast(intent);
+
+ // Listen for responses from the slice purchase application
+ mSlicePurchaseControllerBroadcastReceivers.put(capability,
+ new SlicePurchaseControllerBroadcastReceiver(capability));
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED);
+ filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR);
+ filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED);
+ filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION);
+ filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS);
+ filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN);
+ mPhone.getContext().registerReceiver(
+ mSlicePurchaseControllerBroadcastReceivers.get(capability), filter,
+ Context.RECEIVER_NOT_EXPORTED);
+ }
+
+ /**
+ * Get a valid purchase URL from either entitlement response or carrier configs, if one exists.
+ *
+ * @param entitlementResponse The entitlement response to get the purchase URL from.
+ * @return A valid purchase URL or an empty string if one doesn't exist.
+ */
+ @VisibleForTesting
+ @NonNull public String getPurchaseUrl(
+ @NonNull PremiumNetworkEntitlementResponse entitlementResponse) {
+ String purchaseUrl = entitlementResponse.mServiceFlowURL;
+ if (!isUrlValid(purchaseUrl)) {
+ purchaseUrl = getCarrierConfigs().getString(
+ CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING);
+ if (!isUrlValid(purchaseUrl)) {
+ purchaseUrl = "";
+ }
+ }
+ return purchaseUrl;
+ }
+
+ /**
+ * Get the SIM operator. This is the carrier name from the SIM rather than from the network,
+ * which will be the same regardless of whether the user is roaming or not.
+ *
+ * @return The operator name from the SIM.
+ */
+ @VisibleForTesting
+ @Nullable public String getSimOperator() {
+ if (mPhone.getPhoneId() < TelephonyProperties.icc_operator_alpha().size()) {
+ return TelephonyProperties.icc_operator_alpha().get(mPhone.getPhoneId());
+ }
+ return null;
+ }
+
+ /**
+ * Create the PendingIntent to allow the slice purchase application to send back responses.
+ *
+ * @param action The action that will be sent for this PendingIntent
+ * @param capability The premium capability that was requested.
+ * @param mutable {@code true} if the PendingIntent should be mutable and
+ * {@code false} if it should be immutable.
+ * @return The PendingIntent for the given action and capability.
+ */
+ @VisibleForTesting
+ @NonNull public PendingIntent createPendingIntent(@NonNull String action,
+ @TelephonyManager.PremiumCapability int capability, boolean mutable) {
+ Intent intent = new Intent(action);
+ intent.putExtra(EXTRA_PHONE_ID, mPhone.getPhoneId());
+ intent.putExtra(EXTRA_PREMIUM_CAPABILITY, capability);
+ intent.setPackage(mPhone.getContext().getPackageName());
+ return PendingIntent.getBroadcast(mPhone.getContext(), capability, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT
+ | (mutable ? PendingIntent.FLAG_MUTABLE : PendingIntent.FLAG_IMMUTABLE));
+ }
+
+ private void onTimeout(@TelephonyManager.PremiumCapability int capability) {
+ logd("onTimeout: " + TelephonyManager.convertPremiumCapabilityToString(capability));
+ // Broadcast timeout intent to clean up the slice purchase notification and activity
+ Intent intent = new Intent(ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT);
+ intent.setComponent(SLICE_PURCHASE_APP_COMPONENT_NAME);
+ intent.putExtra(EXTRA_PHONE_ID, mPhone.getPhoneId());
+ intent.putExtra(EXTRA_PREMIUM_CAPABILITY, capability);
+ logd("Broadcasting timeout intent to SlicePurchaseBroadcastReceiver.");
+ mPhone.getContext().sendBroadcast(intent);
+
+ handlePurchaseResult(
+ capability, TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT, true);
+ }
+
+ private void onCarrierError(@TelephonyManager.PremiumCapability int capability,
+ @FailureCode int failureCode, @Nullable String failureReason) {
+ logd("Carrier error for capability: "
+ + TelephonyManager.convertPremiumCapabilityToString(capability) + " with code: "
+ + convertFailureCodeToString(failureCode) + " and reason: " + failureReason);
+ if (failureCode == FAILURE_CODE_UNKNOWN && !TextUtils.isEmpty(failureReason)) {
+ reportAnomaly(UUID_UNKNOWN_FAILURE_CODE,
+ "Failure code needs to be added for: " + failureReason);
+ }
+ handlePurchaseResult(capability,
+ TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR, true);
+ }
+
+ private void onCarrierSuccess(@TelephonyManager.PremiumCapability int capability,
+ long duration) {
+ logd("Successfully purchased premium capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability) + (duration > 0
+ ? " for " + TimeUnit.MILLISECONDS.toMinutes(duration) + " minutes." : "."));
+ mPendingSetupCapabilities.add(capability);
+ long setupDuration = getCarrierConfigs().getLong(
+ CarrierConfigManager.KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG);
+ logd("Waiting " + TimeUnit.MILLISECONDS.toMinutes(setupDuration) + " minutes for the "
+ + "network to set up the slicing configuration.");
+ sendMessageDelayed(obtainMessage(EVENT_SETUP_TIMEOUT, capability), setupDuration);
+ handlePurchaseResult(
+ capability, TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS, false);
+ }
+
+ private void onSetupTimeout(@TelephonyManager.PremiumCapability int capability) {
+ logd("onSetupTimeout: " + TelephonyManager.convertPremiumCapabilityToString(capability));
+ mPendingSetupCapabilities.remove(capability);
+ if (!isSlicingConfigActive(capability)) {
+ reportAnomaly(UUID_NETWORK_SETUP_FAILED,
+ "Failed to set up slicing configuration for capability "
+ + TelephonyManager.convertPremiumCapabilityToString(capability)
+ + " within the time specified.");
+ }
+ }
+
+ private void onNotificationShown() {
+ SharedPreferences sp = mPhone.getContext().getSharedPreferences(
+ PERFORMANCE_BOOST_NOTIFICATION_PREFERENCES, 0);
+ mDailyCount = sp.getInt((KEY_DAILY_NOTIFICATION_COUNT + mPhone.getPhoneId()), 0) + 1;
+ mMonthlyCount = sp.getInt((KEY_MONTHLY_NOTIFICATION_COUNT + mPhone.getPhoneId()), 0) + 1;
+ logd("Performance boost notification was shown " + mDailyCount + " times today and "
+ + mMonthlyCount + " times this month.");
+
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putInt((KEY_DAILY_NOTIFICATION_COUNT + mPhone.getPhoneId()), mDailyCount);
+ editor.putInt((KEY_MONTHLY_NOTIFICATION_COUNT + mPhone.getPhoneId()), mMonthlyCount);
+ editor.apply();
+
+ // Don't call updateNotificationCounts here because it will be called whenever a new
+ // purchase request comes in or when SlicePurchaseController is initialized.
+ }
+
+ /**
+ * Update the current daily and monthly performance boost notification counts.
+ * If it has been at least a day since the last daily reset or at least a month since the last
+ * monthly reset, reset the current daily or monthly notification counts.
+ */
+ @VisibleForTesting
+ public void updateNotificationCounts() {
+ SharedPreferences sp = mPhone.getContext().getSharedPreferences(
+ PERFORMANCE_BOOST_NOTIFICATION_PREFERENCES, 0);
+ mDailyCount = sp.getInt((KEY_DAILY_NOTIFICATION_COUNT + mPhone.getPhoneId()), 0);
+ mMonthlyCount = sp.getInt((KEY_MONTHLY_NOTIFICATION_COUNT + mPhone.getPhoneId()), 0);
+
+ if (mLocalDate == null) {
+ // Standardize to UTC to prevent default time zone dependency
+ mLocalDate = LocalDate.now(ZoneId.of("UTC"));
+ }
+ LocalDate lastLocalDate = LocalDate.of(1, 1, 1);
+ String lastLocalDateString = sp.getString(
+ (KEY_NOTIFICATION_COUNT_LAST_RESET_DATE + mPhone.getPhoneId()), "");
+ if (!TextUtils.isEmpty(lastLocalDateString)) {
+ try {
+ lastLocalDate = LocalDate.parse(lastLocalDateString);
+ } catch (DateTimeParseException e) {
+ loge("Error parsing LocalDate from SharedPreferences: " + e);
+ }
+ }
+ logd("updateNotificationCounts: mDailyCount=" + mDailyCount + ", mMonthlyCount="
+ + mMonthlyCount + ", mLocalDate=" + mLocalDate + ", lastLocalDate="
+ + lastLocalDate);
+
+ boolean resetMonthly = lastLocalDate.getYear() != mLocalDate.getYear()
+ || lastLocalDate.getMonthValue() != mLocalDate.getMonthValue();
+ boolean resetDaily = resetMonthly
+ || lastLocalDate.getDayOfMonth() != mLocalDate.getDayOfMonth();
+ if (resetDaily) {
+ logd("Resetting daily" + (resetMonthly ? " and monthly" : "") + " notification count.");
+ SharedPreferences.Editor editor = sp.edit();
+ if (resetMonthly) {
+ mMonthlyCount = 0;
+ editor.putInt((KEY_MONTHLY_NOTIFICATION_COUNT + mPhone.getPhoneId()),
+ mMonthlyCount);
+ }
+ mDailyCount = 0;
+ editor.putInt((KEY_DAILY_NOTIFICATION_COUNT + mPhone.getPhoneId()), mDailyCount);
+ editor.putString((KEY_NOTIFICATION_COUNT_LAST_RESET_DATE + mPhone.getPhoneId()),
+ mLocalDate.toString());
+ editor.apply();
+ }
+ }
+
+ @Nullable private PersistableBundle getCarrierConfigs() {
+ return mPhone.getContext().getSystemService(CarrierConfigManager.class)
+ .getConfigForSubId(mPhone.getSubId());
+ }
+
+ private long getThrottleDuration(@TelephonyManager.PurchasePremiumCapabilityResult int result) {
+ if (result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED
+ || result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT) {
+ return getCarrierConfigs().getLong(CarrierConfigManager
+ .KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG);
+ }
+ if (result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED
+ || result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR) {
+ return getCarrierConfigs().getLong(CarrierConfigManager
+ .KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG);
+ }
+ return 0;
+ }
+
+ private boolean isPremiumCapabilitySupportedByCarrier(
+ @TelephonyManager.PremiumCapability int capability) {
+ int[] supportedCapabilities = getCarrierConfigs().getIntArray(
+ CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY);
+ if (supportedCapabilities == null) {
+ logd("No premium capabilities are supported by the carrier.");
+ return false;
+ }
+ return Arrays.stream(supportedCapabilities)
+ .anyMatch(supportedCapability -> supportedCapability == capability);
+ }
+
+ private boolean isUrlValid(@Nullable String url) {
+ if (!URLUtil.isValidUrl(url)) {
+ loge("Invalid URL: " + url);
+ return false;
+ }
+ if (URLUtil.isAssetUrl(url) && !url.equals(SLICE_PURCHASE_TEST_FILE)) {
+ loge("Invalid asset: " + url);
+ return false;
+ }
+ try {
+ new URL(url).toURI();
+ } catch (MalformedURLException | URISyntaxException e) {
+ loge("Invalid URI: " + url);
+ return false;
+ }
+ logd("Valid URL: " + url);
+ return true;
+ }
+
+ private boolean arePremiumCapabilitiesSupportedByDevice() {
+ if ((mPhone.getCachedAllowedNetworkTypesBitmask()
+ & TelephonyManager.NETWORK_TYPE_BITMASK_NR) == 0) {
+ logd("Premium capabilities unsupported because NR is not allowed on the device.");
+ return false;
+ }
+ if (!mIsSlicingUpsellEnabled) {
+ logd("Premium capabilities unsupported because "
+ + "slicing upsell is disabled on the device.");
+ }
+ return mIsSlicingUpsellEnabled;
+ }
+
+ private boolean isDefaultDataSub() {
+ return mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId();
+ }
+
+ private boolean isSlicingConfigActive(@TelephonyManager.PremiumCapability int capability) {
+ if (mSlicingConfig == null) {
+ return false;
+ }
+ int capabilityServiceType = getSliceServiceType(capability);
+ for (NetworkSliceInfo sliceInfo : mSlicingConfig.getSliceInfo()) {
+ if (sliceInfo.getSliceServiceType() == capabilityServiceType
+ && sliceInfo.getStatus() == NetworkSliceInfo.SLICE_STATUS_ALLOWED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @NetworkSliceInfo.SliceServiceType private int getSliceServiceType(
+ @TelephonyManager.PremiumCapability int capability) {
+ if (capability == TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY) {
+ return NetworkSliceInfo.SLICE_SERVICE_TYPE_URLLC;
+ }
+ return NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE;
+ }
+
+ private boolean isNetworkAvailable() {
+ if (mPhone.getServiceState().getDataRoaming()) {
+ logd("Network unavailable because device is roaming.");
+ return false;
+ }
+
+ if (!mPhone.getDataSettingsManager().isDataEnabledForReason(
+ TelephonyManager.DATA_ENABLED_REASON_USER)) {
+ logd("Network unavailable because user data is disabled.");
+ return false;
+ }
+
+ // TODO (b/251558673): Create a listener for data network type changed to dismiss
+ // notification and activity when the network is no longer available.
+ switch (mPhone.getServiceState().getDataNetworkType()) {
+ case TelephonyManager.NETWORK_TYPE_NR:
+ return true;
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ case TelephonyManager.NETWORK_TYPE_LTE_CA:
+ return getCarrierConfigs().getBoolean(
+ CarrierConfigManager.KEY_PREMIUM_CAPABILITY_SUPPORTED_ON_LTE_BOOL);
+ }
+ return false;
+ }
+
+ /**
+ * Returns the failure code {@link FailureCode} as a String.
+ *
+ * @param failureCode The failure code.
+ * @return The failure code as a String.
+ */
+ @NonNull private static String convertFailureCodeToString(@FailureCode int failureCode) {
+ switch (failureCode) {
+ case FAILURE_CODE_UNKNOWN: return "UNKNOWN";
+ case FAILURE_CODE_CARRIER_URL_UNAVAILABLE: return "CARRIER_URL_UNAVAILABLE";
+ case FAILURE_CODE_SERVER_UNREACHABLE: return "SERVER_UNREACHABLE";
+ case FAILURE_CODE_AUTHENTICATION_FAILED: return "AUTHENTICATION_FAILED";
+ case FAILURE_CODE_PAYMENT_FAILED: return "PAYMENT_FAILED";
+ default:
+ return "UNKNOWN(" + failureCode + ")";
+ }
+ }
+
+ private void reportAnomaly(@NonNull String uuid, @NonNull String log) {
+ loge(log);
+ AnomalyReporter.reportAnomaly(UUID.fromString(uuid), log);
+ }
+
+ private void logd(String s) {
+ Log.d(TAG + "-" + mPhone.getPhoneId(), s);
+ }
+
+ private void loge(String s) {
+ Log.e(TAG + "-" + mPhone.getPhoneId(), s);
+ }
+}
diff --git a/src/com/android/phone/utils/CarrierAllowListInfo.java b/src/com/android/phone/utils/CarrierAllowListInfo.java
new file mode 100644
index 0000000..8e22cb9
--- /dev/null
+++ b/src/com/android/phone/utils/CarrierAllowListInfo.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2023 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.phone.utils;
+
+import android.annotation.TestApi;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+
+public class CarrierAllowListInfo {
+ private static final String LOG_TAG = "CarrierAllowListInfo";
+ private JSONObject mDataJSON;
+ private static final String JSON_CHARSET = "UTF-8";
+ private static final String MESSAGE_DIGEST_ALGORITHM = "SHA1";
+ private static final String CALLER_SHA_1_ID = "callerSHA1Id";
+ private static final String CALLER_CARRIER_ID = "carrierId";
+ public static final int INVALID_CARRIER_ID = -1;
+
+ private static final String CARRIER_RESTRICTION_OPERATOR_REGISTERED_FILE =
+ "CarrierRestrictionOperatorDetails.json";
+
+ private static CarrierAllowListInfo mInstance = null;
+ private Context mContext;
+
+ private CarrierAllowListInfo(Context context) {
+ mContext = context;
+ loadJsonFile(context);
+ }
+
+ public static CarrierAllowListInfo loadInstance(Context context) {
+ if (mInstance == null) {
+ mInstance = new CarrierAllowListInfo(context);
+ }
+ return mInstance;
+ }
+
+ public int validateCallerAndGetCarrierId(String packageName) {
+ CarrierInfo carrierInfo = parseJsonForCallerInfo(packageName);
+ boolean isValid = (carrierInfo != null) && validateCallerSignature(mContext, packageName,
+ carrierInfo.getSHAIdList());
+ return (isValid) ? carrierInfo.getCallerCarrierId() : INVALID_CARRIER_ID;
+ }
+
+ private void loadJsonFile(Context context) {
+ try {
+ String jsonString = getJsonFromAssets(context,
+ CARRIER_RESTRICTION_OPERATOR_REGISTERED_FILE, JSON_CHARSET);
+ if (!TextUtils.isEmpty(jsonString)) {
+ mDataJSON = new JSONObject(jsonString);
+ }
+ } catch (Exception ex) {
+ Rlog.e(LOG_TAG, "CarrierAllowListInfo: JSON file reading exception = " + ex);
+ }
+ }
+
+ /**
+ * Parse the JSON object to fetch the given caller's SHA-Ids and carrierId.
+ */
+ private CarrierInfo parseJsonForCallerInfo(String callerPackage) {
+ try {
+ if (mDataJSON != null && callerPackage != null) {
+ JSONObject callerJSON = mDataJSON.getJSONObject(callerPackage.trim());
+ JSONArray callerJSONArray = callerJSON.getJSONArray(CALLER_SHA_1_ID);
+ int carrierId = callerJSON.getInt(CALLER_CARRIER_ID);
+ List<String> appSignatures = new ArrayList<>();
+ for (int index = 0; index < callerJSONArray.length(); index++) {
+ appSignatures.add((String) callerJSONArray.get(index));
+ }
+ return new CarrierInfo(carrierId, appSignatures);
+ }
+ } catch (JSONException ex) {
+ Rlog.e(LOG_TAG, "getCallerSignatureInfo: JSONException = " + ex);
+ }
+ return null;
+ }
+
+ /**
+ * Read the Json file from the assert folder.
+ *
+ * @param context context
+ * @param fileName JSON file name in assets folder
+ * @param charset JSON file data format
+ * @return JSON file content in string format or null in case of IOException
+ */
+ private static String getJsonFromAssets(Context context, String fileName, String charset) {
+ String jsonStr;
+ try {
+ InputStream ipStream = context.getAssets().open(fileName);
+ int bufSize = ipStream.available();
+ byte[] fileBuffer = new byte[bufSize];
+ ipStream.read(fileBuffer);
+ ipStream.close();
+ jsonStr = new String(fileBuffer, charset);
+ } catch (IOException ex) {
+ Rlog.e(LOG_TAG, "getJsonFromAssets: Exception = " + ex);
+ return null;
+ }
+ return jsonStr;
+ }
+
+ /**
+ * API fetches all the related signatures of the given package from the packageManager
+ * and validate all the signatures.
+ *
+ * @param context context
+ * @param packageName package name of the caller to validate the signatures.
+ * @param allowListSignatures list of signatures to be validated.
+ * @return {@code true} if all the signatures are available with package manager.
+ * {@code false} if any one of the signatures won't match with package manager.
+ */
+ public static boolean validateCallerSignature(Context context, String packageName,
+ List<String> allowListSignatures) {
+ if (TextUtils.isEmpty(packageName) || allowListSignatures.size() == 0) {
+ // package name is mandatory
+ return false;
+ }
+ final PackageManager packageManager = context.getPackageManager();
+ try {
+ MessageDigest sha1MDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM);
+ final PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNATURES);
+ for (Signature signature : packageInfo.signatures) {
+ final byte[] signatureSha1 = sha1MDigest.digest(signature.toByteArray());
+ final String hexSignatureSha1 = IccUtils.bytesToHexString(signatureSha1);
+ if (!allowListSignatures.contains(hexSignatureSha1)) {
+ return false;
+ }
+ }
+ return true;
+ } catch (NoSuchAlgorithmException | PackageManager.NameNotFoundException ex) {
+ Rlog.e(LOG_TAG, "validateCallerSignature: Exception = " + ex);
+ return false;
+ }
+ }
+
+ public int updateJsonForTest(String callerInfo) {
+ try {
+ if (callerInfo == null) {
+ // reset the Json content after testing
+ loadJsonFile(mContext);
+ } else {
+ mDataJSON = new JSONObject(callerInfo);
+ }
+ return 0;
+ } catch (JSONException ex) {
+ Rlog.e(LOG_TAG, "updateJsonForTest: Exception = " + ex);
+ }
+ return -1;
+ }
+
+ private static class CarrierInfo {
+ final private int mCallerCarrierId;
+ final private List<String> mSHAIdList;
+
+ public CarrierInfo(int carrierId, List<String> SHAIds) {
+ mCallerCarrierId = carrierId;
+ mSHAIdList = SHAIds;
+ }
+
+ public int getCallerCarrierId() {
+ return mCallerCarrierId;
+ }
+
+ public List<String> getSHAIdList() {
+ return mSHAIdList;
+ }
+ }
+
+ @TestApi
+ public List<String> getShaIdList(String srcPkg, int carrierId) {
+ CarrierInfo carrierInfo = parseJsonForCallerInfo(srcPkg);
+ if (carrierInfo != null && carrierInfo.getCallerCarrierId() == carrierId) {
+ return carrierInfo.getSHAIdList();
+ }
+ Rlog.e(LOG_TAG, "getShaIdList carrierId or shaIdList is empty");
+ return Collections.EMPTY_LIST;
+ }
+}
diff --git a/src/com/android/phone/vvm/RemoteVvmTaskManager.java b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
index 6b60303..daa5d67b 100644
--- a/src/com/android/phone/vvm/RemoteVvmTaskManager.java
+++ b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
@@ -32,6 +32,7 @@
import android.os.Messenger;
import android.os.PersistableBundle;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
@@ -182,6 +183,7 @@
if (targetPackage != null && !TextUtils.equals(packageName, targetPackage)) {
VvmLog.w(TAG, "target package " + targetPackage
+ " is no longer the active VisualVoicemailService, ignoring");
+ continue;
}
ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(info);
return new ComponentName(componentInfo.packageName, componentInfo.name);
@@ -239,6 +241,7 @@
PhoneAccountHandle phoneAccountHandle = intent.getExtras()
.getParcelable(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE);
int subId = PhoneAccountHandleConverter.toSubId(phoneAccountHandle);
+ UserHandle userHandle = phoneAccountHandle.getUserHandle();
ComponentName remotePackage = getRemotePackage(this, subId,
intent.getStringExtra(EXTRA_TARGET_PACKAGE));
if (remotePackage == null) {
@@ -250,13 +253,15 @@
switch (intent.getAction()) {
case ACTION_START_CELL_SERVICE_CONNECTED:
send(remotePackage, VisualVoicemailService.MSG_ON_CELL_SERVICE_CONNECTED,
- intent.getExtras());
+ intent.getExtras(), userHandle);
break;
case ACTION_START_SMS_RECEIVED:
- send(remotePackage, VisualVoicemailService.MSG_ON_SMS_RECEIVED, intent.getExtras());
+ send(remotePackage, VisualVoicemailService.MSG_ON_SMS_RECEIVED, intent.getExtras(),
+ userHandle);
break;
case ACTION_START_SIM_REMOVED:
- send(remotePackage, VisualVoicemailService.MSG_ON_SIM_REMOVED, intent.getExtras());
+ send(remotePackage, VisualVoicemailService.MSG_ON_SIM_REMOVED, intent.getExtras(),
+ userHandle);
break;
default:
Assert.fail("Unexpected action +" + intent.getAction());
@@ -335,7 +340,7 @@
}
}
- private void send(ComponentName remotePackage, int what, Bundle extras) {
+ private void send(ComponentName remotePackage, int what, Bundle extras, UserHandle userHandle) {
Assert.isMainThread();
if (getBroadcastPackage(this) != null) {
@@ -351,7 +356,7 @@
intent.putExtras(extras);
intent.putExtra(EXTRA_WHAT, what);
intent.setComponent(remotePackage);
- sendBroadcast(intent);
+ sendBroadcastAsUser(intent, userHandle);
return;
}
@@ -367,7 +372,7 @@
Intent intent = newBindIntent(this);
intent.setComponent(remotePackage);
VvmLog.i(TAG, "Binding to " + intent.getComponent());
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, userHandle);
}
}
diff --git a/src/com/android/services/telephony/ConferenceParticipantConnection.java b/src/com/android/services/telephony/ConferenceParticipantConnection.java
index cbfe9ed..8179817 100644
--- a/src/com/android/services/telephony/ConferenceParticipantConnection.java
+++ b/src/com/android/services/telephony/ConferenceParticipantConnection.java
@@ -32,6 +32,8 @@
import com.android.phone.PhoneGlobals;
import com.android.telephony.Rlog;
+import java.util.Locale;
+
/**
* Represents a participant in a conference call.
*/
@@ -207,7 +209,7 @@
// The SubscriptionInfo reports ISO country codes in lower case. Convert to upper case,
// since ultimately we use this ISO when formatting the CEP phone number, and the phone
// number formatting library expects uppercase ISO country codes.
- return subInfo.getCountryIso().toUpperCase();
+ return subInfo.getCountryIso().toUpperCase(Locale.ROOT);
}
/**
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 587ac43..d36f8be 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -232,6 +232,7 @@
case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED:
case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL:
case android.telephony.DisconnectCause.MEDIA_TIMEOUT:
+ case android.telephony.DisconnectCause.SATELLITE_ENABLED:
return DisconnectCause.ERROR;
case android.telephony.DisconnectCause.DIALED_MMI:
@@ -418,6 +419,9 @@
case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
resourceId = R.string.callFailed_wfc_service_not_available_in_this_location;
break;
+ case android.telephony.DisconnectCause.SATELLITE_ENABLED:
+ resourceId = R.string.incall_error_satellite_enabled;
+ break;
default:
break;
}
@@ -599,6 +603,9 @@
case android.telephony.DisconnectCause.OUT_OF_SERVICE:
resourceId = R.string.clh_incall_error_out_of_service_txt;
break;
+ case android.telephony.DisconnectCause.SATELLITE_ENABLED:
+ resourceId = R.string.clh_callFailed_satelliteEnabled_txt;
+ break;
default:
resourceId = R.string.clh_card_title_call_ended_txt;
break;
@@ -822,6 +829,9 @@
case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
resourceId = R.string.callFailed_wfc_service_not_available_in_this_location;
break;
+ case android.telephony.DisconnectCause.SATELLITE_ENABLED:
+ resourceId = R.string.incall_error_satellite_enabled;
+ break;
default:
break;
}
diff --git a/src/com/android/services/telephony/HoldTracker.java b/src/com/android/services/telephony/HoldTracker.java
index 5032b41..2c6e56c 100644
--- a/src/com/android/services/telephony/HoldTracker.java
+++ b/src/com/android/services/telephony/HoldTracker.java
@@ -16,71 +16,55 @@
package com.android.services.telephony;
-import android.telecom.PhoneAccountHandle;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.HashSet;
+import java.util.Set;
/**
+ * Tracks and updates the hold capability of every call or conference across PhoneAccountHandles.
+ *
* @hide
*/
public class HoldTracker {
- private final Map<PhoneAccountHandle, List<Holdable>> mHoldables;
+ private final Set<Holdable> mHoldables;
public HoldTracker() {
- mHoldables = new HashMap<>();
+ mHoldables = new HashSet<>();
}
/**
- * Adds the holdable associated with the {@code phoneAccountHandle}, this method may update
- * the hold state for all holdable associated with the {@code phoneAccountHandle}.
+ * Adds the holdable, and updates the hold capability for all holdables.
*/
- public void addHoldable(PhoneAccountHandle phoneAccountHandle, Holdable holdable) {
- if (!mHoldables.containsKey(phoneAccountHandle)) {
- mHoldables.put(phoneAccountHandle, new ArrayList<>(1));
- }
- List<Holdable> holdables = mHoldables.get(phoneAccountHandle);
- if (!holdables.contains(holdable)) {
- holdables.add(holdable);
- updateHoldCapability(phoneAccountHandle);
+ public void addHoldable(Holdable holdable) {
+ if (!mHoldables.contains(holdable)) {
+ mHoldables.add(holdable);
+ updateHoldCapability();
}
}
/**
- * Removes the holdable associated with the {@code phoneAccountHandle}, this method may update
- * the hold state for all holdable associated with the {@code phoneAccountHandle}.
+ * Removes the holdable, and updates the hold capability for all holdable.
*/
- public void removeHoldable(PhoneAccountHandle phoneAccountHandle, Holdable holdable) {
- if (!mHoldables.containsKey(phoneAccountHandle)) {
- return;
- }
-
- if (mHoldables.get(phoneAccountHandle).remove(holdable)) {
- updateHoldCapability(phoneAccountHandle);
+ public void removeHoldable(Holdable holdable) {
+ if (mHoldables.remove(holdable)) {
+ updateHoldCapability();
}
}
/**
- * Updates the hold capability for all holdables associated with the {@code phoneAccountHandle}.
+ * Updates the hold capability for all tracked holdables.
*/
- public void updateHoldCapability(PhoneAccountHandle phoneAccountHandle) {
- if (!mHoldables.containsKey(phoneAccountHandle)) {
- return;
- }
-
- List<Holdable> holdables = mHoldables.get(phoneAccountHandle);
+ public void updateHoldCapability() {
int topHoldableCount = 0;
- for (Holdable holdable : holdables) {
+ for (Holdable holdable : mHoldables) {
if (!holdable.isChildHoldable()) {
++topHoldableCount;
}
}
- Log.d(this, "topHoldableCount = " + topHoldableCount);
+ Log.d(this, "updateHoldCapability(): topHoldableCount = "
+ + topHoldableCount);
boolean isHoldable = topHoldableCount < 2;
- for (Holdable holdable : holdables) {
+ for (Holdable holdable : mHoldables) {
holdable.setHoldable(holdable.isChildHoldable() ? false : isHoldable);
}
}
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index c62b4fa..755c85f 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -968,6 +968,7 @@
// of the participants, we can get into a situation where the participant is added twice.
synchronized (mUpdateSyncRoot) {
int oldParticipantCount = mConferenceParticipantConnections.size();
+ boolean wasFullConference = isFullConference();
boolean newParticipantsAdded = false;
boolean oldParticipantsRemoved = false;
ArrayList<ConferenceParticipant> newParticipants = new ArrayList<>(participants.size());
@@ -980,23 +981,25 @@
// event package; some carriers are known to keep a disconnected participant around in
// subsequent CEP updates with a state of disconnected, even though its no longer part
// of the conference.
- // Note: We consider 0 to still be a single party conference since some carriers will
- // send a conference event package with JUST the host in it when the conference is
- // disconnected. We don't want to change back to conference mode prior to disconnection
- // or we will not log the call.
- boolean isSinglePartyConference = participants.stream()
+ final long numActiveCepParticipantsOtherThanHost = participants.stream()
.filter(p -> {
Pair<Uri, Uri> pIdent = new Pair<>(p.getHandle(), p.getEndpoint());
return !Objects.equals(mHostParticipantIdentity, pIdent)
&& p.getState() != Connection.STATE_DISCONNECTED;
})
- .count() <= 1;
+ .count();
+ // We consider 0 to still be a single party conference since some carriers
+ // will send a conference event package with JUST the host in it when the conference
+ // is disconnected. We don't want to change back to conference mode prior to
+ // disconnection or we will not log the call.
+ final boolean isCepForSinglePartyConference =
+ numActiveCepParticipantsOtherThanHost <= 1;
// We will only process the CEP data if:
// 1. We're not emulating a single party call.
// 2. We're emulating a single party call and the CEP contains more than just the
// single party
- if ((!isMultiparty() && !isSinglePartyConference)
+ if ((!isMultiparty() && !isCepForSinglePartyConference)
|| isMultiparty()) {
// Add any new participants and update existing.
for (ConferenceParticipant participant : participants) {
@@ -1082,15 +1085,17 @@
int newParticipantCount = mConferenceParticipantConnections.size();
Log.v(this, "handleConferenceParticipantsUpdate: oldParticipantCount=%d, "
- + "newParticipantcount=%d", oldParticipantCount, newParticipantCount);
- // If the single party call emulation fature flag is enabled, we can potentially treat
+ + "newParticipantCount=%d, isMultiPty=%b, cepParticipantCt=%d",
+ oldParticipantCount, newParticipantCount, isMultiparty(),
+ numActiveCepParticipantsOtherThanHost);
+ // If the single party call emulation feature flag is enabled, we can potentially treat
// the conference as a single party call when there is just one participant.
if (mFeatureFlagProxy.isUsingSinglePartyCallEmulation() &&
!mConferenceHost.isAdhocConferenceCall()) {
if (oldParticipantCount != 1 && newParticipantCount == 1) {
// If number of participants goes to 1, emulate a single party call.
startEmulatingSinglePartyCall();
- } else if (!isMultiparty() && !isSinglePartyConference) {
+ } else if (!isMultiparty() && !isCepForSinglePartyConference) {
// Number of participants increased, so stop emulating a single party call.
stopEmulatingSinglePartyCall();
}
@@ -1102,14 +1107,20 @@
updateManageConference();
}
+ // If the "fullness" of the conference changed, we need to inform listeners.
+ // Ie tell ImsConferenceController.
+ if (wasFullConference != isFullConference()) {
+ notifyConferenceCapacityChanged();
+ }
+
// If the conference is empty and we're supposed to do a local disconnect, do so now.
if (mCarrierConfig.shouldLocalDisconnectEmptyConference()
// If we dropped from > 0 participants to zero
// OR if the conference had a single participant and is emulating a standalone
// call.
&& (oldParticipantCount > 0 || !isMultiparty())
- // AND the CEP says there is nobody left any more.
- && newParticipantCount == 0) {
+ // AND the CEP says there is nobody left anymore.
+ && numActiveCepParticipantsOtherThanHost == 0) {
Log.i(this, "handleConferenceParticipantsUpdate: empty conference; "
+ "local disconnect.");
onDisconnect();
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 7cf9415..fa2151b 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -16,6 +16,7 @@
package com.android.services.telephony;
+import android.annotation.NonNull;
import android.content.Context;
import android.os.PersistableBundle;
import android.telecom.Conference;
@@ -26,17 +27,17 @@
import android.telecom.PhoneAccountHandle;
import android.telephony.CarrierConfigManager;
-import com.android.telephony.Rlog;
-
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.phone.PhoneUtils;
+import com.android.telephony.Rlog;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.stream.Collectors;
/**
@@ -51,6 +52,14 @@
private final TelephonyConferenceBase.TelephonyConferenceListener mConferenceListener =
new TelephonyConferenceBase.TelephonyConferenceListener() {
@Override
+ public void onConferenceCapacityChanged() {
+ // If the conference reached or is no longer at capacity then we need to recalculate
+ // as it may be possible to merge or not merge now.
+ Log.i(ImsConferenceController.this, "onConferenceCapacityChanged: recalc");
+ recalculateConferenceable();
+ }
+
+ @Override
public void onDestroyed(Conference conference) {
if (Log.VERBOSE) {
Log.v(ImsConferenceController.class, "onDestroyed: %s", conference);
@@ -220,14 +229,33 @@
recalculateConference();
}
+ private PhoneAccountHandle getPhoneAccountHandle(@NonNull Conferenceable c) {
+ if (c instanceof Connection) {
+ Connection connection = (Connection) c;
+ return connection.getPhoneAccountHandle();
+ } else if (c instanceof Conference) {
+ Conference conference = (Conference) c;
+ return conference.getPhoneAccountHandle();
+ }
+ throw new IllegalArgumentException("Unrecognized Conferenceable!" + c);
+ }
+
+ private boolean isSamePhoneAccountHandle(
+ @NonNull Conferenceable left, @NonNull Conferenceable right) {
+ PhoneAccountHandle leftHandle = getPhoneAccountHandle(left);
+ PhoneAccountHandle rightHandle = getPhoneAccountHandle(right);
+ return Objects.equals(leftHandle, rightHandle);
+ }
+
/**
* Calculates the conference-capable state of all GSM connections in this connection service.
+ * Connections from different {@link PhoneAccountHandle}s shall not be conferenceable.
*/
private void recalculateConferenceable() {
Log.v(this, "recalculateConferenceable : %d", mTelephonyConnections.size());
HashSet<Conferenceable> conferenceableSet = new HashSet<>(mTelephonyConnections.size() +
mImsConferences.size());
- HashSet<Conferenceable> conferenceParticipantsSet = new HashSet<>();
+ HashSet<Connection> conferenceParticipantsSet = new HashSet<>();
// Loop through and collect all calls which are active or holding
for (TelephonyConnection connection : mTelephonyConnections) {
@@ -300,11 +328,6 @@
for (Conferenceable c : conferenceableSet) {
if (c instanceof Connection) {
- // Remove this connection from the Set and add all others
- List<Conferenceable> conferenceables = conferenceableSet
- .stream()
- .filter(conferenceable -> c != conferenceable)
- .collect(Collectors.toList());
// TODO: Remove this once RemoteConnection#setConferenceableConnections is fixed.
// Add all conference participant connections as conferenceable with a standalone
// Connection. We need to do this to ensure that RemoteConnections work properly.
@@ -313,7 +336,18 @@
// into the conference.
// We should add support for RemoteConnection#setConferenceables, which accepts a
// list of remote conferences and connections in the future.
- conferenceables.addAll(conferenceParticipantsSet);
+ List<Conferenceable> conferenceables = conferenceParticipantsSet
+ .stream()
+ // Removes conference participants from different PhoneAccountHandles.
+ .filter(connection -> isSamePhoneAccountHandle(c, connection))
+ .collect(Collectors.toCollection(ArrayList::new));
+
+ // Removes this connection from the Set and add all others. Removes conferenceables
+ // from different PhoneAccountHandles.
+ conferenceables.addAll(conferenceableSet
+ .stream()
+ .filter(conferenceable -> c != conferenceable
+ && isSamePhoneAccountHandle(c, conferenceable)).toList());
((Connection) c).setConferenceables(conferenceables);
} else if (c instanceof ImsConference) {
@@ -325,10 +359,11 @@
}
// Remove all conferences from the set, since we can not conference a conference
- // to another conference.
+ // to another conference. Removes connections from different PhoneAccountHandles.
List<Connection> connections = conferenceableSet
.stream()
- .filter(conferenceable -> conferenceable instanceof Connection)
+ .filter(conferenceable -> conferenceable instanceof Connection
+ && isSamePhoneAccountHandle(c, conferenceable))
.map(conferenceable -> (Connection) conferenceable)
.collect(Collectors.toList());
// Conference equivalent to setConferenceables that only accepts Connections
@@ -396,6 +431,12 @@
Log.w(this, "start new ImsConference - control should never come here");
return;
}
+
+ // Mark the foreground connection as MERGE_COMPLETE before it is disconnected as part of
+ // the IMS merge conference process:
+ connection.sendTelephonyConnectionEvent(
+ android.telecom.Connection.EVENT_MERGE_COMPLETE, null);
+
// Make a clone of the connection which will become the Ims conference host connection.
// This is necessary since the Connection Service does not support removing a connection
// from Telecom. Instead we create a new instance and remove the old one from telecom.
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 8615325..d58c211 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -393,8 +393,9 @@
*/
private PhoneAccountHandle findCorrectPhoneAccountHandle() {
TelecomAccountRegistry telecomAccountRegistry = TelecomAccountRegistry.getInstance(null);
- // Check to see if a the SIM PhoneAccountHandle Exists for the Call.
- PhoneAccountHandle handle = PhoneUtils.makePstnPhoneAccountHandle(mPhone);
+ // Check to see if a SIM PhoneAccountHandle Exists for the Call.
+ PhoneAccountHandle handle = telecomAccountRegistry.getPhoneAccountHandleForSubId(
+ mPhone.getSubId());
if (telecomAccountRegistry.hasAccountEntryForPhoneAccount(handle)) {
return handle;
}
@@ -403,7 +404,8 @@
// receives an MT call while in ECM. Use the Emergency PhoneAccount to receive the account
// if it exists.
PhoneAccountHandle emergencyHandle =
- PhoneUtils.makePstnPhoneAccountHandleWithPrefix(mPhone, "", true);
+ PhoneUtils.makePstnPhoneAccountHandleWithPrefix(mPhone,
+ "", true, mPhone.getUserHandle());
if(telecomAccountRegistry.hasAccountEntryForPhoneAccount(emergencyHandle)) {
Log.i(this, "Receiving MT call in ECM. Using Emergency PhoneAccount Instead.");
return emergencyHandle;
diff --git a/src/com/android/services/telephony/RadioOnHelper.java b/src/com/android/services/telephony/RadioOnHelper.java
deleted file mode 100644
index 63a648f..0000000
--- a/src/com/android/services/telephony/RadioOnHelper.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2014 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.services.telephony;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.telephony.TelephonyManager;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper class that implements special behavior related to emergency calls or making phone calls
- * when the radio is in the POWER_OFF STATE. Specifically, this class handles the case of the user
- * trying to dial an emergency number while the radio is off (i.e. the device is in airplane mode)
- * or a normal number while the radio is off (because of the device is on Bluetooth), by turning the
- * radio back on, waiting for it to come up, and then retrying the call.
- */
-public class RadioOnHelper implements RadioOnStateListener.Callback {
-
- private final Context mContext;
- private RadioOnStateListener.Callback mCallback;
- private List<RadioOnStateListener> mListeners;
- private List<RadioOnStateListener> mInProgressListeners;
- private boolean mIsRadioOnCallingEnabled;
-
- public RadioOnHelper(Context context) {
- mContext = context;
- mInProgressListeners = new ArrayList<>(2);
- }
-
- private void setupListeners() {
- if (mListeners == null) {
- mListeners = new ArrayList<>(2);
- }
- int activeModems = TelephonyManager.from(mContext).getActiveModemCount();
- // Add new listeners if active modem count increased.
- while (mListeners.size() < activeModems) {
- mListeners.add(new RadioOnStateListener());
- }
- // Clean up listeners if active modem count decreased.
- while (mListeners.size() > activeModems) {
- mListeners.get(mListeners.size() - 1).cleanup();
- mListeners.remove(mListeners.size() - 1);
- }
- }
- /**
- * Starts the "turn on radio" sequence. This is the (single) external API of the
- * RadioOnHelper class.
- *
- * This method kicks off the following sequence:
- * - Power on the radio for each Phone
- * - Listen for radio events telling us the radio has come up.
- * - Retry if we've gone a significant amount of time without any response from the radio.
- * - Finally, clean up any leftover state.
- *
- * This method is safe to call from any thread, since it simply posts a message to the
- * RadioOnHelper's handler (thus ensuring that the rest of the sequence is entirely
- * serialized, and runs on the main looper.)
- */
- public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback,
- boolean forEmergencyCall, Phone phoneForEmergencyCall, boolean isTestEmergencyNumber) {
- setupListeners();
- mCallback = callback;
- mInProgressListeners.clear();
- mIsRadioOnCallingEnabled = false;
- for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount(); i++) {
- Phone phone = PhoneFactory.getPhone(i);
- if (phone == null) {
- continue;
- }
-
- mInProgressListeners.add(mListeners.get(i));
- mListeners.get(i).waitForRadioOn(phone, this, forEmergencyCall, forEmergencyCall
- && phone == phoneForEmergencyCall);
- }
- powerOnRadio(forEmergencyCall, phoneForEmergencyCall, isTestEmergencyNumber);
- }
- /**
- * Attempt to power on the radio (i.e. take the device out of airplane mode). We'll eventually
- * get an onServiceStateChanged() callback when the radio successfully comes up.
- */
- private void powerOnRadio(boolean forEmergencyCall, Phone phoneForEmergencyCall,
- boolean isTestEmergencyNumber) {
-
- // Always try to turn on the radio here independent of APM setting - if we got here in the
- // first place, the radio is off independent of APM setting.
- for (Phone phone : PhoneFactory.getPhones()) {
- Log.d(this, "powerOnRadio, enabling Radio");
- if (isTestEmergencyNumber) {
- phone.setRadioPowerOnForTestEmergencyCall(phone == phoneForEmergencyCall);
- } else {
- phone.setRadioPower(true, forEmergencyCall, phone == phoneForEmergencyCall,
- false);
- }
- }
-
- // If airplane mode is on, we turn it off the same way that the Settings activity turns it
- // off to keep the setting in sync.
- if (Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) > 0) {
- Log.d(this, "==> Turning off airplane mode for emergency call.");
-
- // Change the system setting
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0);
-
- // Post the broadcast intend for change in airplane mode
- // TODO: We really should not be in charge of sending this broadcast.
- // If changing the setting is sufficient to trigger all of the rest of the logic,
- // then that should also trigger the broadcast intent.
- Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.putExtra("state", false);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
- }
-
- /**
- * This method is called from multiple Listeners on the Main Looper.
- * Synchronization is not necessary.
- */
- @Override
- public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
- mIsRadioOnCallingEnabled |= isRadioReady;
- mInProgressListeners.remove(listener);
- if (mCallback != null && mInProgressListeners.isEmpty()) {
- mCallback.onComplete(null, mIsRadioOnCallingEnabled);
- }
- }
-
- @Override
- public boolean isOkToCall(Phone phone, int serviceState) {
- return (mCallback == null) ? false : mCallback.isOkToCall(phone, serviceState);
- }
-}
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
deleted file mode 100644
index 93e1e3c..0000000
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright (C) 2016 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.services.telephony;
-
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.telephony.ServiceState;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.telephony.Phone;
-
-/**
- * Helper class that listens to a Phone's radio state and sends an onComplete callback when we
- * return true for isOkToCall.
- */
-public class RadioOnStateListener {
-
- interface Callback {
- /**
- * Receives the result of the RadioOnStateListener's attempt to turn on the radio.
- */
- void onComplete(RadioOnStateListener listener, boolean isRadioReady);
-
- /**
- * Given the Phone and the new service state of that phone, return whether or not this
- * phone is ok to call. If it is, onComplete will be called shortly after.
- */
- boolean isOkToCall(Phone phone, int serviceState);
- }
-
- // Number of times to retry the call, and time between retry attempts.
- // not final for testing
- private static int MAX_NUM_RETRIES = 5;
- // not final for testing
- private static long TIME_BETWEEN_RETRIES_MILLIS = 5000; // msec
-
- // Handler message codes; see handleMessage()
- private static final int MSG_START_SEQUENCE = 1;
- @VisibleForTesting
- public static final int MSG_SERVICE_STATE_CHANGED = 2;
- private static final int MSG_RETRY_TIMEOUT = 3;
- @VisibleForTesting
- public static final int MSG_RADIO_ON = 4;
- public static final int MSG_RADIO_OFF_OR_NOT_AVAILABLE = 5;
-
- private final Handler mHandler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_START_SEQUENCE:
- SomeArgs args = (SomeArgs) msg.obj;
- try {
- Phone phone = (Phone) args.arg1;
- RadioOnStateListener.Callback callback =
- (RadioOnStateListener.Callback) args.arg2;
- boolean forEmergencyCall = (boolean) args.arg3;
- boolean isSelectedPhoneForEmergencyCall = (boolean) args.arg4;
- startSequenceInternal(phone, callback, forEmergencyCall,
- isSelectedPhoneForEmergencyCall);
- } finally {
- args.recycle();
- }
- break;
- case MSG_SERVICE_STATE_CHANGED:
- onServiceStateChanged((ServiceState) ((AsyncResult) msg.obj).result);
- break;
- case MSG_RADIO_ON:
- onRadioOn();
- break;
- case MSG_RADIO_OFF_OR_NOT_AVAILABLE:
- registerForRadioOn();
- break;
- case MSG_RETRY_TIMEOUT:
- onRetryTimeout();
- break;
- default:
- Log.wtf(this, "handleMessage: unexpected message: %d.", msg.what);
- break;
- }
- }
- };
-
-
- private Callback mCallback; // The callback to notify upon completion.
- private Phone mPhone; // The phone that will attempt to place the call.
- private boolean mForEmergencyCall; // Whether radio is being turned on for emergency call.
- // Whether this phone is selected to place emergency call. Can be true only if
- // mForEmergencyCall is true.
- private boolean mSelectedPhoneForEmergencyCall;
- private int mNumRetriesSoFar;
-
- /**
- * Starts the "wait for radio" sequence. This is the (single) external API of the
- * RadioOnStateListener class.
- *
- * This method kicks off the following sequence:
- * - Listen for the service state change event telling us the radio has come up.
- * - Retry if we've gone {@link #TIME_BETWEEN_RETRIES_MILLIS} without any response from the
- * radio.
- * - Finally, clean up any leftover state.
- *
- * This method is safe to call from any thread, since it simply posts a message to the
- * RadioOnStateListener's handler (thus ensuring that the rest of the sequence is entirely
- * serialized, and runs only on the handler thread.)
- */
- public void waitForRadioOn(Phone phone, Callback callback,
- boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall) {
- Log.d(this, "waitForRadioOn: Phone " + phone.getPhoneId());
-
- if (mPhone != null) {
- // If there already is an ongoing request, ignore the new one!
- return;
- }
-
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = phone;
- args.arg2 = callback;
- args.arg3 = forEmergencyCall;
- args.arg4 = isSelectedPhoneForEmergencyCall;
- mHandler.obtainMessage(MSG_START_SEQUENCE, args).sendToTarget();
- }
-
- /**
- * Actual implementation of waitForRadioOn(), guaranteed to run on the handler thread.
- *
- * @see #waitForRadioOn
- */
- private void startSequenceInternal(Phone phone, Callback callback,
- boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall) {
- Log.d(this, "startSequenceInternal: Phone " + phone.getPhoneId());
-
- // First of all, clean up any state left over from a prior RadioOn call sequence. This
- // ensures that we'll behave sanely if another startTurnOnRadioSequence() comes in while
- // we're already in the middle of the sequence.
- cleanup();
-
- mPhone = phone;
- mCallback = callback;
- mForEmergencyCall = forEmergencyCall;
- mSelectedPhoneForEmergencyCall = isSelectedPhoneForEmergencyCall;
-
- registerForServiceStateChanged();
- // Register for RADIO_OFF to handle cases where emergency call is dialed before
- // we receive UNSOL_RESPONSE_RADIO_STATE_CHANGED with RADIO_OFF.
- registerForRadioOff();
- // Next step: when the SERVICE_STATE_CHANGED event comes in, we'll retry the call; see
- // onServiceStateChanged(). But also, just in case, start a timer to make sure we'll retry
- // the call even if the SERVICE_STATE_CHANGED event never comes in for some reason.
- startRetryTimer();
- }
-
- /**
- * Handles the SERVICE_STATE_CHANGED event. This event tells us that the radio state has changed
- * and is probably coming up. We can now check to see if the conditions are met to place the
- * call with {@link Callback#isOkToCall}
- */
- private void onServiceStateChanged(ServiceState state) {
- if (mPhone == null) return;
- Log.d(this, "onServiceStateChanged(), new state = %s, Phone = %s", state,
- mPhone.getPhoneId());
-
- // Possible service states:
- // - STATE_IN_SERVICE // Normal operation
- // - STATE_OUT_OF_SERVICE // Still searching for an operator to register to,
- // // or no radio signal
- // - STATE_EMERGENCY_ONLY // Only emergency numbers are allowed; currently not used
- // - STATE_POWER_OFF // Radio is explicitly powered off (airplane mode)
-
- if (isOkToCall(state.getState())) {
- // Woo hoo! It's OK to actually place the call.
- Log.d(this, "onServiceStateChanged: ok to call!");
-
- onComplete(true);
- cleanup();
- } else {
- // The service state changed, but we're still not ready to call yet.
- Log.d(this, "onServiceStateChanged: not ready to call yet, keep waiting.");
- }
- }
-
- private void onRadioOn() {
- if (mPhone == null) return;
- ServiceState state = mPhone.getServiceState();
- Log.d(this, "onRadioOn, state = %s, Phone = %s", state,
- mPhone.getPhoneId());
- if (isOkToCall(state.getState())) {
- onComplete(true);
- cleanup();
- } else {
- Log.d(this, "onRadioOn: not ready to call yet, keep waiting.");
- }
- }
- /**
- * Callback to see if it is okay to call yet, given the current conditions.
- */
- private boolean isOkToCall(int serviceState) {
- return (mCallback == null) ? false : mCallback.isOkToCall(mPhone, serviceState);
- }
-
- /**
- * Handles the retry timer expiring.
- */
- private void onRetryTimeout() {
- if (mPhone == null) return;
- int serviceState = mPhone.getServiceState().getState();
- Log.d(this, "onRetryTimeout(): phone state = %s, service state = %d, retries = %d.",
- mPhone.getState(), serviceState, mNumRetriesSoFar);
-
- // - If we're actually in a call, we've succeeded.
- // - Otherwise, if the radio is now on, that means we successfully got out of airplane mode
- // but somehow didn't get the service state change event. In that case, try to place the
- // call.
- // - If the radio is still powered off, try powering it on again.
-
- if (isOkToCall(serviceState)) {
- Log.d(this, "onRetryTimeout: Radio is on. Cleaning up.");
-
- // Woo hoo -- we successfully got out of airplane mode.
- onComplete(true);
- cleanup();
- } else {
- // Uh oh; we've waited the full TIME_BETWEEN_RETRIES_MILLIS and the radio is still not
- // powered-on. Try again.
-
- mNumRetriesSoFar++;
- Log.d(this, "mNumRetriesSoFar is now " + mNumRetriesSoFar);
-
- if (mNumRetriesSoFar > MAX_NUM_RETRIES) {
- Log.w(this, "Hit MAX_NUM_RETRIES; giving up.");
- cleanup();
- } else {
- Log.d(this, "Trying (again) to turn on the radio.");
- mPhone.setRadioPower(true, mForEmergencyCall, mSelectedPhoneForEmergencyCall,
- false);
- startRetryTimer();
- }
- }
- }
-
- /**
- * Clean up when done with the whole sequence: either after successfully turning on the radio,
- * or after bailing out because of too many failures.
- *
- * The exact cleanup steps are:
- * - Notify callback if we still hadn't sent it a response.
- * - Double-check that we're not still registered for any telephony events
- * - Clean up any extraneous handler messages (like retry timeouts) still in the queue
- *
- * Basically this method guarantees that there will be no more activity from the
- * RadioOnStateListener until someone kicks off the whole sequence again with another call
- * to {@link #waitForRadioOn}
- *
- * TODO: Do the work for the comment below:
- * Note we don't call this method simply after a successful call to placeCall(), since it's
- * still possible the call will disconnect very quickly with an OUT_OF_SERVICE error.
- */
- public void cleanup() {
- Log.d(this, "cleanup()");
-
- // This will send a failure call back if callback has yet to be invoked. If the callback
- // was already invoked, it's a no-op.
- onComplete(false);
-
- unregisterForServiceStateChanged();
- unregisterForRadioOff();
- unregisterForRadioOn();
- cancelRetryTimer();
-
- // Used for unregisterForServiceStateChanged() so we null it out here instead.
- mPhone = null;
- mNumRetriesSoFar = 0;
- }
-
- private void startRetryTimer() {
- cancelRetryTimer();
- mHandler.sendEmptyMessageDelayed(MSG_RETRY_TIMEOUT, TIME_BETWEEN_RETRIES_MILLIS);
- }
-
- private void cancelRetryTimer() {
- mHandler.removeMessages(MSG_RETRY_TIMEOUT);
- }
-
- private void registerForServiceStateChanged() {
- // Unregister first, just to make sure we never register ourselves twice. (We need this
- // because Phone.registerForServiceStateChanged() does not prevent multiple registration of
- // the same handler.)
- unregisterForServiceStateChanged();
- mPhone.registerForServiceStateChanged(mHandler, MSG_SERVICE_STATE_CHANGED, null);
- }
-
- private void unregisterForServiceStateChanged() {
- // This method is safe to call even if we haven't set mPhone yet.
- if (mPhone != null) {
- mPhone.unregisterForServiceStateChanged(mHandler); // Safe even if unnecessary
- }
- mHandler.removeMessages(MSG_SERVICE_STATE_CHANGED); // Clean up any pending messages too
- }
-
- private void registerForRadioOff() {
- mPhone.mCi.registerForOffOrNotAvailable(mHandler, MSG_RADIO_OFF_OR_NOT_AVAILABLE, null);
- }
-
- private void unregisterForRadioOff() {
- // This method is safe to call even if we haven't set mPhone yet.
- if (mPhone != null) {
- mPhone.mCi.unregisterForOffOrNotAvailable(mHandler); // Safe even if unnecessary
- }
- mHandler.removeMessages(MSG_RADIO_OFF_OR_NOT_AVAILABLE); // Clean up any pending messages
- }
-
- private void registerForRadioOn() {
- unregisterForRadioOff();
- mPhone.mCi.registerForOn(mHandler, MSG_RADIO_ON, null);
- }
-
- private void unregisterForRadioOn() {
- // This method is safe to call even if we haven't set mPhone yet.
- if (mPhone != null) {
- mPhone.mCi.unregisterForOn(mHandler); // Safe even if unnecessary
- }
- mHandler.removeMessages(MSG_RADIO_ON); // Clean up any pending messages too
- }
-
- private void onComplete(boolean isRadioReady) {
- if (mCallback != null) {
- Callback tempCallback = mCallback;
- mCallback = null;
- tempCallback.onComplete(this, isRadioReady);
- }
- }
-
- @VisibleForTesting
- public Handler getHandler() {
- return mHandler;
- }
-
- @VisibleForTesting
- public void setMaxNumRetries(int retries) {
- MAX_NUM_RETRIES = retries;
- }
-
- @VisibleForTesting
- public void setTimeBetweenRetriesMillis(long timeMs) {
- TIME_BETWEEN_RETRIES_MILLIS = timeMs;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || !getClass().equals(o.getClass())) return false;
-
- RadioOnStateListener that = (RadioOnStateListener) o;
-
- if (mNumRetriesSoFar != that.mNumRetriesSoFar) {
- return false;
- }
- if (mCallback != null ? !mCallback.equals(that.mCallback) : that.mCallback != null) {
- return false;
- }
- return mPhone != null ? mPhone.equals(that.mPhone) : that.mPhone == null;
-
- }
-}
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index b04cd8f..2b69b82 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -63,7 +63,6 @@
import com.android.internal.telephony.ExponentialBackoff;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.phone.PhoneGlobals;
import com.android.phone.PhoneUtils;
@@ -73,6 +72,7 @@
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import java.util.Locale;
import java.util.Optional;
import java.util.function.Predicate;
@@ -274,6 +274,8 @@
private PhoneAccount registerPstnPhoneAccount(boolean isEmergency, boolean isTestAccount) {
PhoneAccount account = buildPstnPhoneAccount(mIsEmergency, mIsTestAccount);
+ Log.i(this, "registerPstnPhoneAccount: Registering account=%s with "
+ + "Telecom. subId=%d", account, getSubId());
// Register with Telecom and put into the account entry.
mTelecomManager.registerPhoneAccount(account);
return account;
@@ -285,13 +287,18 @@
private PhoneAccount buildPstnPhoneAccount(boolean isEmergency, boolean isTestAccount) {
String testPrefix = isTestAccount ? "Test " : "";
+ // Check if we are registering another user. If we are, ensure that the account
+ // is registered to that user handle.
+ int subId = mPhone.getSubId();
+ // Get user handle from phone's sub id (if we get null, then system user will be used)
+ UserHandle userToRegister = mPhone.getUserHandle();
+
// Build the Phone account handle.
PhoneAccountHandle phoneAccountHandle =
PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
- mPhone, testPrefix, isEmergency);
+ mPhone, testPrefix, isEmergency, userToRegister);
// Populate the phone account data.
- int subId = mPhone.getSubId();
String subscriberId = mPhone.getSubscriberId();
int color = PhoneAccount.NO_HIGHLIGHT_COLOR;
int slotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
@@ -304,8 +311,8 @@
subNumber = "";
}
- String label;
- String description;
+ String label = "";
+ String description = "";
Icon icon = null;
// We can only get the real slotId from the SubInfoRecord, we can't calculate the
@@ -321,7 +328,9 @@
} else if (mTelephonyManager.getPhoneCount() == 1) {
// For single-SIM devices, we show the label and description as whatever the name of
// the network is.
- description = label = tm.getNetworkOperatorName();
+ if (record != null) {
+ description = label = String.valueOf(record.getDisplayName());
+ }
} else {
CharSequence subDisplayName = null;
@@ -355,8 +364,12 @@
// By default all SIM phone accounts can place emergency calls.
int capabilities = PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
- PhoneAccount.CAPABILITY_CALL_PROVIDER |
- PhoneAccount.CAPABILITY_MULTI_USER;
+ PhoneAccount.CAPABILITY_CALL_PROVIDER;
+
+ // This is enabled by default. To support work profiles, it should not be enabled.
+ if (userToRegister == null) {
+ capabilities |= PhoneAccount.CAPABILITY_MULTI_USER;
+ }
if (mContext.getResources().getBoolean(R.bool.config_pstnCanPlaceEmergencyCalls)) {
capabilities |= PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS;
@@ -536,38 +549,22 @@
return false;
}
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- if (SubscriptionManagerService.getInstance() == null) {
- Log.d(this,
- "isEmergencyPreferredAccount: SubscriptionManagerService not "
- + "available.");
- return false;
- }
- // Only set an emergency preference on devices with multiple active subscriptions
- // (include opportunistic subscriptions) in this check.
- // API says never null, but this can return null in testing.
- int[] activeSubIds = SubscriptionManagerService.getInstance()
- .getActiveSubIdList(false);
- if (activeSubIds == null || activeSubIds.length <= 1) {
- Log.d(this, "isEmergencyPreferredAccount: one or less active subscriptions.");
- return false;
- }
- } else {
- SubscriptionController controller = SubscriptionController.getInstance();
- if (controller == null) {
- Log.d(this,
- "isEmergencyPreferredAccount: SubscriptionController not available.");
- return false;
- }
- // Only set an emergency preference on devices with multiple active subscriptions
- // (include opportunistic subscriptions) in this check.
- // API says never null, but this can return null in testing.
- int[] activeSubIds = controller.getActiveSubIdList(false);
- if (activeSubIds == null || activeSubIds.length <= 1) {
- Log.d(this, "isEmergencyPreferredAccount: one or less active subscriptions.");
- return false;
- }
+ if (SubscriptionManagerService.getInstance() == null) {
+ Log.d(this,
+ "isEmergencyPreferredAccount: SubscriptionManagerService not "
+ + "available.");
+ return false;
}
+ // Only set an emergency preference on devices with multiple active subscriptions
+ // (include opportunistic subscriptions) in this check.
+ // API says never null, but this can return null in testing.
+ int[] activeSubIds = SubscriptionManagerService.getInstance()
+ .getActiveSubIdList(false);
+ if (activeSubIds == null || activeSubIds.length <= 1) {
+ Log.d(this, "isEmergencyPreferredAccount: one or less active subscriptions.");
+ return false;
+ }
+
// Check to see if this PhoneAccount is associated with the default Data subscription.
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
Log.d(this, "isEmergencyPreferredAccount: provided subId " + subId + "is not "
@@ -577,17 +574,10 @@
int userDefaultData = SubscriptionManager.getDefaultDataSubscriptionId();
boolean isActiveDataValid = SubscriptionManager.isValidSubscriptionId(activeDataSubId);
- boolean isActiveDataOpportunistic;
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- SubscriptionInfo subInfo;
- subInfo = SubscriptionManagerService.getInstance()
- .getSubscriptionInfo(activeDataSubId);
- isActiveDataOpportunistic = isActiveDataValid && subInfo != null
- && subInfo.isOpportunistic();
- } else {
- isActiveDataOpportunistic = isActiveDataValid
- && SubscriptionController.getInstance().isOpportunistic(activeDataSubId);
- }
+ SubscriptionInfo subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfo(activeDataSubId);
+ boolean isActiveDataOpportunistic = isActiveDataValid && subInfo != null
+ && subInfo.isOpportunistic();
// compare the activeDataSubId to the subId specified only if it is valid and not an
// opportunistic subscription (only supports data). If not, use the current default
@@ -955,7 +945,7 @@
// Next check whether we're in or near a country that supports it
String country =
mPhone.getServiceStateTracker().getLocaleTracker()
- .getLastKnownCountryIso().toLowerCase();
+ .getLastKnownCountryIso().toLowerCase(Locale.ROOT);
String[] supportedCountries = mContext.getResources().getStringArray(
R.array.config_simless_emergency_rtt_supported_countries);
@@ -1163,7 +1153,10 @@
@Override
public void onServiceStateChanged(ServiceState serviceState) {
int newState = serviceState.getState();
+ Log.i(this, "onServiceStateChanged: newState=%d, mServiceState=%d",
+ newState, mServiceState);
if (newState == ServiceState.STATE_IN_SERVICE && mServiceState != newState) {
+ Log.i(this, "onServiceStateChanged: Tearing down and re-setting up accounts.");
tearDownAccounts();
setupAccounts();
} else {
@@ -1576,9 +1569,8 @@
int subscriptionId = phone.getSubId();
Log.i(this, "setupAccounts: Phone with subscription id %d", subscriptionId);
// setupAccounts can be called multiple times during service changes.
- // Don't add an account if the Icc has not been set yet.
- if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)
- || phone.getFullIccSerialNumber() == null) {
+ // Don't add an account if subscription is not ready.
+ if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
Log.d(this, "setupAccounts: skipping invalid subid %d", subscriptionId);
continue;
}
@@ -1610,6 +1602,7 @@
// Add a fake account entry.
if (DBG && phones.length > 0 && "TRUE".equals(System.getProperty("test_sim"))) {
+ Log.i(this, "setupAccounts: adding a fake AccountEntry");
mAccounts.add(new AccountEntry(phones[0], false /* emergency */,
true /* isTest */));
}
diff --git a/src/com/android/services/telephony/TelephonyConferenceBase.java b/src/com/android/services/telephony/TelephonyConferenceBase.java
index 1c81fb9..1e7f956 100644
--- a/src/com/android/services/telephony/TelephonyConferenceBase.java
+++ b/src/com/android/services/telephony/TelephonyConferenceBase.java
@@ -60,6 +60,11 @@
* @param conference The conference.
*/
public void onDestroyed(Conference conference) {}
+
+ /**
+ * Listener called when a conference either reaches capacity or is no longer at capacity.
+ */
+ public void onConferenceCapacityChanged() {}
}
private final Set<TelephonyConferenceListener> mListeners = Collections.newSetFromMap(
@@ -237,6 +242,14 @@
}
/**
+ * Notifies the {@link TelephonyConferenceListener}s when the capacity of the conference has
+ * changed.
+ */
+ public void notifyConferenceCapacityChanged() {
+ mListeners.forEach(l -> l.onConferenceCapacityChanged());
+ }
+
+ /**
* Notifies {@link TelephonyConferenceListener}s of a conference being destroyed
*/
private void notifyDestroyed() {
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index f6d3feb..6d136b0 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -16,6 +16,10 @@
package com.android.services.telephony;
+import static android.telephony.ims.ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED;
+import static android.telephony.ims.ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL;
+import static android.telephony.ims.ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -48,11 +52,13 @@
import android.telephony.ServiceState.RilRadioTechnology;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
import android.telephony.ims.RtpHeaderExtension;
import android.telephony.ims.RtpHeaderExtensionType;
+import android.telephony.ims.feature.MmTelFeature;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Pair;
@@ -95,6 +101,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -141,6 +148,7 @@
private static final int MSG_REJECT = 21;
private static final int MSG_DTMF_DONE = 22;
private static final int MSG_MEDIA_ATTRIBUTES_CHANGED = 23;
+ private static final int MSG_ON_RTT_INITIATED = 24;
private static final String JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN = "+81";
private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
@@ -323,11 +331,19 @@
SomeArgs args = (SomeArgs) msg.obj;
try {
sendTelephonyConnectionEvent((String) args.arg1, (Bundle) args.arg2);
-
} finally {
args.recycle();
}
break;
+ case MSG_ON_RTT_INITIATED:
+ if (mOriginalConnection != null) {
+ // if mOriginalConnection is null, the properties will get set when
+ // mOriginalConnection gets set.
+ updateConnectionProperties();
+ refreshConferenceSupported();
+ }
+ sendRttInitiationSuccess();
+ break;
}
}
};
@@ -721,7 +737,13 @@
SomeArgs args = SomeArgs.obtain();
args.arg1 = event;
args.arg2 = extras;
- mHandler.obtainMessage(MSG_ON_CONNECTION_EVENT, args).sendToTarget();
+ if (EVENT_MERGE_COMPLETE.equals(event)){
+ // To ensure the MERGE_COMPLETE event logs before the listeners are removed,
+ // circumvent the handler by sending the connection event directly:
+ sendTelephonyConnectionEvent(event, extras);
+ } else {
+ mHandler.obtainMessage(MSG_ON_CONNECTION_EVENT, args).sendToTarget();
+ }
}
@Override
@@ -749,13 +771,11 @@
@Override
public void onRttInitiated() {
- if (mOriginalConnection != null) {
- // if mOriginalConnection is null, the properties will get set when
- // mOriginalConnection gets set.
- updateConnectionProperties();
- refreshConferenceSupported();
- }
- sendRttInitiationSuccess();
+ Log.i(TelephonyConnection.this, "onRttInitiated: callId=%s", getTelecomCallId());
+ // Post RTT initiation to the Handler associated with this TelephonyConnection.
+ // This avoids a race condition where a call starts as RTT but ConnectionService call to
+ // handleCreateConnectionComplete happens AFTER the RTT status is reported to Telecom.
+ mHandler.obtainMessage(MSG_ON_RTT_INITIATED).sendToTarget();
}
@Override
@@ -800,6 +820,13 @@
Log.i(this, "onReceivedDtmfDigit: digit=%c", digit);
mDtmfTransport.onDtmfReceived(digit);
}
+
+ @Override
+ public void onAudioModeIsVoipChanged(int imsAudioHandler) {
+ boolean isVoip = imsAudioHandler == MmTelFeature.AUDIO_HANDLER_ANDROID;
+ Log.i(this, "onAudioModeIsVoipChanged isVoip =" + isVoip);
+ setAudioModeIsVoip(isVoip);
+ }
};
private TelephonyConnectionService mTelephonyConnectionService;
@@ -925,6 +952,8 @@
private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap(
new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1));
+ private Integer mEmergencyServiceCategory = null;
+
protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection,
String callId, @android.telecom.Call.Details.CallDirection int callDirection) {
setCallDirection(callDirection);
@@ -1306,6 +1335,8 @@
if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
ImsPhone imsPhone = (ImsPhone) phone;
imsPhone.holdActiveCall();
+ mTelephonyConnectionService.maybeUnholdCallsOnOtherSubs(
+ getPhoneAccountHandle());
return;
}
phone.switchHoldingAndActive();
@@ -1674,7 +1705,8 @@
if (filteredCnapNames != null) {
long cnapNameMatches = Arrays.asList(filteredCnapNames)
.stream()
- .filter(filteredCnapName -> filteredCnapName.equals(cnapName.toUpperCase()))
+ .filter(filteredCnapName -> filteredCnapName.equals(
+ cnapName.toUpperCase(Locale.ROOT)))
.count();
if (cnapNameMatches > 0) {
Log.i(this, "filterCnapName: Filtered CNAP Name: " + cnapName);
@@ -2017,32 +2049,47 @@
}
@VisibleForTesting
- public PersistableBundle getCarrierConfig() {
+ public @NonNull PersistableBundle getCarrierConfig() {
Phone phone = getPhone();
if (phone == null) {
- return null;
+ Log.w(this,
+ "getCarrierConfig: phone is null. Returning CarrierConfigManager"
+ + ".getDefaultConfig()");
+ return CarrierConfigManager.getDefaultConfig();
}
- return PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
+
+ // potential null returned from .getCarrierConfigForSubId() and method guarantees non-null.
+ // hence, need for try/finally block
+ PersistableBundle pb = null;
+ try {
+ pb = PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
+ } catch (Exception e) {
+ Log.e(this, e,
+ "getCarrierConfig: caught Exception when calling "
+ + "PhoneGlobals.getCarrierConfigForSubId(phone.getSubId()). Returning "
+ + "CarrierConfigManager.getDefaultConfig()");
+ } finally {
+ if (pb == null) {
+ pb = CarrierConfigManager.getDefaultConfig();
+ }
+ }
+ return pb;
+ }
+
+ @VisibleForTesting
+ public boolean isRttMergeSupported(@NonNull PersistableBundle pb) {
+ return pb.getBoolean(CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL);
}
private boolean canDeflectImsCalls() {
- PersistableBundle b = getCarrierConfig();
- // Return false if the CarrierConfig is unavailable
- if (b != null) {
- return b.getBoolean(
- CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL) &&
- isValidRingingCall();
- }
- return false;
+ return getCarrierConfig().getBoolean(
+ CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL)
+ && isValidRingingCall();
}
private boolean isCallTransferSupported() {
- PersistableBundle b = getCarrierConfig();
- // Return false if the CarrierConfig is unavailable
- if (b != null) {
- return b.getBoolean(CarrierConfigManager.KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL);
- }
- return false;
+ return getCarrierConfig().getBoolean(
+ CarrierConfigManager.KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL);
}
private boolean canTransfer(TelephonyConnection c) {
@@ -2142,7 +2189,7 @@
mPhoneForEvents = null;
}
- @VisibleForTesting
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
public void hangup(int telephonyDisconnectCode) {
if (mOriginalConnection != null) {
mHangupDisconnectCause = telephonyDisconnectCode;
@@ -2168,6 +2215,7 @@
Log.e(this, e, "Call to Connection.hangup failed with exception");
}
} else {
+ mTelephonyConnectionService.onLocalHangup(this);
if (getState() == STATE_DISCONNECTED) {
Log.i(this, "hangup called on an already disconnected call!");
close();
@@ -2438,6 +2486,42 @@
setTelephonyConnectionRinging();
break;
case DISCONNECTED:
+ if (mTelephonyConnectionService != null) {
+ ImsReasonInfo reasonInfo = null;
+ if (isImsConnection()) {
+ ImsPhoneConnection imsPhoneConnection =
+ (ImsPhoneConnection) mOriginalConnection;
+ reasonInfo = imsPhoneConnection.getImsReasonInfo();
+ if (reasonInfo != null) {
+ int reasonCode = reasonInfo.getCode();
+ int extraCode = reasonInfo.getExtraCode();
+ if ((reasonCode == CODE_SIP_ALTERNATE_EMERGENCY_CALL)
+ || (reasonCode == CODE_LOCAL_CALL_CS_RETRY_REQUIRED
+ && extraCode == EXTRA_CODE_CALL_RETRY_EMERGENCY)) {
+ EmergencyNumber numberInfo =
+ imsPhoneConnection.getEmergencyNumberInfo();
+ if (numberInfo != null) {
+ mEmergencyServiceCategory =
+ numberInfo.getEmergencyServiceCategoryBitmask();
+ } else {
+ Log.i(this, "mEmergencyServiceCategory no EmergencyNumber");
+ }
+
+ if (mEmergencyServiceCategory != null) {
+ Log.i(this, "mEmergencyServiceCategory="
+ + mEmergencyServiceCategory);
+ }
+ }
+ }
+ }
+
+ if (mTelephonyConnectionService.maybeReselectDomain(this,
+ mOriginalConnection.getPreciseDisconnectCause(), reasonInfo)) {
+ clearOriginalConnection();
+ break;
+ }
+ }
+
if (shouldTreatAsEmergencyCall()
&& (cause
== android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
@@ -3049,8 +3133,6 @@
if (isIms) {
isVoWifiEnabled = isWfcEnabled(phone);
}
- boolean isRttMergeSupported = getCarrierConfig()
- .getBoolean(CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL);
PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils
.makePstnPhoneAccountHandle(phone.getDefaultPhone())
: PhoneUtils.makePstnPhoneAccountHandle(phone);
@@ -3088,7 +3170,7 @@
if (mTreatAsEmergencyCall) {
isConferenceSupported = false;
Log.d(this, "refreshConferenceSupported = false; emergency call");
- } else if (isRtt() && !isRttMergeSupported) {
+ } else if (isRtt() && !isRttMergeSupported(getCarrierConfig())) {
isConferenceSupported = false;
Log.d(this, "refreshConferenceSupported = false; rtt call");
} else if (!isConferencingSupported || isIms && !isImsConferencingSupported) {
@@ -3145,12 +3227,9 @@
Phone phone = getPhone();
if (phone != null && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA)
&& !mOriginalConnection.isIncoming()) {
- PersistableBundle pb = getCarrierConfig();
- if (pb != null) {
- showOrigDialString = pb.getBoolean(CarrierConfigManager
- .KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL);
- Log.d(this, "showOrigDialString: " + showOrigDialString);
- }
+ showOrigDialString = getCarrierConfig().getBoolean(CarrierConfigManager
+ .KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL);
+ Log.d(this, "showOrigDialString: " + showOrigDialString);
}
return showOrigDialString;
}
@@ -3727,8 +3806,7 @@
if (mOriginalConnection.isIncoming()
&& !TextUtils.isEmpty(mOriginalConnection.getAddress())
&& mOriginalConnection.getAddress().startsWith(JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN)) {
- PersistableBundle b = getCarrierConfig();
- return b != null && b.getBoolean(
+ return getCarrierConfig().getBoolean(
CarrierConfigManager.KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL);
}
return false;
@@ -3753,8 +3831,7 @@
* otherwise.
*/
private boolean supportsD2DUsingRtp() {
- PersistableBundle b = getCarrierConfig();
- return b != null && b.getBoolean(
+ return getCarrierConfig().getBoolean(
CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL);
}
@@ -3762,8 +3839,7 @@
* @return {@code true} if the carrier supports D2D using DTMF digits, {@code false} otherwise.
*/
private boolean supportsD2DUsingDtmf() {
- PersistableBundle b = getCarrierConfig();
- return b != null && b.getBoolean(
+ return getCarrierConfig().getBoolean(
CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL);
}
@@ -3772,8 +3848,7 @@
* extensions used in D2D comms, {@code false} otherwise.
*/
private boolean supportsSdpNegotiationOfRtpHeaderExtensions() {
- PersistableBundle b = getCarrierConfig();
- return b != null && b.getBoolean(
+ return getCarrierConfig().getBoolean(
CarrierConfigManager
.KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL);
}
@@ -3834,4 +3909,21 @@
public List<TelephonyConnectionListener> getTelephonyConnectionListeners() {
return new ArrayList<>(mTelephonyListeners);
}
+
+ /**
+ * @return An {@link Integer} instance of the emergency service category.
+ */
+ public @Nullable Integer getEmergencyServiceCategory() {
+ return mEmergencyServiceCategory;
+ }
+
+ /**
+ * Sets the emergency service category.
+ *
+ * @param eccCategory The emergency service category.
+ */
+ @VisibleForTesting
+ public void setEmergencyServiceCategory(int eccCategory) {
+ mEmergencyServiceCategory = eccCategory;
+ }
}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index ed81aed..bf7ce00 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -16,6 +16,9 @@
package com.android.services.telephony;
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -29,7 +32,9 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelUuid;
+import android.provider.DeviceConfig;
import android.telecom.Conference;
+import android.telecom.Conferenceable;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
import android.telecom.ConnectionService;
@@ -38,17 +43,27 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation.DisconnectCauses;
import android.telephony.CarrierConfigManager;
+import android.telephony.DataSpecificRegistrationInfo;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.EmergencyRegResult;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneNumberUtils;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.Pair;
import android.view.WindowManager;
+import com.android.ims.ImsManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
@@ -59,12 +74,21 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RIL;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.d2d.Communicator;
import com.android.internal.telephony.data.PhoneSwitcher;
+import com.android.internal.telephony.domainselection.DomainSelectionConnection;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.domainselection.EmergencyCallDomainSelectionConnection;
+import com.android.internal.telephony.domainselection.NormalCallDomainSelectionConnection;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
+import com.android.internal.telephony.emergency.RadioOnHelper;
+import com.android.internal.telephony.emergency.RadioOnStateListener;
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneConnection;
+import com.android.internal.telephony.imsphone.ImsPhoneMmiCode;
+import com.android.internal.telephony.satellite.SatelliteController;
+import com.android.internal.telephony.satellite.SatelliteSOSMessageRecommender;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.phone.FrameworksUtils;
@@ -87,6 +111,7 @@
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.regex.Pattern;
@@ -101,6 +126,16 @@
// from the modem.
private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1000;
+ // Timeout to start dynamic routing of normal routing emergency numbers.
+ @VisibleForTesting
+ public static final int TIMEOUT_TO_DYNAMIC_ROUTING_MS = 10000;
+
+ // Timeout before we terminate the outgoing DSDA call if HOLD did not complete in time on the
+ // existing call.
+ private static final int DEFAULT_DSDA_OUTGOING_CALL_HOLD_TIMEOUT_MS = 2000;
+ private static final String KEY_DOMAIN_COMPARE_FEATURE_ENABLED_FLAG =
+ "is_domain_selection_compare_feature_enabled";
+
// If configured, reject attempts to dial numbers matching this pattern.
private static final Pattern CDMA_ACTIVATION_CODE_REGEX_PATTERN =
Pattern.compile("\\*228[0-9]{0,2}");
@@ -117,6 +152,12 @@
}
@Override
public void addConference(ImsConference mImsConference) {
+ Connection conferenceHost = mImsConference.getConferenceHost();
+ if (conferenceHost instanceof TelephonyConnection) {
+ TelephonyConnection tcConferenceHost = (TelephonyConnection) conferenceHost;
+ tcConferenceHost.setTelephonyConnectionService(TelephonyConnectionService.this);
+ tcConferenceHost.setPhoneAccountHandle(mImsConference.getPhoneAccountHandle());
+ }
TelephonyConnectionService.this.addTelephonyConference(mImsConference);
}
@Override
@@ -177,24 +218,44 @@
@VisibleForTesting
public Pair<WeakReference<TelephonyConnection>, Queue<Phone>> mEmergencyRetryCache;
private DeviceState mDeviceState = new DeviceState();
+ private EmergencyStateTracker mEmergencyStateTracker;
+ private SatelliteSOSMessageRecommender mSatelliteSOSMessageRecommender;
+ private DomainSelectionResolver mDomainSelectionResolver;
+ private EmergencyCallDomainSelectionConnection mEmergencyCallDomainSelectionConnection;
+ private TelephonyConnection mEmergencyConnection;
+ private String mEmergencyCallId = null;
+ private Executor mDomainSelectionMainExecutor;
+ private ImsManager mImsManager = null;
+ private DomainSelectionConnection mDomainSelectionConnection;
+ private TelephonyConnection mNormalCallConnection;
+ private SatelliteController mSatelliteController;
/**
* Keeps track of the status of a SIM slot.
*/
private static class SlotStatus {
public int slotId;
+ public int activeSubId;
// RAT capabilities
public int capabilities;
// By default, we will assume that the slots are not locked.
public boolean isLocked = false;
// Is the emergency number associated with the slot
public boolean hasDialedEmergencyNumber = false;
- //SimState
+ //SimState.
public int simState;
- public SlotStatus(int slotId, int capabilities) {
+ //helper to check if sim is really 'present' in the traditional sense.
+ // since eSIM always reports SIM_STATE_READY
+ public boolean isSubActiveAndSimPresent() {
+ return (simState != TelephonyManager.SIM_STATE_ABSENT
+ && activeSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+
+ public SlotStatus(int slotId, int capabilities, int activeSubId) {
this.slotId = slotId;
this.capabilities = capabilities;
+ this.activeSubId = activeSubId;
}
}
@@ -204,6 +265,7 @@
@VisibleForTesting
public interface SubscriptionManagerProxy {
int getDefaultVoicePhoneId();
+ int getDefaultDataPhoneId();
int getSimStateForSlotIdx(int slotId);
int getPhoneId(int subId);
}
@@ -215,6 +277,11 @@
}
@Override
+ public int getDefaultDataPhoneId() {
+ return getPhoneId(SubscriptionManager.getDefaultDataSubscriptionId());
+ }
+
+ @Override
public int getSimStateForSlotIdx(int slotId) {
return TelephonyManager.getSimStateForSlotIndex(slotId);
}
@@ -223,6 +290,7 @@
public int getPhoneId(int subId) {
return SubscriptionManager.getPhoneId(subId);
}
+
};
/**
@@ -231,10 +299,20 @@
@VisibleForTesting
public interface TelephonyManagerProxy {
int getPhoneCount();
- boolean hasIccCard(int slotId);
boolean isCurrentEmergencyNumber(String number);
Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList();
+
+ /**
+ * Determines whether concurrent IMS calls across both SIMs are possible, based on whether
+ * the device is DSDA capable, or if the DSDS device supports virtual DSDA.
+ */
boolean isConcurrentCallsPossible();
+
+ /**
+ * Gets the maximum number of SIMs that can be active, based on the device's multisim
+ * configuration. Returns 1 for DSDS, 2 for DSDA.
+ */
+ int getMaxNumberOfSimultaneouslyActiveSims();
}
private TelephonyManagerProxy mTelephonyManagerProxy;
@@ -253,11 +331,6 @@
}
@Override
- public boolean hasIccCard(int slotId) {
- return mTelephonyManager.hasIccCard(slotId);
- }
-
- @Override
public boolean isCurrentEmergencyNumber(String number) {
try {
return mTelephonyManager.isEmergencyNumber(number);
@@ -276,9 +349,22 @@
}
@Override
+ public int getMaxNumberOfSimultaneouslyActiveSims() {
+ try {
+ return mTelephonyManager.getMaxNumberOfSimultaneouslyActiveSims();
+ } catch (IllegalStateException ise) {
+ return 1;
+ }
+ }
+
+ @Override
public boolean isConcurrentCallsPossible() {
- // Under DSDA, need to be determined by voice capabilities
- return mTelephonyManager.getMaxNumberOfSimultaneouslyActiveSims() > 1;
+ try {
+ return getMaxNumberOfSimultaneouslyActiveSims() > 1
+ || mTelephonyManager.getPhoneCapability().getMaxActiveVoiceSubscriptions() > 1;
+ } catch (IllegalStateException ise) {
+ return false;
+ }
}
}
@@ -334,7 +420,8 @@
@Override
public PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(Phone phone, String prefix,
boolean isEmergency) {
- return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(phone, prefix, isEmergency);
+ return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
+ phone, prefix, isEmergency, phone.getUserHandle());
}
};
@@ -467,6 +554,213 @@
}
/**
+ * A listener for emergency calls.
+ */
+ private final TelephonyConnection.TelephonyConnectionListener mEmergencyConnectionListener =
+ new TelephonyConnection.TelephonyConnectionListener() {
+ @Override
+ public void onOriginalConnectionConfigured(TelephonyConnection c) {
+ com.android.internal.telephony.Connection origConn = c.getOriginalConnection();
+ if ((origConn == null) || (mEmergencyStateTracker == null)) {
+ // mEmergencyStateTracker is null when no emergency call has been dialed
+ // after bootup and normal call fails with 380 response.
+ return;
+ }
+ // Update the domain in the case that it changes,for example during initial
+ // setup or when there was an srvcc or internal redial.
+ mEmergencyStateTracker.onEmergencyCallDomainUpdated(
+ origConn.getPhoneType(), c.getTelecomCallId());
+ }
+
+ @Override
+ public void onStateChanged(Connection connection,
+ @Connection.ConnectionState int state) {
+ if (mEmergencyCallDomainSelectionConnection == null) return;
+ if (connection == null) return;
+ TelephonyConnection c = (TelephonyConnection) connection;
+ Log.i(this, "onStateChanged callId=" + c.getTelecomCallId()
+ + ", state=" + state);
+ if (c.getState() == Connection.STATE_ACTIVE) {
+ mEmergencyStateTracker.onEmergencyCallStateChanged(
+ c.getOriginalConnection().getState(), c.getTelecomCallId());
+ releaseEmergencyCallDomainSelection(false);
+ }
+ }
+ };
+
+ private final TelephonyConnection.TelephonyConnectionListener
+ mEmergencyConnectionSatelliteListener =
+ new TelephonyConnection.TelephonyConnectionListener() {
+ @Override
+ public void onStateChanged(Connection connection,
+ @Connection.ConnectionState int state) {
+ if (connection == null) {
+ Log.d(this,
+ "onStateChanged for satellite listener: connection is null");
+ return;
+ }
+ if (mSatelliteSOSMessageRecommender == null) {
+ Log.d(this, "onStateChanged for satellite listener: "
+ + "mSatelliteSOSMessageRecommender is null");
+ return;
+ }
+
+ TelephonyConnection c = (TelephonyConnection) connection;
+ mSatelliteSOSMessageRecommender.onEmergencyCallConnectionStateChanged(
+ c.getTelecomCallId(), state);
+ if (state == Connection.STATE_DISCONNECTED
+ || state == Connection.STATE_ACTIVE) {
+ c.removeTelephonyConnectionListener(mEmergencyConnectionSatelliteListener);
+ mSatelliteSOSMessageRecommender = null;
+ }
+ }
+ };
+
+ /**
+ * A listener for calls.
+ */
+ private final TelephonyConnection.TelephonyConnectionListener mNormalCallConnectionListener =
+ new TelephonyConnection.TelephonyConnectionListener() {
+ @Override
+ public void onStateChanged(
+ Connection connection, @Connection.ConnectionState int state) {
+ TelephonyConnection c = (TelephonyConnection) connection;
+ if (c != null) {
+ switch(c.getState()) {
+ case Connection.STATE_ACTIVE: {
+ Log.d(LOG_TAG, "Call State->ACTIVE."
+ + "Clearing DomainSelectionConnection");
+ if (mDomainSelectionConnection != null) {
+ mDomainSelectionConnection.finishSelection();
+ mDomainSelectionConnection = null;
+ }
+ mNormalCallConnection = null;
+ }
+ break;
+
+ case Connection.STATE_DISCONNECTED: {
+ c.removeTelephonyConnectionListener(mNormalCallConnectionListener);
+ }
+ break;
+ }
+ }
+ }
+ };
+
+ private static class StateHoldingListener extends
+ TelephonyConnection.TelephonyConnectionListener {
+ private final CompletableFuture<Boolean> mStateHoldingFuture;
+
+ StateHoldingListener(CompletableFuture<Boolean> future) {
+ mStateHoldingFuture = future;
+ }
+
+ @Override
+ public void onStateChanged(
+ Connection connection, @Connection.ConnectionState int state) {
+ TelephonyConnection c = (TelephonyConnection) connection;
+ if (c != null) {
+ switch (c.getState()) {
+ case Connection.STATE_HOLDING: {
+ Log.d(LOG_TAG, "Connection " + connection.getTelecomCallId()
+ + " changed to STATE_HOLDING!");
+ mStateHoldingFuture.complete(true);
+ c.removeTelephonyConnectionListener(this);
+ }
+ break;
+ case Connection.STATE_DISCONNECTED: {
+ Log.d(LOG_TAG, "Connection " + connection.getTelecomCallId()
+ + " changed to STATE_DISCONNECTED!");
+ mStateHoldingFuture.complete(false);
+ c.removeTelephonyConnectionListener(this);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ private final DomainSelectionConnection.DomainSelectionConnectionCallback
+ mEmergencyDomainSelectionConnectionCallback =
+ new DomainSelectionConnection.DomainSelectionConnectionCallback() {
+ @Override
+ public void onSelectionTerminated(@DisconnectCauses int cause) {
+ mDomainSelectionMainExecutor.execute(() -> {
+ Log.i(this, "onSelectionTerminated cause=" + cause);
+ if (mEmergencyCallDomainSelectionConnection == null) {
+ Log.i(this, "onSelectionTerminated no DomainSelectionConnection");
+ return;
+ }
+
+ // Cross stack redial
+ if (cause == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
+ || cause == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE) {
+ if (mEmergencyConnection != null) {
+ boolean isPermanentFailure =
+ cause == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
+ Log.i(this, "onSelectionTerminated permanent=" + isPermanentFailure);
+ TelephonyConnection c = mEmergencyConnection;
+ Phone phone = mEmergencyCallDomainSelectionConnection.getPhone();
+ mEmergencyConnection.removeTelephonyConnectionListener(
+ mEmergencyConnectionListener);
+ releaseEmergencyCallDomainSelection(true);
+ mEmergencyStateTracker.endCall(mEmergencyCallId);
+ mEmergencyCallId = null;
+ retryOutgoingOriginalConnection(c, phone, isPermanentFailure);
+ return;
+ }
+ }
+ if (mEmergencyConnection != null) {
+ mEmergencyConnection.hangup(android.telephony.DisconnectCause.OUT_OF_NETWORK);
+ mEmergencyConnection = null;
+ }
+ });
+ }
+ };
+
+ private final DomainSelectionConnection.DomainSelectionConnectionCallback
+ mCallDomainSelectionConnectionCallback =
+ new DomainSelectionConnection.DomainSelectionConnectionCallback() {
+ @Override
+ public void onSelectionTerminated(@DisconnectCauses int cause) {
+ mDomainSelectionMainExecutor.execute(new Runnable() {
+ int mCause = cause;
+ @Override
+ public void run() {
+ Log.v(this, "Call domain selection terminated.");
+ if (mDomainSelectionConnection != null) {
+ mDomainSelectionConnection = null;
+ }
+ if (mNormalCallConnection != null) {
+ // TODO: To support ShowPreciseFailedCause, TelephonyConnection
+ // .getShowPreciseFailedCause API should be added.
+
+ // If cause is NOT_VALID then, it's a redial cancellation and
+ // use cause code from original connection.
+ com.android.internal.telephony.Connection connection =
+ mNormalCallConnection.getOriginalConnection();
+ if (connection != null) {
+ if (mCause == android.telephony.DisconnectCause.NOT_VALID) {
+ mCause = connection.getDisconnectCause();
+ }
+
+ String reason = connection.getVendorDisconnectCause();
+ int phoneId = mNormalCallConnection.getPhone().getPhoneId();
+ mNormalCallConnection.setTelephonyConnectionDisconnected(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
+ mCause, reason, phoneId));
+ Log.d(this, "Call connection closed. Cause: " + mCause
+ + " Reason: " + reason);
+ }
+ mNormalCallConnection.close();
+ mNormalCallConnection = null;
+ }
+ }
+ });
+ }
+ };
+
+ /**
* A listener to actionable events specific to the TelephonyConnection.
*/
private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
@@ -478,7 +772,7 @@
@Override
public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {
- retryOutgoingOriginalConnection(c, isPermanentFailure);
+ retryOutgoingOriginalConnection(c, c.getPhone(), isPermanentFailure);
}
};
@@ -486,7 +780,7 @@
new TelephonyConferenceBase.TelephonyConferenceListener() {
@Override
public void onConferenceMembershipChanged(Connection connection) {
- mHoldTracker.updateHoldCapability(connection.getPhoneAccountHandle());
+ mHoldTracker.updateHoldCapability();
}
};
@@ -505,6 +799,9 @@
TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);
mHoldTracker = new HoldTracker();
mIsTtyEnabled = mDeviceState.isTtyModeEnabled(this);
+ mDomainSelectionMainExecutor = getApplicationContext().getMainExecutor();
+ mDomainSelectionResolver = DomainSelectionResolver.getInstance();
+ mSatelliteController = SatelliteController.getInstance();
IntentFilter intentFilter = new IntentFilter(
TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
@@ -766,13 +1063,25 @@
boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
|| isRadioPowerDownOnBluetooth();
+ boolean needToTurnOffSatellite = isSatelliteBlockingCall(isEmergencyNumber);
// Get the right phone object from the account data passed in.
final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
/* Note: when not an emergency, handle can be null for unknown callers */
handle == null ? null : handle.getSchemeSpecificPart());
- if (needToTurnOnRadio) {
+ if (mDomainSelectionResolver.isDomainSelectionSupported()) {
+ // Normal routing emergency number shall be handled by normal call domain selctor.
+ if (isEmergencyNumber && !isNormalRouting(phone, number)) {
+ final Connection resultConnection =
+ placeEmergencyConnection(phone,
+ request, numberToDial, isTestEmergencyNumber,
+ handle, needToTurnOnRadio);
+ if (resultConnection != null) return resultConnection;
+ }
+ }
+
+ if (needToTurnOnRadio || needToTurnOffSatellite) {
final Uri resultHandle = handle;
final int originalPhoneType = phone.getPhoneType();
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
@@ -784,6 +1093,8 @@
if (isEmergencyNumber) {
mIsEmergencyCallPending = true;
}
+ int timeoutToOnTimeoutCallback = mDomainSelectionResolver.isDomainSelectionSupported()
+ ? TIMEOUT_TO_DYNAMIC_ROUTING_MS : 0;
mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() {
@Override
public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
@@ -792,45 +1103,58 @@
}
@Override
- public boolean isOkToCall(Phone phone, int serviceState) {
+ public boolean onTimeout(Phone phone, int serviceState, boolean imsVoiceCapable) {
+ if (mDomainSelectionResolver.isDomainSelectionSupported()) {
+ return isEmergencyNumber;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isOkToCall(Phone phone, int serviceState, boolean imsVoiceCapable) {
// HAL 1.4 introduced a new variant of dial for emergency calls, which includes
// an isTesting parameter. For HAL 1.4+, do not wait for IN_SERVICE, this will
// be handled at the RIL/vendor level by emergencyDial(...).
boolean waitForInServiceToDialEmergency = isTestEmergencyNumber
- && phone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4);
+ && phone.getHalVersion(HAL_SERVICE_VOICE)
+ .less(RIL.RADIO_HAL_VERSION_1_4);
+ if (mDomainSelectionResolver.isDomainSelectionSupported()) {
+ if (isEmergencyNumber) {
+ // Since the domain selection service is enabled,
+ // dilaing normal routing emergency number only reaches here.
+ if (!isVoiceInService(phone, imsVoiceCapable)) {
+ // Wait for voice in service.
+ // That is, wait for IMS registration on PS only network.
+ serviceState = ServiceState.STATE_OUT_OF_SERVICE;
+ waitForInServiceToDialEmergency = true;
+ }
+ }
+ }
if (isEmergencyNumber && !waitForInServiceToDialEmergency) {
// We currently only look to make sure that the radio is on before dialing.
// We should be able to make emergency calls at any time after the radio has
// been powered on and isn't in the UNAVAILABLE state, even if it is
// reporting the OUT_OF_SERVICE state.
- return (phone.getState() == PhoneConstants.State.OFFHOOK)
- || phone.getServiceStateTracker().isRadioOn();
+ return phone.getState() == PhoneConstants.State.OFFHOOK
+ || (phone.getServiceStateTracker().isRadioOn()
+ && !mSatelliteController.isSatelliteEnabled());
} else {
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- SubscriptionInfoInternal subInfo = SubscriptionManagerService
- .getInstance().getSubscriptionInfoInternal(phone.getSubId());
- // Wait until we are in service and ready to make calls. This can happen
- // when we power down the radio on bluetooth to save power on watches or
- // if it is a test emergency number and we have to wait for the device
- // to move IN_SERVICE before the call can take place over normal
- // routing.
- return (phone.getState() == PhoneConstants.State.OFFHOOK)
- // Do not wait for voice in service on opportunistic SIMs.
- || (subInfo != null && subInfo.isOpportunistic())
- || serviceState == ServiceState.STATE_IN_SERVICE;
- }
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService
+ .getInstance().getSubscriptionInfoInternal(phone.getSubId());
// Wait until we are in service and ready to make calls. This can happen
- // when we power down the radio on bluetooth to save power on watches or if
- // it is a test emergency number and we have to wait for the device to move
- // IN_SERVICE before the call can take place over normal routing.
- return (phone.getState() == PhoneConstants.State.OFFHOOK)
+ // when we power down the radio on bluetooth to save power on watches or
+ // if it is a test emergency number and we have to wait for the device
+ // to move IN_SERVICE before the call can take place over normal
+ // routing.
+ return phone.getState() == PhoneConstants.State.OFFHOOK
// Do not wait for voice in service on opportunistic SIMs.
- || SubscriptionController.getInstance().isOpportunistic(
- phone.getSubId())
- || serviceState == ServiceState.STATE_IN_SERVICE;
+ || subInfo != null && subInfo.isOpportunistic()
+ || (serviceState == ServiceState.STATE_IN_SERVICE
+ && !isSatelliteBlockingCall(isEmergencyNumber));
}
}
- }, isEmergencyNumber && !isTestEmergencyNumber, phone, isTestEmergencyNumber);
+ }, isEmergencyNumber && !isTestEmergencyNumber, phone, isTestEmergencyNumber,
+ timeoutToOnTimeoutCallback);
// Return the still unconnected GsmConnection and wait for the Radios to boot before
// connecting it to the underlying Phone.
return resultConnection;
@@ -847,6 +1171,13 @@
}
if (!isEmergencyNumber) {
+ if (mSatelliteController.isSatelliteEnabled()) {
+ Log.d(this, "onCreateOutgoingConnection, cannot make call in satellite mode.");
+ return Connection.createFailedConnection(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.SATELLITE_ENABLED,
+ "Call failed because satellite modem is enabled."));
+ }
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
false, handle, phone);
if (isAdhocConference) {
@@ -856,15 +1187,57 @@
}
return resultConnection;
} else {
+ if (mTelephonyManagerProxy.isConcurrentCallsPossible()) {
+ Conferenceable c = maybeHoldCallsOnOtherSubs(request.getAccountHandle());
+ if (c != null) {
+ delayDialForOtherSubHold(phone, c, (success) -> {
+ Log.d(this,
+ "onCreateOutgoingConn - delayDialForOtherSubHold"
+ + " success = " + success);
+ if (success) {
+ placeOutgoingConnection(request, resultConnection,
+ phone);
+ } else {
+ ((TelephonyConnection) resultConnection).hangup(
+ android.telephony.DisconnectCause.LOCAL);
+ }
+ });
+ return resultConnection;
+ }
+ }
return placeOutgoingConnection(request, resultConnection, phone);
}
} else {
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
true, handle, phone);
- delayDialForDdsSwitch(phone, (result) -> {
- Log.i(this, "onCreateOutgoingConn - delayDialForDdsSwitch result = " + result);
- placeOutgoingConnection(request, resultConnection, phone);
- });
+
+ CompletableFuture<Void> maybeHoldFuture = CompletableFuture.completedFuture(null);
+ if (mTelephonyManagerProxy.isConcurrentCallsPossible()
+ && shouldHoldForEmergencyCall(phone)) {
+ // If the PhoneAccountHandle was adjusted on building the TelephonyConnection,
+ // the relevant PhoneAccountHandle will be updated in resultConnection.
+ PhoneAccountHandle phoneAccountHandle =
+ resultConnection.getPhoneAccountHandle() == null
+ ? request.getAccountHandle() : resultConnection.getPhoneAccountHandle();
+ Conferenceable c = maybeHoldCallsOnOtherSubs(phoneAccountHandle);
+ if (c != null) {
+ maybeHoldFuture = delayDialForOtherSubHold(phone, c, (success) -> {
+ Log.i(this, "onCreateOutgoingConn emergency-"
+ + " delayDialForOtherSubHold success = " + success);
+ if (!success) {
+ // Terminates the existing call to make way for the emergency call.
+ hangup(c, android.telephony.DisconnectCause
+ .OUTGOING_EMERGENCY_CALL_PLACED);
+ }
+ });
+ }
+ }
+ Consumer<Boolean> ddsSwitchConsumer = (result) -> {
+ Log.i(this, "onCreateOutgoingConn emergency-"
+ + " delayDialForDdsSwitch result = " + result);
+ placeOutgoingConnection(request, resultConnection, phone);
+ };
+ maybeHoldFuture.thenRun(() -> delayDialForDdsSwitch(phone, ddsSwitchConsumer));
return resultConnection;
}
}
@@ -954,11 +1327,19 @@
});
}
} else {
- Log.w(this, "onCreateOutgoingConnection, failed to turn on radio");
- closeOrDestroyConnection(originalConnection,
- mDisconnectCauseFactory.toTelecomDisconnectCause(
- android.telephony.DisconnectCause.POWER_OFF,
- "Failed to turn on radio."));
+ if (isSatelliteBlockingCall(isEmergencyNumber)) {
+ Log.w(LOG_TAG, "handleOnComplete, failed to turn off satellite modem");
+ closeOrDestroyConnection(originalConnection,
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.SATELLITE_ENABLED,
+ "Failed to turn off satellite modem."));
+ } else {
+ Log.w(LOG_TAG, "handleOnComplete, failed to turn on radio");
+ closeOrDestroyConnection(originalConnection,
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.POWER_OFF,
+ "Failed to turn on radio."));
+ }
mIsEmergencyCallPending = false;
}
}
@@ -979,16 +1360,9 @@
// Notify Telecom of the new Connection type.
// TODO: Switch out the underlying connection instead of creating a new
// one and causing UI Jank.
- boolean noActiveSimCard;
- if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
- noActiveSimCard = SubscriptionManagerService.getInstance()
- .getActiveSubInfoCount(phone.getContext().getOpPackageName(),
- phone.getContext().getAttributionTag()) == 0;
- } else {
- noActiveSimCard = SubscriptionController.getInstance()
- .getActiveSubInfoCount(phone.getContext().getOpPackageName(),
- phone.getContext().getAttributionTag()) == 0;
- }
+ boolean noActiveSimCard = SubscriptionManagerService.getInstance()
+ .getActiveSubInfoCount(phone.getContext().getOpPackageName(),
+ phone.getContext().getAttributionTag()) == 0;
// If there's no active sim card and the device is in emergency mode, use E account.
addExistingConnection(mPhoneUtilsProxy.makePstnPhoneAccountHandleWithPrefix(
phone, "", isEmergencyNumber && noActiveSimCard), repConnection);
@@ -1530,29 +1904,28 @@
@Override
public void onConnectionAdded(Connection connection) {
if (connection instanceof Holdable && !isExternalConnection(connection)) {
- mHoldTracker.addHoldable(
- connection.getPhoneAccountHandle(), (Holdable) connection);
+ mHoldTracker.addHoldable((Holdable) connection);
}
}
@Override
public void onConnectionRemoved(Connection connection) {
if (connection instanceof Holdable && !isExternalConnection(connection)) {
- mHoldTracker.removeHoldable(connection.getPhoneAccountHandle(), (Holdable) connection);
+ mHoldTracker.removeHoldable((Holdable) connection);
}
}
@Override
public void onConferenceAdded(Conference conference) {
if (conference instanceof Holdable) {
- mHoldTracker.addHoldable(conference.getPhoneAccountHandle(), (Holdable) conference);
+ mHoldTracker.addHoldable((Holdable) conference);
}
}
@Override
public void onConferenceRemoved(Conference conference) {
if (conference instanceof Holdable) {
- mHoldTracker.removeHoldable(conference.getPhoneAccountHandle(), (Holdable) conference);
+ mHoldTracker.removeHoldable((Holdable) conference);
}
}
@@ -1606,6 +1979,14 @@
return result;
}
+ private boolean isSatelliteBlockingCall(boolean isEmergencyNumber) {
+ if (isEmergencyNumber) {
+ return mSatelliteController.isSatelliteEnabled();
+ } else {
+ return mSatelliteController.isDemoModeEnabled();
+ }
+ }
+
private Pair<WeakReference<TelephonyConnection>, Queue<Phone>> makeCachedConnectionPhonePair(
TelephonyConnection c) {
Queue<Phone> phones = new LinkedList<>(Arrays.asList(mPhoneFactoryProxy.getPhones()));
@@ -1615,7 +1996,7 @@
// Update the mEmergencyRetryCache by removing the Phone used to call the last failed emergency
// number and then moving it to the back of the queue if it is not a permanent failure cause
// from the modem.
- private void updateCachedConnectionPhonePair(TelephonyConnection c,
+ private void updateCachedConnectionPhonePair(TelephonyConnection c, Phone phone,
boolean isPermanentFailure) {
// No cache exists, create a new one.
if (mEmergencyRetryCache == null) {
@@ -1630,7 +2011,7 @@
Queue<Phone> cachedPhones = mEmergencyRetryCache.second;
// Need to refer default phone considering ImsPhone because
// cachedPhones is a list that contains default phones.
- Phone phoneUsed = c.getPhone().getDefaultPhone();
+ Phone phoneUsed = phone.getDefaultPhone();
if (phoneUsed == null) {
return;
}
@@ -1659,9 +2040,10 @@
* This will continue until there are no more slots to dial on.
*/
@VisibleForTesting
- public void retryOutgoingOriginalConnection(TelephonyConnection c, boolean isPermanentFailure) {
- int phoneId = (c.getPhone() == null) ? -1 : c.getPhone().getPhoneId();
- updateCachedConnectionPhonePair(c, isPermanentFailure);
+ public void retryOutgoingOriginalConnection(TelephonyConnection c,
+ Phone phone, boolean isPermanentFailure) {
+ int phoneId = (phone == null) ? -1 : phone.getPhoneId();
+ updateCachedConnectionPhonePair(c, phone, isPermanentFailure);
// Pull next phone to use from the cache or null if it is empty
Phone newPhoneToUse = (mEmergencyRetryCache.second != null)
? mEmergencyRetryCache.second.peek() : null;
@@ -1671,12 +2053,16 @@
Log.i(this, "retryOutgoingOriginalConnection, redialing on Phone Id: " + newPhoneToUse);
c.clearOriginalConnection();
if (phoneId != newPhoneToUse.getPhoneId()) {
- if (!mTelephonyManagerProxy.isConcurrentCallsPossible()) {
+ if (mTelephonyManagerProxy.getMaxNumberOfSimultaneouslyActiveSims() < 2) {
disconnectAllCallsOnOtherSubs(
mPhoneUtilsProxy.makePstnPhoneAccountHandle(newPhoneToUse));
}
updatePhoneAccount(c, newPhoneToUse);
}
+ if (mDomainSelectionResolver.isDomainSelectionSupported()) {
+ onEmergencyRedial(c, newPhoneToUse);
+ return;
+ }
placeOutgoingConnection(c, newPhoneToUse, videoState, connExtras);
} else {
// We have run out of Phones to use. Disconnect the call and destroy the connection.
@@ -1731,9 +2117,10 @@
final com.android.internal.telephony.Connection originalConnection;
try {
if (phone != null) {
- EmergencyNumber emergencyNumber =
- phone.getEmergencyNumberTracker().getEmergencyNumber(number);
- if (emergencyNumber != null) {
+ boolean isEmergency = mTelephonyManagerProxy.isCurrentEmergencyNumber(number);
+ Log.i(this, "placeOutgoingConnection isEmergency=" + isEmergency);
+ if (isEmergency) {
+ handleEmergencyCallStartedForSatelliteSOSMessageRecommender(connection, phone);
if (!getAllConnections().isEmpty()) {
if (!shouldHoldForEmergencyCall(phone)) {
// If we do not support holding ongoing calls for an outgoing
@@ -1769,12 +2156,25 @@
}
}
}
+ if (mDomainSelectionResolver.isDomainSelectionSupported()) {
+ if (isNormalRouting(phone, number)
+ && handleOutgoingCallConnection(number, connection,
+ phone, videoState)) {
+ /** Normal routing emergency number shall be handled
+ * by normal call domain selctor.*/
+ Log.i(this, "placeOutgoingConnection normal routing number");
+ return;
+ }
+ }
+ } else if (handleOutgoingCallConnection(number, connection,
+ phone, videoState)) {
+ return;
}
originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
- .setVideoState(videoState)
- .setIntentExtras(extras)
- .setRttTextStream(connection.getRttTextStream())
- .build(),
+ .setVideoState(videoState)
+ .setIntentExtras(extras)
+ .setRttTextStream(connection.getRttTextStream())
+ .build(),
// We need to wait until the phone has been chosen in GsmCdmaPhone to
// register for the associated TelephonyConnection call event listeners.
connection::registerForCallEvents);
@@ -1787,12 +2187,11 @@
handleCallStateException(e, connection, phone);
return;
}
-
if (originalConnection == null) {
int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
// On GSM phones, null connection means that we dialed an MMI code
if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM ||
- phone.isUtEnabled()) {
+ phone.isUtEnabled()) {
Log.d(this, "dialed MMI code");
int subId = phone.getSubId();
Log.d(this, "subId: "+subId);
@@ -1825,6 +2224,673 @@
}
}
+ private void handleOutgoingCallConnectionByCallDomainSelection(
+ int domain, Phone phone, String number, int videoState) {
+ Log.d(this, "Call Domain Selected : " + domain);
+ try {
+ Bundle extras = mNormalCallConnection.getExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, domain);
+ // Add flag to bundle for comparing legacy and new domain selection results. When
+ // EXTRA_COMPARE_DOMAIN flag is true, legacy domain selection result is used for
+ // placing the call and if both the results are not same then bug report is generated.
+ DeviceConfig.Properties properties = //read all telephony properties
+ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TELEPHONY);
+ boolean compareDomainSelection =
+ properties.getBoolean(KEY_DOMAIN_COMPARE_FEATURE_ENABLED_FLAG, false);
+ extras.putBoolean(PhoneConstants.EXTRA_COMPARE_DOMAIN, compareDomainSelection);
+
+ if (phone != null) {
+ Log.v(LOG_TAG, "Call dialing. Domain: " + domain);
+ com.android.internal.telephony.Connection connection =
+ phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
+ .setVideoState(videoState)
+ .setIntentExtras(extras)
+ .setRttTextStream(mNormalCallConnection.getRttTextStream())
+ .setIsWpsCall(NormalCallDomainSelectionConnection
+ .isWpsCall(number))
+ .build(),
+ mNormalCallConnection::registerForCallEvents);
+
+ mNormalCallConnection.setOriginalConnection(connection);
+ mNormalCallConnection.addTelephonyConnectionListener(mNormalCallConnectionListener);
+ return;
+ } else {
+ Log.w(this, "placeOutgoingCallConnection. Dialing failed. Phone is null");
+ mNormalCallConnection.setTelephonyConnectionDisconnected(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.OUTGOING_FAILURE,
+ "Phone is null", phone.getPhoneId()));
+ mNormalCallConnection.close();
+ }
+ } catch (CallStateException e) {
+ Log.e(this, e, "Call placeOutgoingCallConnection, phone.dial exception: " + e);
+ mNormalCallConnection.unregisterForCallEvents();
+ handleCallStateException(e, mNormalCallConnection, phone);
+ } catch (Exception e) {
+ Log.e(this, e, "Call exception in placeOutgoingCallConnection:" + e);
+ mNormalCallConnection.unregisterForCallEvents();
+ mNormalCallConnection.setTelephonyConnectionDisconnected(DisconnectCauseUtil
+ .toTelecomDisconnectCause(android.telephony.DisconnectCause.OUTGOING_FAILURE,
+ e.getMessage(), phone.getPhoneId()));
+ mNormalCallConnection.close();
+ }
+ if (mDomainSelectionConnection != null) {
+ mDomainSelectionConnection.finishSelection();
+ mDomainSelectionConnection = null;
+ }
+ mNormalCallConnection = null;
+ }
+
+ private boolean handleOutgoingCallConnection(
+ String number, TelephonyConnection connection, Phone phone, int videoState) {
+
+ if (!mDomainSelectionResolver.isDomainSelectionSupported()) {
+ return false;
+ }
+
+ if (phone == null) {
+ return false;
+ }
+
+ String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(
+ PhoneNumberUtils.stripSeparators(number));
+ boolean isMmiCode = (dialPart.startsWith("*") || dialPart.startsWith("#"))
+ && dialPart.endsWith("#");
+ boolean isSuppServiceCode = ImsPhoneMmiCode.isSuppServiceCodes(dialPart, phone);
+
+ // If the number is both an MMI code and a supplementary service code,
+ // it shall be treated as UT. In this case, domain selection is not performed.
+ if (isMmiCode && isSuppServiceCode) {
+ Log.v(LOG_TAG, "UT code not handled by call domain selection.");
+ return false;
+ }
+
+ // Check and select same domain as ongoing call on the same subscription (if exists)
+ int activeCallDomain = getActiveCallDomain(phone.getSubId());
+ if (activeCallDomain != NetworkRegistrationInfo.DOMAIN_UNKNOWN
+ && !NormalCallDomainSelectionConnection.isWpsCall(number)) {
+ Log.d(LOG_TAG, "Selecting same domain as ongoing call on same subId");
+ mNormalCallConnection = connection;
+ handleOutgoingCallConnectionByCallDomainSelection(
+ activeCallDomain, phone, number, videoState);
+ return true;
+ }
+
+ mDomainSelectionConnection = mDomainSelectionResolver
+ .getDomainSelectionConnection(phone, SELECTOR_TYPE_CALLING, false);
+ if (mDomainSelectionConnection == null) {
+ return false;
+ }
+ Log.d(LOG_TAG, "Call Connection created");
+ SelectionAttributes selectionAttributes =
+ new SelectionAttributes.Builder(phone.getPhoneId(), phone.getSubId(),
+ SELECTOR_TYPE_CALLING)
+ .setNumber(number)
+ .setEmergency(false)
+ .setVideoCall(VideoProfile.isVideo(videoState))
+ .build();
+
+ NormalCallDomainSelectionConnection normalCallDomainSelectionConnection =
+ (NormalCallDomainSelectionConnection) mDomainSelectionConnection;
+ CompletableFuture<Integer> future = normalCallDomainSelectionConnection
+ .createNormalConnection(selectionAttributes,
+ mCallDomainSelectionConnectionCallback);
+ Log.d(LOG_TAG, "Call Domain selection triggered.");
+
+ mNormalCallConnection = connection;
+ future.thenAcceptAsync((domain) -> handleOutgoingCallConnectionByCallDomainSelection(
+ domain, phone, number, videoState), mDomainSelectionMainExecutor);
+ return true;
+ }
+
+ @SuppressWarnings("FutureReturnValueIgnored")
+ private Connection placeEmergencyConnection(
+ final Phone phone, final ConnectionRequest request,
+ final String numberToDial, final boolean isTestEmergencyNumber,
+ final Uri handle, final boolean needToTurnOnRadio) {
+
+ final Connection resultConnection =
+ getTelephonyConnection(request, numberToDial, true, handle, phone);
+
+ if (resultConnection instanceof TelephonyConnection) {
+ Log.i(this, "placeEmergencyConnection");
+
+ mIsEmergencyCallPending = true;
+ ((TelephonyConnection) resultConnection).addTelephonyConnectionListener(
+ mEmergencyConnectionListener);
+
+ if (mEmergencyStateTracker == null) {
+ mEmergencyStateTracker = EmergencyStateTracker.getInstance();
+ }
+
+ mEmergencyCallId = resultConnection.getTelecomCallId();
+ CompletableFuture<Integer> future = mEmergencyStateTracker.startEmergencyCall(
+ phone, mEmergencyCallId, isTestEmergencyNumber);
+ future.thenAccept((result) -> {
+ Log.d(this, "startEmergencyCall-complete result=" + result);
+ if (mEmergencyCallId == null) {
+ Log.i(this, "startEmergencyCall-complete dialing canceled");
+ return;
+ }
+ if (result == android.telephony.DisconnectCause.NOT_DISCONNECTED) {
+ createEmergencyConnection(phone, (TelephonyConnection) resultConnection,
+ numberToDial, request, needToTurnOnRadio,
+ mEmergencyStateTracker.getEmergencyRegResult());
+ } else {
+ mEmergencyConnection = null;
+ String reason = "Couldn't setup emergency call";
+ if (result == android.telephony.DisconnectCause.POWER_OFF) {
+ reason = "Failed to turn on radio.";
+ }
+ ((TelephonyConnection) resultConnection).setTelephonyConnectionDisconnected(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(result, reason));
+ ((TelephonyConnection) resultConnection).close();
+ mIsEmergencyCallPending = false;
+ }
+ });
+ mEmergencyConnection = (TelephonyConnection) resultConnection;
+ return resultConnection;
+ }
+ Log.i(this, "placeEmergencyConnection returns null");
+ return null;
+ }
+
+ @SuppressWarnings("FutureReturnValueIgnored")
+ private void createEmergencyConnection(final Phone phone,
+ final TelephonyConnection resultConnection, final String number,
+ final ConnectionRequest request, boolean needToTurnOnRadio,
+ final EmergencyRegResult regResult) {
+ Log.i(this, "createEmergencyConnection");
+
+ if (phone.getImsPhone() == null) {
+ // Dialing emergency calls over IMS is not available without ImsPhone instance.
+ Log.w(this, "createEmergencyConnection no ImsPhone");
+ dialCsEmergencyCall(phone, resultConnection, request);
+ return;
+ }
+
+ ImsManager imsManager = mImsManager;
+ if (imsManager == null) {
+ // mImsManager is not null only while unit test.
+ imsManager = ImsManager.getInstance(phone.getContext(), phone.getPhoneId());
+ }
+ if (!imsManager.isNonTtyOrTtyOnVolteEnabled()) {
+ Log.w(this, "createEmergencyConnection - TTY on VoLTE is not supported.");
+ dialCsEmergencyCall(phone, resultConnection, request);
+ return;
+ }
+
+ DomainSelectionConnection selectConnection =
+ mDomainSelectionResolver.getDomainSelectionConnection(
+ phone, SELECTOR_TYPE_CALLING, true);
+
+ if (selectConnection == null) {
+ // While the domain selection service is enabled, the valid
+ // {@link DomainSelectionConnection} is not available.
+ // This can happen when the domain selection service is not available.
+ Log.w(this, "createEmergencyConnection - no selectionConnection");
+ dialCsEmergencyCall(phone, resultConnection, request);
+ return;
+ }
+
+ mEmergencyCallDomainSelectionConnection =
+ (EmergencyCallDomainSelectionConnection) selectConnection;
+
+ DomainSelectionService.SelectionAttributes attr =
+ EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+ phone.getPhoneId(), phone.getSubId(), needToTurnOnRadio,
+ request.getTelecomCallId(), number, 0, null, regResult);
+
+ CompletableFuture<Integer> future =
+ mEmergencyCallDomainSelectionConnection.createEmergencyConnection(
+ attr, mEmergencyDomainSelectionConnectionCallback);
+ future.thenAcceptAsync((result) -> {
+ Log.d(this, "createEmergencyConnection-complete result=" + result);
+ if (mEmergencyCallId == null) {
+ Log.i(this, "createEmergencyConnection-complete dialing canceled");
+ return;
+ }
+ Bundle extras = request.getExtras();
+ extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, result);
+ placeOutgoingConnection(request, resultConnection, phone);
+ mIsEmergencyCallPending = false;
+ }, mDomainSelectionMainExecutor);
+ }
+
+ private void dialCsEmergencyCall(final Phone phone,
+ final TelephonyConnection resultConnection, final ConnectionRequest request) {
+ Log.d(this, "dialCsEmergencyCall");
+ Bundle extras = request.getExtras();
+ extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, NetworkRegistrationInfo.DOMAIN_CS);
+ mDomainSelectionMainExecutor.execute(
+ () -> {
+ if (mEmergencyCallId == null) {
+ Log.i(this, "dialCsEmergencyCall dialing canceled");
+ return;
+ }
+ placeOutgoingConnection(request, resultConnection, phone);
+ });
+ }
+
+ private void releaseEmergencyCallDomainSelection(boolean cancel) {
+ if (mEmergencyCallDomainSelectionConnection != null) {
+ if (cancel) mEmergencyCallDomainSelectionConnection.cancelSelection();
+ else mEmergencyCallDomainSelectionConnection.finishSelection();
+ mEmergencyCallDomainSelectionConnection = null;
+ }
+ mIsEmergencyCallPending = false;
+ mEmergencyConnection = null;
+ }
+
+ /**
+ * Determine whether reselection of domain is required or not.
+ * @param c the {@link Connection} instance.
+ * @param callFailCause the reason why CS call is disconnected. Allowed values are defined in
+ * {@link com.android.internal.telephony.CallFailCause}.
+ * @param reasonInfo the reason why PS call is disconnected.
+ * @return {@code true} if reselection of domain is required.
+ */
+ public boolean maybeReselectDomain(final TelephonyConnection c,
+ int callFailCause, ImsReasonInfo reasonInfo) {
+ if (!mDomainSelectionResolver.isDomainSelectionSupported()) return false;
+
+ Log.i(this, "maybeReselectDomain csCause=" + callFailCause + ", psCause=" + reasonInfo);
+ if (TextUtils.equals(mEmergencyCallId, c.getTelecomCallId())) {
+ if (mEmergencyCallDomainSelectionConnection != null) {
+ return maybeReselectDomainForEmergencyCall(c, callFailCause, reasonInfo);
+ }
+ Log.i(this, "maybeReselectDomain endCall()");
+ c.removeTelephonyConnectionListener(mEmergencyConnectionListener);
+ mEmergencyStateTracker.endCall(c.getTelecomCallId());
+ mEmergencyCallId = null;
+ return false;
+ }
+
+ if (reasonInfo != null) {
+ int reasonCode = reasonInfo.getCode();
+ int extraCode = reasonInfo.getExtraCode();
+ if ((reasonCode == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL)
+ || (reasonCode == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
+ && extraCode == ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY)) {
+ // clear normal call domain selector
+ c.removeTelephonyConnectionListener(mNormalCallConnectionListener);
+ if (mDomainSelectionConnection != null) {
+ mDomainSelectionConnection.finishSelection();
+ mDomainSelectionConnection = null;
+ }
+ mNormalCallConnection = null;
+
+ onEmergencyRedial(c, c.getPhone().getDefaultPhone());
+ return true;
+ }
+ }
+
+ return maybeReselectDomainForNormalCall(c, callFailCause, reasonInfo);
+ }
+
+ private boolean maybeReselectDomainForEmergencyCall(final TelephonyConnection c,
+ int callFailCause, ImsReasonInfo reasonInfo) {
+ Log.i(this, "maybeReselectDomainForEmergencyCall "
+ + "csCause=" + callFailCause + ", psCause=" + reasonInfo);
+
+ if (c.getOriginalConnection() != null
+ && c.getOriginalConnection().getDisconnectCause()
+ != android.telephony.DisconnectCause.LOCAL
+ && c.getOriginalConnection().getDisconnectCause()
+ != android.telephony.DisconnectCause.POWER_OFF) {
+
+ DomainSelectionService.SelectionAttributes attr =
+ EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+ c.getPhone().getPhoneId(), c.getPhone().getSubId(), false,
+ c.getTelecomCallId(), c.getAddress().getSchemeSpecificPart(),
+ callFailCause, reasonInfo, null);
+
+ CompletableFuture<Integer> future =
+ mEmergencyCallDomainSelectionConnection.reselectDomain(attr);
+ // TeleponyConnection will clear original connection. Keep the reference to Phone.
+ final Phone phone = c.getPhone().getDefaultPhone();
+ if (future != null) {
+ future.thenAcceptAsync((result) -> {
+ Log.d(this, "reselectDomain-complete");
+ if (mEmergencyCallId == null) {
+ Log.i(this, "reselectDomain-complete dialing canceled");
+ return;
+ }
+ onEmergencyRedialOnDomain(c, phone, result);
+ }, mDomainSelectionMainExecutor);
+ return true;
+ }
+ }
+
+ Log.i(this, "maybeReselectDomainForEmergencyCall endCall()");
+ c.removeTelephonyConnectionListener(mEmergencyConnectionListener);
+ releaseEmergencyCallDomainSelection(true);
+ mEmergencyStateTracker.endCall(c.getTelecomCallId());
+ mEmergencyCallId = null;
+ return false;
+ }
+
+ private boolean isNormalRouting(Phone phone, String number) {
+ if (phone.getEmergencyNumberTracker() != null) {
+ EmergencyNumber num = phone.getEmergencyNumberTracker().getEmergencyNumber(number);
+ if (num != null) {
+ return num.getEmergencyCallRouting()
+ == EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL;
+ }
+ }
+ return false;
+ }
+
+ private boolean isVoiceInService(Phone phone, boolean imsVoiceCapable) {
+ // Dialing normal call is available.
+ if (phone.isWifiCallingEnabled()) {
+ Log.i(this, "isVoiceInService VoWi-Fi available");
+ return true;
+ }
+
+ ServiceState ss = phone.getServiceStateTracker().getServiceState();
+ if (ss.getState() != ServiceState.STATE_IN_SERVICE) return false;
+
+ NetworkRegistrationInfo regState = ss.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (regState != null) {
+ int registrationState = regState.getRegistrationState();
+ if (registrationState != NetworkRegistrationInfo.REGISTRATION_STATE_HOME
+ && registrationState != NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING) {
+ return true;
+ }
+
+ int networkType = regState.getAccessNetworkTechnology();
+ if (networkType == TelephonyManager.NETWORK_TYPE_LTE) {
+ DataSpecificRegistrationInfo regInfo = regState.getDataSpecificInfo();
+ if (regInfo.getLteAttachResultType()
+ == DataSpecificRegistrationInfo.LTE_ATTACH_TYPE_COMBINED) {
+ Log.i(this, "isVoiceInService combined attach");
+ return true;
+ }
+ }
+
+ if (networkType == TelephonyManager.NETWORK_TYPE_NR
+ || networkType == TelephonyManager.NETWORK_TYPE_LTE) {
+ Log.i(this, "isVoiceInService PS only network, IMS available " + imsVoiceCapable);
+ return imsVoiceCapable;
+ }
+ }
+ return true;
+ }
+
+ private boolean maybeReselectDomainForNormalCall(
+ final TelephonyConnection c, int callFailCause, ImsReasonInfo reasonInfo) {
+
+ Log.i(LOG_TAG, "maybeReselectDomainForNormalCall " + "csCause:" + callFailCause
+ + ", psCause:" + reasonInfo);
+
+ if (mDomainSelectionConnection != null && c.getOriginalConnection() != null) {
+ Phone phone = c.getPhone().getDefaultPhone();
+ final String number = c.getAddress().getSchemeSpecificPart();
+ int videoState = c.getOriginalConnection().getVideoState();
+ SelectionAttributes selectionAttributes = NormalCallDomainSelectionConnection
+ .getSelectionAttributes(phone.getPhoneId(), phone.getSubId(),
+ c.getTelecomCallId(), number, VideoProfile.isVideo(videoState),
+ callFailCause, reasonInfo);
+
+ Log.d(LOG_TAG, "Reselecting the domain for call");
+ mNormalCallConnection = c;
+ CompletableFuture<Integer> future = mDomainSelectionConnection
+ .reselectDomain(selectionAttributes);
+ if (future != null) {
+ future.thenAcceptAsync((result) -> {
+ onNormalCallRedial(c, phone, result, videoState);
+ }, mDomainSelectionMainExecutor);
+ return true;
+ }
+ }
+
+ c.removeTelephonyConnectionListener(mTelephonyConnectionListener);
+ if (mDomainSelectionConnection != null) {
+ mDomainSelectionConnection.finishSelection();
+ mDomainSelectionConnection = null;
+ }
+ mNormalCallConnection = null;
+ Log.d(LOG_TAG, "Reselect call domain not triggered.");
+ return false;
+ }
+
+ private void onEmergencyRedialOnDomain(TelephonyConnection connection,
+ final Phone phone, @NetworkRegistrationInfo.Domain int domain) {
+ Log.i(this, "onEmergencyRedialOnDomain phoneId=" + phone.getPhoneId()
+ + ", domain=" + DomainSelectionService.getDomainName(domain));
+
+ String number = connection.getAddress().getSchemeSpecificPart();
+
+ // Indicates undetectable emergency number with DialArgs
+ boolean isEmergency = false;
+ int eccCategory = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+ if (connection.getEmergencyServiceCategory() != null) {
+ isEmergency = true;
+ eccCategory = connection.getEmergencyServiceCategory();
+ Log.i(this, "onEmergencyRedialOnDomain eccCategory=" + eccCategory);
+ }
+
+ Bundle extras = new Bundle();
+ extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, domain);
+
+ com.android.internal.telephony.Connection originalConnection =
+ connection.getOriginalConnection();
+ try {
+ if (phone != null) {
+ originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
+ .setVideoState(VideoProfile.STATE_AUDIO_ONLY)
+ .setIntentExtras(extras)
+ .setRttTextStream(connection.getRttTextStream())
+ .setIsEmergency(isEmergency)
+ .setEccCategory(eccCategory)
+ .build(),
+ connection::registerForCallEvents);
+ }
+ } catch (CallStateException e) {
+ Log.e(this, e, "onEmergencyRedialOnDomain, exception: " + e);
+ }
+ if (originalConnection == null) {
+ Log.d(this, "onEmergencyRedialOnDomain, phone.dial returned null");
+ connection.setDisconnected(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
+ "unknown error"));
+ } else {
+ connection.setOriginalConnection(originalConnection);
+ }
+ }
+
+ @SuppressWarnings("FutureReturnValueIgnored")
+ private void onEmergencyRedial(final TelephonyConnection c, final Phone phone) {
+ Log.i(this, "onEmergencyRedial phoneId=" + phone.getPhoneId());
+
+ final String number = c.getAddress().getSchemeSpecificPart();
+ final boolean isTestEmergencyNumber = isEmergencyNumberTestNumber(number);
+
+ mIsEmergencyCallPending = true;
+ c.addTelephonyConnectionListener(mEmergencyConnectionListener);
+ handleEmergencyCallStartedForSatelliteSOSMessageRecommender(c, phone);
+
+ if (mEmergencyStateTracker == null) {
+ mEmergencyStateTracker = EmergencyStateTracker.getInstance();
+ }
+
+ mEmergencyCallId = c.getTelecomCallId();
+ CompletableFuture<Integer> future = mEmergencyStateTracker.startEmergencyCall(
+ phone, mEmergencyCallId, isTestEmergencyNumber);
+ future.thenAccept((result) -> {
+ Log.d(this, "onEmergencyRedial-complete result=" + result);
+ if (mEmergencyCallId == null) {
+ Log.i(this, "onEmergencyRedial-complete dialing canceled");
+ return;
+ }
+ if (result == android.telephony.DisconnectCause.NOT_DISCONNECTED) {
+ DomainSelectionConnection selectConnection =
+ mDomainSelectionResolver.getDomainSelectionConnection(
+ phone, SELECTOR_TYPE_CALLING, true);
+
+ if (selectConnection == null) {
+ Log.w(this, "onEmergencyRedial no selectionConnection, dial CS emergency call");
+ mIsEmergencyCallPending = false;
+ mDomainSelectionMainExecutor.execute(
+ () -> recreateEmergencyConnection(c, phone,
+ NetworkRegistrationInfo.DOMAIN_CS));
+ return;
+ }
+
+ mEmergencyCallDomainSelectionConnection =
+ (EmergencyCallDomainSelectionConnection) selectConnection;
+
+ mEmergencyConnection = c;
+
+ DomainSelectionService.SelectionAttributes attr =
+ EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+ phone.getPhoneId(),
+ phone.getSubId(), false,
+ c.getTelecomCallId(),
+ c.getAddress().getSchemeSpecificPart(),
+ 0, null, mEmergencyStateTracker.getEmergencyRegResult());
+
+ CompletableFuture<Integer> domainFuture =
+ mEmergencyCallDomainSelectionConnection.createEmergencyConnection(
+ attr, mEmergencyDomainSelectionConnectionCallback);
+ domainFuture.thenAcceptAsync((domain) -> {
+ Log.d(this, "onEmergencyRedial-createEmergencyConnection-complete domain="
+ + domain);
+ recreateEmergencyConnection(c, phone, domain);
+ mIsEmergencyCallPending = false;
+ }, mDomainSelectionMainExecutor);
+ } else {
+ c.setTelephonyConnectionDisconnected(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(result, "unknown error"));
+ c.close();
+ mIsEmergencyCallPending = false;
+ }
+ });
+ }
+
+ private void recreateEmergencyConnection(final TelephonyConnection connection,
+ final Phone phone, final @NetworkRegistrationInfo.Domain int result) {
+ Log.d(this, "recreateEmergencyConnection result=" + result);
+ if (mEmergencyCallId == null) {
+ Log.i(this, "recreateEmergencyConnection dialing canceled");
+ return;
+ }
+ if (!getAllConnections().isEmpty()) {
+ if (!shouldHoldForEmergencyCall(phone)) {
+ // If we do not support holding ongoing calls for an outgoing
+ // emergency call, disconnect the ongoing calls.
+ for (Connection c : getAllConnections()) {
+ if (!c.equals(connection)
+ && c.getState() != Connection.STATE_DISCONNECTED
+ && c instanceof TelephonyConnection) {
+ ((TelephonyConnection) c).hangup(
+ android.telephony.DisconnectCause
+ .OUTGOING_EMERGENCY_CALL_PLACED);
+ }
+ }
+ for (Conference c : getAllConferences()) {
+ if (c.getState() != Connection.STATE_DISCONNECTED) {
+ c.onDisconnect();
+ }
+ }
+ } else if (!isVideoCallHoldAllowed(phone)) {
+ // If we do not support holding ongoing video call for an outgoing
+ // emergency call, disconnect the ongoing video call.
+ for (Connection c : getAllConnections()) {
+ if (!c.equals(connection)
+ && c.getState() == Connection.STATE_ACTIVE
+ && VideoProfile.isVideo(c.getVideoState())
+ && c instanceof TelephonyConnection) {
+ ((TelephonyConnection) c).hangup(
+ android.telephony.DisconnectCause
+ .OUTGOING_EMERGENCY_CALL_PLACED);
+ break;
+ }
+ }
+ }
+ }
+ onEmergencyRedialOnDomain(connection, phone, result);
+ }
+
+ private void onNormalCallRedial(TelephonyConnection connection, Phone phone,
+ @NetworkRegistrationInfo.Domain int domain, int videocallState) {
+
+ Log.v(LOG_TAG, "Redialing the call in domain:"
+ + DomainSelectionService.getDomainName(domain));
+
+ String number = connection.getAddress().getSchemeSpecificPart();
+
+ Bundle extras = new Bundle();
+ extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, domain);
+ // Add flag to bundle for comparing legacy and new domain selection results. When
+ // EXTRA_COMPARE_DOMAIN flag is true, legacy domain selection result is used for
+ // placing the call and if both the results are not same then bug report is generated.
+ DeviceConfig.Properties properties = //read all telephony properties
+ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TELEPHONY);
+ boolean compareDomainSelection =
+ properties.getBoolean(KEY_DOMAIN_COMPARE_FEATURE_ENABLED_FLAG, false);
+ extras.putBoolean(PhoneConstants.EXTRA_COMPARE_DOMAIN, compareDomainSelection);
+
+ com.android.internal.telephony.Connection originalConnection =
+ connection.getOriginalConnection();
+ if (originalConnection instanceof ImsPhoneConnection) {
+ if (((ImsPhoneConnection) originalConnection).isRttEnabledForCall()) {
+ extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, true);
+ }
+ }
+
+ try {
+ if (phone != null) {
+ Log.d(LOG_TAG, "Redialing Call.");
+ originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
+ .setVideoState(videocallState)
+ .setIntentExtras(extras)
+ .setRttTextStream(connection.getRttTextStream())
+ .setIsEmergency(false)
+ .build(),
+ connection::registerForCallEvents);
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, e, "Call redial exception: " + e);
+ }
+ if (originalConnection == null) {
+ Log.e(LOG_TAG, new Exception("Phone is null"),
+ "Call redial failure due to phone.dial returned null");
+ connection.setDisconnected(mDisconnectCauseFactory.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.OUTGOING_FAILURE, "connection is null"));
+ connection.close();
+ } else {
+ connection.setOriginalConnection(originalConnection);
+ }
+ }
+
+ protected void onLocalHangup(TelephonyConnection c) {
+ if (TextUtils.equals(mEmergencyCallId, c.getTelecomCallId())) {
+ Log.i(this, "onLocalHangup " + mEmergencyCallId);
+ c.removeTelephonyConnectionListener(mEmergencyConnectionListener);
+ releaseEmergencyCallDomainSelection(true);
+ mEmergencyStateTracker.endCall(c.getTelecomCallId());
+ mEmergencyCallId = null;
+ }
+ }
+
+ @VisibleForTesting
+ public TelephonyConnection.TelephonyConnectionListener getEmergencyConnectionListener() {
+ return mEmergencyConnectionListener;
+ }
+
+ @VisibleForTesting
+ public TelephonyConnection.TelephonyConnectionListener
+ getEmergencyConnectionSatelliteListener() {
+ return mEmergencyConnectionSatelliteListener;
+ }
+
private boolean isVideoCallHoldAllowed(Phone phone) {
CarrierConfigManager cfgManager = (CarrierConfigManager)
phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -1874,6 +2940,9 @@
case CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS:
cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
break;
+ case CallStateException.ERROR_FDN_BLOCKED:
+ cause = android.telephony.DisconnectCause.FDN_BLOCKED;
+ break;
}
connection.setTelephonyConnectionDisconnected(
DisconnectCauseUtil.toTelecomDisconnectCause(cause, e.getMessage(),
@@ -1977,7 +3046,7 @@
}
/**
- * If needed, block until the the default data is is switched for outgoing emergency call, or
+ * If needed, block until the default data is switched for outgoing emergency call, or
* timeout expires.
* @param phone The Phone to switch the DDS on.
* @param completeConsumer The consumer to call once the default data subscription has been
@@ -1988,6 +3057,7 @@
if (phone == null) {
// Do not block indefinitely.
completeConsumer.accept(false);
+ return;
}
try {
// Waiting for PhoneSwitcher to complete the operation.
@@ -2107,6 +3177,57 @@
return modemResultFuture;
}
+ private void addTelephonyConnectionListener(Conferenceable c,
+ TelephonyConnection.TelephonyConnectionListener listener) {
+ if (c instanceof TelephonyConnection) {
+ TelephonyConnection telephonyConnection = (TelephonyConnection) c;
+ telephonyConnection.addTelephonyConnectionListener(listener);
+ } else if (c instanceof ImsConference) {
+ ImsConference imsConference = (ImsConference) c;
+ TelephonyConnection conferenceHost =
+ (TelephonyConnection) imsConference.getConferenceHost();
+ conferenceHost.addTelephonyConnectionListener(listener);
+ } else {
+ throw new IllegalArgumentException(
+ "addTelephonyConnectionListener(): Unexpected conferenceable! " + c);
+ }
+ }
+
+ private CompletableFuture<Boolean> listenForHoldStateChanged(
+ @NonNull Conferenceable conferenceable) {
+ CompletableFuture<Boolean> future = new CompletableFuture<>();
+ final StateHoldingListener stateHoldingListener = new StateHoldingListener(future);
+ addTelephonyConnectionListener(conferenceable, stateHoldingListener);
+ return future;
+ }
+
+ // Returns a future that waits for the STATE_HOLDING confirmation on the input
+ // {@link Conferenceable}, or times out.
+ private CompletableFuture<Void> delayDialForOtherSubHold(Phone phone, Conferenceable c,
+ Consumer<Boolean> completeConsumer) {
+ if (c == null || phone == null) {
+ // Unexpected inputs
+ completeConsumer.accept(false);
+ return CompletableFuture.completedFuture(null);
+ }
+
+ try {
+ CompletableFuture<Boolean> stateHoldingFuture = listenForHoldStateChanged(c);
+ // a timeout that will complete the future to not block the outgoing call indefinitely.
+ CompletableFuture<Boolean> timeout = new CompletableFuture<>();
+ phone.getContext().getMainThreadHandler().postDelayed(
+ () -> timeout.complete(false), DEFAULT_DSDA_OUTGOING_CALL_HOLD_TIMEOUT_MS);
+ // Ensure that the Consumer is completed on the main thread.
+ return stateHoldingFuture.acceptEitherAsync(timeout, completeConsumer,
+ phone.getContext().getMainExecutor());
+ } catch (Exception e) {
+ Log.w(this, "delayDialForOtherSubHold - exception= "
+ + e.getMessage());
+ completeConsumer.accept(false);
+ return CompletableFuture.completedFuture(null);
+ }
+ }
+
/**
* Get the Phone to use for an emergency call of the given emergency number address:
* a) If there are multiple Phones with the Subscriptions that support the emergency number
@@ -2125,7 +3246,7 @@
for (Phone phone : mPhoneFactoryProxy.getPhones()) {
if (phone.getEmergencyNumberTracker() != null) {
if (phone.getEmergencyNumberTracker().isEmergencyNumber(
- emergencyNumberAddress, true)) {
+ emergencyNumberAddress)) {
if (isAvailableForEmergencyCalls(phone)) {
// a)
if (phone.getPhoneId() == defaultVoicePhoneId) {
@@ -2156,39 +3277,57 @@
/**
* Retrieves the most sensible Phone to use for an emergency call using the following Priority
* list (for multi-SIM devices):
- * 1) The User's SIM preference for Voice calling
- * 2) The First Phone that is currently IN_SERVICE or is available for emergency calling
- * 3) Prioritize phones that have the dialed emergency number as part of their emergency
+ * 1) The Phone that is in emergency SMS mode
+ * 2) The phone based on User's SIM preference of Voice calling or Data in order
+ * 3) The First Phone that is currently IN_SERVICE or is available for emergency calling
+ * 4) Prioritize phones that have the dialed emergency number as part of their emergency
* number list
- * 4) If there is a PUK locked SIM, compare the SIMs that are not PUK locked. If all the SIMs
- * are locked, skip to condition 5).
- * 5) The Phone with more Capabilities.
- * 6) The First Phone that has a SIM card in it (Starting from Slot 0...N)
- * 7) The Default Phone (Currently set as Slot 0)
+ * 5) If there is a PUK locked SIM, compare the SIMs that are not PUK locked. If all the SIMs
+ * are locked, skip to condition 6).
+ * 6) The Phone with more Capabilities.
+ * 7) The First Phone that has a SIM card in it (Starting from Slot 0...N)
+ * 8) The Default Phone (Currently set as Slot 0)
*/
@VisibleForTesting
+ @NonNull
public Phone getFirstPhoneForEmergencyCall(List<Phone> phonesWithEmergencyNumber) {
- // 1)
+ int phoneCount = mTelephonyManagerProxy.getPhoneCount();
+ for (int i = 0; i < phoneCount; i++) {
+ Phone phone = mPhoneFactoryProxy.getPhone(i);
+ // 1)
+ if (phone != null && phone.isInEmergencySmsMode()) {
+ if (isAvailableForEmergencyCalls(phone)) {
+ if (phonesWithEmergencyNumber == null
+ || phonesWithEmergencyNumber.contains(phone)) {
+ return phone;
+ }
+ }
+ }
+ }
+
+ // 2)
int phoneId = mSubscriptionManagerProxy.getDefaultVoicePhoneId();
+ if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
+ phoneId = mSubscriptionManagerProxy.getDefaultDataPhoneId();
+ }
if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
- Phone defaultPhone = mPhoneFactoryProxy.getPhone(phoneId);
- if (defaultPhone != null && isAvailableForEmergencyCalls(defaultPhone)) {
+ Phone selectedPhone = mPhoneFactoryProxy.getPhone(phoneId);
+ if (selectedPhone != null && isAvailableForEmergencyCalls(selectedPhone)) {
if (phonesWithEmergencyNumber == null
- || phonesWithEmergencyNumber.contains(defaultPhone)) {
- return defaultPhone;
+ || phonesWithEmergencyNumber.contains(selectedPhone)) {
+ return selectedPhone;
}
}
}
Phone firstPhoneWithSim = null;
- int phoneCount = mTelephonyManagerProxy.getPhoneCount();
List<SlotStatus> phoneSlotStatus = new ArrayList<>(phoneCount);
for (int i = 0; i < phoneCount; i++) {
Phone phone = mPhoneFactoryProxy.getPhone(i);
if (phone == null) {
continue;
}
- // 2)
+ // 3)
if (isAvailableForEmergencyCalls(phone)) {
if (phonesWithEmergencyNumber == null
|| phonesWithEmergencyNumber.contains(phone)) {
@@ -2198,14 +3337,15 @@
return phone;
}
}
- // 5)
+ // 6)
// Store the RAF Capabilities for sorting later.
int radioAccessFamily = phone.getRadioAccessFamily();
- SlotStatus status = new SlotStatus(i, radioAccessFamily);
+ SlotStatus status = new SlotStatus(i, radioAccessFamily, phone.getSubId());
phoneSlotStatus.add(status);
Log.i(this, "getFirstPhoneForEmergencyCall, RAF:" +
- Integer.toHexString(radioAccessFamily) + " saved for Phone Id:" + i);
- // 4)
+ Integer.toHexString(radioAccessFamily) + " saved for Phone Id:" + i + " subId:"
+ + phone.getSubId());
+ // 5)
// Report Slot's PIN/PUK lock status for sorting later.
int simState = mSubscriptionManagerProxy.getSimStateForSlotIdx(i);
// Record SimState.
@@ -2214,7 +3354,8 @@
simState == TelephonyManager.SIM_STATE_PUK_REQUIRED) {
status.isLocked = true;
}
- // 3) Store if the Phone has the corresponding emergency number
+
+ // 4) Store if the Phone has the corresponding emergency number
if (phonesWithEmergencyNumber != null) {
for (Phone phoneWithEmergencyNumber : phonesWithEmergencyNumber) {
if (phoneWithEmergencyNumber != null
@@ -2223,41 +3364,49 @@
}
}
}
- // 6)
- if (firstPhoneWithSim == null && mTelephonyManagerProxy.hasIccCard(i)) {
- // The slot has a SIM card inserted, but is not in service, so keep track of this
- // Phone. Do not return because we want to make sure that none of the other Phones
+ // 7)
+ if (firstPhoneWithSim == null &&
+ (phone.getSubId() != SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
+ // The slot has a SIM card inserted (and an active subscription), but is not in
+ // service, so keep track of this Phone.
+ // Do not return because we want to make sure that none of the other Phones
// are in service (because that is always faster).
firstPhoneWithSim = phone;
- Log.i(this, "getFirstPhoneForEmergencyCall, SIM card inserted, Phone Id:" +
- firstPhoneWithSim.getPhoneId());
+ Log.i(this, "getFirstPhoneForEmergencyCall, SIM with active sub, Phone Id:" +
+ firstPhoneWithSim.getPhoneId());
}
}
- // 7)
+ // 8)
if (firstPhoneWithSim == null && phoneSlotStatus.isEmpty()) {
- if (phonesWithEmergencyNumber == null || phonesWithEmergencyNumber.isEmpty()) {
- // No Phones available, get the default
- Log.i(this, "getFirstPhoneForEmergencyCall, return default phone");
- return mPhoneFactoryProxy.getDefaultPhone();
+ if (phonesWithEmergencyNumber != null) {
+ for (Phone phoneWithEmergencyNumber : phonesWithEmergencyNumber) {
+ if (phoneWithEmergencyNumber != null) {
+ return phoneWithEmergencyNumber;
+ }
+ }
}
- return phonesWithEmergencyNumber.get(0);
+
+ // No Phones available, get the default
+ Log.i(this, "getFirstPhoneForEmergencyCall, return default phone");
+ return mPhoneFactoryProxy.getDefaultPhone();
} else {
- // 5)
+ // 6)
final int defaultPhoneId = mPhoneFactoryProxy.getDefaultPhone().getPhoneId();
final Phone firstOccupiedSlot = firstPhoneWithSim;
if (!phoneSlotStatus.isEmpty()) {
+ Log.i(this, "getFirstPhoneForEmergencyCall, list size: " + phoneSlotStatus.size()
+ + " defaultPhoneId: " + defaultPhoneId + " firstOccupiedSlot: "
+ + firstOccupiedSlot);
// Only sort if there are enough elements to do so.
if (phoneSlotStatus.size() > 1) {
Collections.sort(phoneSlotStatus, (o1, o2) -> {
- // Sort by non-absent SIM.
- if (o1.simState == TelephonyManager.SIM_STATE_ABSENT
- && o2.simState != TelephonyManager.SIM_STATE_ABSENT) {
- return -1;
- }
- if (o2.simState == TelephonyManager.SIM_STATE_ABSENT
- && o1.simState != TelephonyManager.SIM_STATE_ABSENT) {
+ // Sort by non-absent SIM (SIM without active sub is considered absent).
+ if (o1.isSubActiveAndSimPresent() && !o2.isSubActiveAndSimPresent()) {
return 1;
}
+ if (o2.isSubActiveAndSimPresent() && !o1.isSubActiveAndSimPresent()) {
+ return -1;
+ }
// First start by seeing if either of the phone slots are locked. If they
// are, then sort by non-locked SIM first. If they are both locked, sort
// by capability instead.
@@ -2304,7 +3453,7 @@
"with highest capability");
return mPhoneFactoryProxy.getPhone(mostCapablePhoneId);
} else {
- // 6)
+ // 7)
return firstPhoneWithSim;
}
}
@@ -2314,6 +3463,13 @@
* Returns true if the state of the Phone is IN_SERVICE or available for emergency calling only.
*/
private boolean isAvailableForEmergencyCalls(Phone phone) {
+ if (phone.getImsRegistrationTech() == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
+ // When a Phone is registered to Cross-SIM calling, there must always be a Phone on the
+ // other sub which is registered to cellular, so that must be selected.
+ Log.d(this, "isAvailableForEmergencyCalls: skipping over phone "
+ + phone + " as it is registered to CROSS_SIM");
+ return false;
+ }
return ServiceState.STATE_IN_SERVICE == phone.getServiceState().getState() ||
phone.getServiceState().isEmergencyOnly();
}
@@ -2607,8 +3763,8 @@
}
/**
- * For the passed in incoming {@link TelephonyConnection}, add
- * {@link Connection#EXTRA_ANSWERING_DROPS_FG_CALL} if there are ongoing calls on another
+ * For the passed in incoming {@link TelephonyConnection}, for non- dual active voice devices,
+ * adds {@link Connection#EXTRA_ANSWERING_DROPS_FG_CALL} if there are ongoing calls on another
* subscription (ie phone account handle) than the one passed in.
* @param connection The connection.
* @param phoneAccountHandle The {@link PhoneAccountHandle} the incoming call originated on;
@@ -2619,6 +3775,9 @@
*/
public void maybeIndicateAnsweringWillDisconnect(@NonNull TelephonyConnection connection,
@NonNull PhoneAccountHandle phoneAccountHandle) {
+ if (mTelephonyManagerProxy.isConcurrentCallsPossible()) {
+ return;
+ }
if (isCallPresentOnOtherSub(phoneAccountHandle)) {
Log.i(this, "maybeIndicateAnsweringWillDisconnect; answering call %s will cause a call "
+ "on another subscription to drop.", connection.getTelecomCallId());
@@ -2644,37 +3803,46 @@
/**
* Where there are ongoing calls on another subscription other than the one specified,
- * disconnect these calls. This is used where there is an incoming call on one sub, but there
- * are ongoing calls on another sub which need to be disconnected.
+ * disconnect these calls for non-DSDA devices. This is used where there is an incoming call on
+ * one sub, but there are ongoing calls on another sub which need to be disconnected.
* @param incomingHandle The incoming {@link PhoneAccountHandle}.
*/
public void maybeDisconnectCallsOnOtherSubs(@NonNull PhoneAccountHandle incomingHandle) {
Log.i(this, "maybeDisconnectCallsOnOtherSubs: check for calls not on %s", incomingHandle);
- maybeDisconnectCallsOnOtherSubs(getAllConnections(), incomingHandle);
+ maybeDisconnectCallsOnOtherSubs(getAllConnections(), incomingHandle,
+ mTelephonyManagerProxy);
}
/**
- * Used by {@link #maybeDisconnectCallsOnOtherSubs(PhoneAccountHandle)} to perform call
- * disconnection. This method exists as a convenience so that it is possible to unit test
+ * Used by {@link #maybeDisconnectCallsOnOtherSubs(PhoneAccountHandle)} to evaluate and perform
+ * call disconnection. This method exists as a convenience so that it is possible to unit test
* the core functionality.
* @param connections the calls to check.
* @param incomingHandle the incoming handle.
+ * @param telephonyManagerProxy the proxy to the {@link TelephonyManager} instance.
*/
@VisibleForTesting
public static void maybeDisconnectCallsOnOtherSubs(@NonNull Collection<Connection> connections,
- @NonNull PhoneAccountHandle incomingHandle) {
+ @NonNull PhoneAccountHandle incomingHandle,
+ TelephonyManagerProxy telephonyManagerProxy) {
+ if (telephonyManagerProxy.isConcurrentCallsPossible()) {
+ return;
+ }
connections.stream()
.filter(c ->
// Exclude multiendpoint calls as they're not on this device.
- (c.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) == 0
+ (c.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL)
+ == 0
// Include any calls not on same sub as current connection.
&& !Objects.equals(c.getPhoneAccountHandle(), incomingHandle))
.forEach(c -> {
if (c instanceof TelephonyConnection) {
TelephonyConnection tc = (TelephonyConnection) c;
if (!tc.shouldTreatAsEmergencyCall()) {
- Log.i(LOG_TAG, "maybeDisconnectCallsOnOtherSubs: disconnect %s due to "
- + "incoming call on other sub.", tc.getTelecomCallId());
+ Log.i(LOG_TAG,
+ "maybeDisconnectCallsOnOtherSubs: disconnect %s due to "
+ + "incoming call on other sub.",
+ tc.getTelecomCallId());
// Note: intentionally calling hangup instead of onDisconnect.
// onDisconnect posts the disconnection to a handle which means that the
// disconnection will take place AFTER we answer the incoming call.
@@ -2684,6 +3852,172 @@
});
}
+ static void onHold(Conferenceable conferenceable) {
+ if (conferenceable instanceof Connection) {
+ Connection connection = (Connection) conferenceable;
+ connection.onHold();
+ } else if (conferenceable instanceof Conference) {
+ Conference conference = (Conference) conferenceable;
+ conference.onHold();
+ } else {
+ throw new IllegalArgumentException(
+ "onHold(): Unexpected conferenceable! " + conferenceable);
+ }
+ }
+
+ static void onUnhold(Conferenceable conferenceable) {
+ if (conferenceable instanceof Connection) {
+ Connection connection = (Connection) conferenceable;
+ connection.onUnhold();
+ } else if (conferenceable instanceof Conference) {
+ Conference conference = (Conference) conferenceable;
+ conference.onUnhold();
+ } else {
+ throw new IllegalArgumentException(
+ "onUnhold(): Unexpected conferenceable! " + conferenceable);
+ }
+ }
+
+ private static void hangup(Conferenceable conferenceable, int code) {
+ if (conferenceable instanceof TelephonyConnection) {
+ ((TelephonyConnection) conferenceable).hangup(code);
+ } else if (conferenceable instanceof Conference) {
+ ((Conference) conferenceable).onDisconnect();
+ } else {
+ Log.w(LOG_TAG, "hangup(): Unexpected conferenceable! " + conferenceable);
+ }
+ }
+
+ /**
+ * Evaluates whether a connection or conference exists on subscriptions other than the one
+ * corresponding to the existing {@link PhoneAccountHandle}.
+ * @param connections all individual connections, including conference participants.
+ * @param conferences all conferences.
+ * @param currentHandle the existing call handle;
+ * @param telephonyManagerProxy the proxy to the {@link TelephonyManager} instance.
+ */
+ private static @Nullable Conferenceable maybeGetFirstConferenceableFromOtherSubscription(
+ @NonNull Collection<Connection> connections,
+ @NonNull Collection<Conference> conferences,
+ @NonNull PhoneAccountHandle currentHandle,
+ TelephonyManagerProxy telephonyManagerProxy) {
+ if (!telephonyManagerProxy.isConcurrentCallsPossible()) {
+ return null;
+ }
+
+ List<Conference> otherSubConferences = conferences.stream()
+ .filter(c ->
+ // Exclude multiendpoint calls as they're not on this device.
+ (c.getConnectionProperties()
+ & Connection.PROPERTY_IS_EXTERNAL_CALL) == 0
+ // Include any conferences not on same sub as current connection.
+ && !Objects.equals(c.getPhoneAccountHandle(),
+ currentHandle))
+ .toList();
+ if (!otherSubConferences.isEmpty()) {
+ Log.i(LOG_TAG, "maybeGetFirstConferenceable: found "
+ + otherSubConferences.get(0).getTelecomCallId() + " on "
+ + otherSubConferences.get(0).getPhoneAccountHandle());
+ return otherSubConferences.get(0);
+ }
+
+ // Considers Connections (including conference participants) only if no conferences.
+ List<Connection> otherSubConnections = connections.stream()
+ .filter(c ->
+ // Exclude multiendpoint calls as they're not on this device.
+ (c.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) == 0
+ // Include any calls not on same sub as current connection.
+ && !Objects.equals(c.getPhoneAccountHandle(),
+ currentHandle)).toList();
+
+ if (!otherSubConnections.isEmpty()) {
+ if (otherSubConnections.size() > 1) {
+ Log.w(LOG_TAG, "Unexpected number of connections: "
+ + otherSubConnections.size() + " on other sub!");
+ }
+ Log.i(LOG_TAG, "maybeGetFirstConferenceable: found "
+ + otherSubConnections.get(0).getTelecomCallId() + " on "
+ + otherSubConnections.get(0).getPhoneAccountHandle());
+ return otherSubConnections.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * Where there are ongoing calls on multiple subscriptions for DSDA devices, let the 'hold'
+ * button perform an unhold on the other sub's Connection or Conference. This covers for Dialer
+ * apps that may not have a dedicated 'swap' button for calls across different subs.
+ * @param currentHandle The {@link PhoneAccountHandle} of the current active voice call.
+ */
+ public void maybeUnholdCallsOnOtherSubs(
+ @NonNull PhoneAccountHandle currentHandle) {
+ Log.i(this, "maybeUnholdCallsOnOtherSubs: check for calls not on %s",
+ currentHandle);
+ maybeUnholdCallsOnOtherSubs(getAllConnections(), getAllConferences(),
+ currentHandle, mTelephonyManagerProxy);
+ }
+
+ /**
+ * Where there are ongoing calls on multiple subscriptions for DSDA devices, let the 'hold'
+ * button perform an unhold on the other sub's Connection or Conference. This is a convenience
+ * method to unit test the core functionality.
+ *
+ * @param connections all individual connections, including conference participants.
+ * @param conferences all conferences.
+ * @param currentHandle The {@link PhoneAccountHandle} of the current active call.
+ * @param telephonyManagerProxy the proxy to the {@link TelephonyManager} instance.
+ */
+ @VisibleForTesting
+ protected static void maybeUnholdCallsOnOtherSubs(@NonNull Collection<Connection> connections,
+ @NonNull Collection<Conference> conferences,
+ @NonNull PhoneAccountHandle currentHandle,
+ TelephonyManagerProxy telephonyManagerProxy) {
+ Conferenceable c = maybeGetFirstConferenceableFromOtherSubscription(
+ connections, conferences, currentHandle, telephonyManagerProxy);
+ if (c != null) {
+ onUnhold(c);
+ }
+ }
+
+ /**
+ * For DSDA devices, when an outgoing call is dialed out from the 2nd sub, holds the first call.
+ *
+ * @param outgoingHandle The outgoing {@link PhoneAccountHandle}.
+ * @return the Conferenceable representing the Connection or Conference to be held.
+ */
+ private @Nullable Conferenceable maybeHoldCallsOnOtherSubs(
+ @NonNull PhoneAccountHandle outgoingHandle) {
+ Log.i(this, "maybeHoldCallsOnOtherSubs: check for calls not on %s",
+ outgoingHandle);
+ return maybeHoldCallsOnOtherSubs(getAllConnections(), getAllConferences(),
+ outgoingHandle, mTelephonyManagerProxy);
+ }
+
+ /**
+ * For DSDA devices, when an outgoing call is dialed out from the 2nd sub, holds the first call.
+ * This is a convenience method to unit test the core functionality.
+ *
+ * @param connections all individual connections, including conference participants.
+ * @param conferences all conferences.
+ * @param outgoingHandle The outgoing {@link PhoneAccountHandle}.
+ * @param telephonyManagerProxy the proxy to the {@link TelephonyManager} instance.
+ * @return the {@link Conferenceable} representing the Connection or Conference to be held.
+ */
+ @VisibleForTesting
+ protected static @Nullable Conferenceable maybeHoldCallsOnOtherSubs(
+ @NonNull Collection<Connection> connections,
+ @NonNull Collection<Conference> conferences,
+ @NonNull PhoneAccountHandle outgoingHandle,
+ TelephonyManagerProxy telephonyManagerProxy) {
+ Conferenceable c = maybeGetFirstConferenceableFromOtherSubscription(
+ connections, conferences, outgoingHandle, telephonyManagerProxy);
+ if (c != null) {
+ onHold(c);
+ return c;
+ }
+ return null;
+ }
+
private void disconnectAllCallsOnOtherSubs (@NonNull PhoneAccountHandle handle) {
Collection<Connection>connections = getAllConnections();
connections.stream()
@@ -2702,4 +4036,35 @@
}
});
}
+
+ private @NetworkRegistrationInfo.Domain int getActiveCallDomain(int subId) {
+ for (Connection c: getAllConnections()) {
+ if ((c instanceof TelephonyConnection)) {
+ TelephonyConnection connection = (TelephonyConnection) c;
+ Phone phone = connection.getPhone();
+ if (phone == null) {
+ continue;
+ }
+
+ if (phone.getSubId() == subId) {
+ if (phone instanceof GsmCdmaPhone) {
+ return NetworkRegistrationInfo.DOMAIN_CS;
+ } else if (phone instanceof ImsPhone) {
+ return NetworkRegistrationInfo.DOMAIN_PS;
+ }
+ }
+ }
+ }
+ return NetworkRegistrationInfo.DOMAIN_UNKNOWN;
+ }
+
+ private void handleEmergencyCallStartedForSatelliteSOSMessageRecommender(
+ @NonNull TelephonyConnection connection, @NonNull Phone phone) {
+ if (mSatelliteSOSMessageRecommender == null) {
+ mSatelliteSOSMessageRecommender = new SatelliteSOSMessageRecommender(
+ phone.getContext().getMainLooper());
+ }
+ connection.addTelephonyConnectionListener(mEmergencyConnectionSatelliteListener);
+ mSatelliteSOSMessageRecommender.onEmergencyCallStarted(connection, phone);
+ }
}
diff --git a/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java b/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java
new file mode 100644
index 0000000..f1bb78c
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2023 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.services.telephony.domainselection;
+
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.REDIAL_TIMER_DISABLED;
+import static android.telephony.PreciseDisconnectCause.EMERGENCY_PERM_FAILURE;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.SystemProperties;
+import android.telephony.Annotation.PreciseDisconnectCauses;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+
+import java.util.ArrayList;
+
+/** Controls the cross stack redialing. */
+public class CrossSimRedialingController extends Handler {
+ private static final String TAG = "CrossSimRedialingCtrl";
+ private static final boolean DBG = (SystemProperties.getInt("ro.debuggable", 0) == 1);
+ private static final int LOG_SIZE = 50;
+
+ /** An interface of a helper to check emergency number. */
+ public interface EmergencyNumberHelper {
+ /**
+ * Returns whether the number is an emergency number in the given modem slot.
+ *
+ * @param slotId The slot id to be checked.
+ * @param number The number.
+ * @return {@code true} if the number is an emergency number in the given slot.
+ */
+ boolean isEmergencyNumber(int slotId, String number);
+ }
+
+ @VisibleForTesting
+ public static final int MSG_CROSS_STACK_TIMEOUT = 1;
+ @VisibleForTesting
+ public static final int MSG_QUICK_CROSS_STACK_TIMEOUT = 2;
+
+ private static final LocalLog sLocalLog = new LocalLog(LOG_SIZE);
+
+ private final ArrayList<Integer> mStackSelectionHistory = new ArrayList<>();
+ private final ArrayList<Integer> mPermanentRejectedSlots = new ArrayList<>();
+ private final TelephonyManager mTelephonyManager;
+
+ private EmergencyNumberHelper mEmergencyNumberHelper = new EmergencyNumberHelper() {
+ @Override
+ public boolean isEmergencyNumber(int slotId, String number) {
+ // TODO(b/258112541) Add System api to check emergency number per subscription.
+ try {
+ Phone phone = PhoneFactory.getPhone(slotId);
+ if (phone != null
+ && phone.getEmergencyNumberTracker() != null
+ && phone.getEmergencyNumberTracker().isEmergencyNumber(number)) {
+ return true;
+ }
+ } catch (IllegalStateException e) {
+ loge("isEmergencyNumber e=" + e);
+ }
+ return false;
+ }
+ };
+
+ private int mModemCount;
+
+ /** A cache of the carrier config {@link #KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT}. */
+ private int mCrossStackTimer;
+ /** A cache of the carrier config {@link #KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT}. */
+ private int mQuickCrossStackTimer;
+ /**
+ * A cache of the carrier config
+ * {@link #KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL}.
+ */
+ private boolean mStartQuickCrossStackTimerWhenInService;
+
+ private String mCallId;
+ private EmergencyCallDomainSelector mSelector;
+ private String mNumber;
+ private int mSlotId;
+ private int mSubId;
+
+ /**
+ * Creates an instance.
+ *
+ * @param context The Context this is associated with.
+ * @param looper The Looper to run the CrossSimRedialingController.
+ */
+ public CrossSimRedialingController(@NonNull Context context, @NonNull Looper looper) {
+ super(looper);
+
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ }
+
+ /** For unit test only */
+ @VisibleForTesting
+ public CrossSimRedialingController(@NonNull Context context, @NonNull Looper looper,
+ EmergencyNumberHelper emergencyNumberHelper) {
+ this(context, looper);
+
+ mEmergencyNumberHelper = emergencyNumberHelper;
+ }
+
+ /**
+ * Starts the timer.
+ *
+ * @param context The Context this is associated with.
+ * @param selector The instance of {@link EmergencyCallDomainSelector}.
+ * @param callId The call identifier.
+ * @param number The dialing number.
+ * @param inService Indiates that normal service is available.
+ * @param roaming Indicates that it's in roaming or non-domestic network.
+ * @param modemCount The number of active modem count
+ */
+ public void startTimer(@NonNull Context context,
+ @NonNull EmergencyCallDomainSelector selector,
+ @NonNull String callId, @NonNull String number,
+ boolean inService, boolean roaming, int modemCount) {
+ logi("startTimer callId=" + callId
+ + ", in service=" + inService + ", roaming=" + roaming);
+
+ if (!TextUtils.equals(mCallId, callId)) {
+ logi("startTimer callId changed");
+ mCallId = callId;
+ mStackSelectionHistory.clear();
+ mPermanentRejectedSlots.clear();
+ }
+ mSelector = selector;
+ mSlotId = selector.getSlotId();
+ mSubId = selector.getSubId();
+ mNumber = number;
+ mModemCount = modemCount;
+
+ updateCarrierConfiguration(context);
+
+ boolean firstAttempt = !mStackSelectionHistory.contains(mSlotId);
+ logi("startTimer slot=" + mSlotId + ", firstAttempt=" + firstAttempt);
+ mStackSelectionHistory.add(mSlotId);
+
+ if (firstAttempt && mQuickCrossStackTimer > REDIAL_TIMER_DISABLED && !roaming) {
+ if (inService || !mStartQuickCrossStackTimerWhenInService) {
+ logi("startTimer quick timer started");
+ sendEmptyMessageDelayed(MSG_QUICK_CROSS_STACK_TIMEOUT,
+ mQuickCrossStackTimer);
+ return;
+ }
+ }
+
+ if (mCrossStackTimer > REDIAL_TIMER_DISABLED) {
+ logi("startTimer timer started");
+ sendEmptyMessageDelayed(MSG_CROSS_STACK_TIMEOUT, mCrossStackTimer);
+ }
+ }
+
+ /** Stops the timers. */
+ public void stopTimer() {
+ logi("stopTimer");
+ removeMessages(MSG_CROSS_STACK_TIMEOUT);
+ removeMessages(MSG_QUICK_CROSS_STACK_TIMEOUT);
+ }
+
+ /**
+ * Informs the call failure.
+ * @param cause The call failure cause.
+ */
+ public void notifyCallFailure(@PreciseDisconnectCauses int cause) {
+ logi("notifyCallFailure cause=" + cause);
+ if (cause == EMERGENCY_PERM_FAILURE) {
+ mPermanentRejectedSlots.add(mSlotId);
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_CROSS_STACK_TIMEOUT:
+ case MSG_QUICK_CROSS_STACK_TIMEOUT:
+ handleCrossStackTimeout();
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
+ }
+ }
+
+ private void handleCrossStackTimeout() {
+ logi("handleCrossStackTimeout");
+
+ if (isThereOtherSlot()) {
+ mSelector.notifyCrossStackTimerExpired();
+ }
+ }
+
+ /**
+ * Returns whether there is another slot emergency capable.
+ *
+ * @return {@code true} if there is another slot emergency capable,
+ * {@code false} otherwise.
+ */
+ public boolean isThereOtherSlot() {
+ logi("isThereOtherSlot modemCount=" + mModemCount);
+ if (mModemCount < 2) return false;
+
+ for (int i = 0; i < mModemCount; i++) {
+ if (i == mSlotId) continue;
+
+ if (mPermanentRejectedSlots.contains(i)) {
+ logi("isThereOtherSlot index=" + i + ", permanent rejected");
+ continue;
+ }
+
+ int simState = mTelephonyManager.getSimState(i);
+ if (simState != TelephonyManager.SIM_STATE_READY) {
+ logi("isThereOtherSlot index=" + i + ", simState=" + simState);
+ continue;
+ }
+
+ if (mEmergencyNumberHelper.isEmergencyNumber(i, mNumber)) {
+ logi("isThereOtherSlot index=" + i + ", found");
+ return true;
+ } else {
+ logi("isThereOtherSlot index=" + i + ", not emergency number");
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Caches the configuration.
+ */
+ private void updateCarrierConfiguration(Context context) {
+ CarrierConfigManager configMgr = context.getSystemService(CarrierConfigManager.class);
+ PersistableBundle b = configMgr.getConfigForSubId(mSubId,
+ KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT,
+ KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT,
+ KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL);
+ if (b == null) {
+ b = CarrierConfigManager.getDefaultConfig();
+ }
+
+ mCrossStackTimer = b.getInt(KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT) * 1000;
+ mQuickCrossStackTimer =
+ b.getInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT) * 1000;
+ mStartQuickCrossStackTimerWhenInService =
+ b.getBoolean(KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL);
+
+ logi("updateCarrierConfiguration "
+ + ", crossStackTimer=" + mCrossStackTimer
+ + ", quickCrossStackTimer=" + mQuickCrossStackTimer
+ + ", startQuickTimerInService=" + mStartQuickCrossStackTimerWhenInService);
+ }
+
+ /** Destroys the instance. */
+ public void destroy() {
+ if (DBG) logd("destroy");
+
+ removeMessages(MSG_CROSS_STACK_TIMEOUT);
+ removeMessages(MSG_QUICK_CROSS_STACK_TIMEOUT);
+ }
+
+ private void logd(String s) {
+ Log.d(TAG, "[" + mSlotId + "|" + mSubId + "] " + s);
+ }
+
+ private void logi(String s) {
+ Log.i(TAG, "[" + mSlotId + "|" + mSubId + "] " + s);
+ sLocalLog.log(s);
+ }
+
+ private void loge(String s) {
+ Log.e(TAG, "[" + mSlotId + "|" + mSubId + "] " + s);
+ sLocalLog.log(s);
+ }
+}
diff --git a/src/com/android/services/telephony/domainselection/DomainSelectorBase.java b/src/com/android/services/telephony/domainselection/DomainSelectorBase.java
new file mode 100644
index 0000000..1a7c992
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/DomainSelectorBase.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.DomainSelector;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.WwanSelectorCallback;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.Keep;
+
+import java.io.PrintWriter;
+
+/**
+ * An abstract base class to implement domain selector for a specific use case.
+ */
+@Keep
+public abstract class DomainSelectorBase extends Handler implements DomainSelector {
+ /**
+ * A listener used to inform the DomainSelectorService that this DomainSelector has been
+ * destroyed.
+ */
+ public interface DestroyListener {
+ /**
+ * Called when the specified domain selector is being destroyed.
+ * This MUST be called when this domain selector is no longer available after
+ * {@link DomainSelector#finishSelection} called.
+ */
+ void onDomainSelectorDestroyed(DomainSelectorBase selector);
+ }
+
+ // Persistent Logging
+ protected final LocalLog mEventLog = new LocalLog(30);
+ protected final Context mContext;
+ protected final ImsStateTracker mImsStateTracker;
+ protected SelectionAttributes mSelectionAttributes;
+ protected TransportSelectorCallback mTransportSelectorCallback;
+ protected WwanSelectorCallback mWwanSelectorCallback;
+ private final int mSlotId;
+ private final int mSubId;
+ private final DestroyListener mDestroyListener;
+ private final String mLogTag;
+
+ public DomainSelectorBase(Context context, int slotId, int subId, @NonNull Looper looper,
+ @NonNull ImsStateTracker imsStateTracker, @NonNull DestroyListener destroyListener,
+ String logTag) {
+ super(looper);
+ mContext = context;
+ mImsStateTracker = imsStateTracker;
+ mSlotId = slotId;
+ mSubId = subId;
+ mDestroyListener = destroyListener;
+ mLogTag = logTag;
+ }
+
+ /**
+ * Selects a domain for the specified attributes and callback.
+ *
+ * @param attr The attributes required to determine the domain.
+ * @param callback The callback called when the transport selection is completed.
+ */
+ public abstract void selectDomain(SelectionAttributes attr, TransportSelectorCallback callback);
+
+ /**
+ * Destroys this domain selector.
+ */
+ protected void destroy() {
+ removeCallbacksAndMessages(null);
+ notifyDomainSelectorDestroyed();
+ }
+
+ /**
+ * Notifies the application that this domain selector is being destroyed.
+ */
+ protected void notifyDomainSelectorDestroyed() {
+ if (mDestroyListener != null) {
+ mDestroyListener.onDomainSelectorDestroyed(this);
+ }
+ }
+
+ /**
+ * Returns the slot index for this domain selector.
+ */
+ protected int getSlotId() {
+ return mSlotId;
+ }
+
+ /**
+ * Returns the subscription index for this domain selector.
+ */
+ protected int getSubId() {
+ return mSubId;
+ }
+
+ /**
+ * Dumps this instance into a readable format for dumpsys usage.
+ */
+ protected void dump(@NonNull PrintWriter pw) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println(mLogTag + ":");
+ ipw.increaseIndent();
+ ipw.println("SlotId: " + getSlotId());
+ ipw.println("SubId: " + getSubId());
+ mEventLog.dump(ipw);
+ ipw.decreaseIndent();
+ }
+
+ protected void logd(String s) {
+ Log.d(mLogTag, "[" + getSlotId() + "|" + getSubId() + "] " + s);
+ }
+
+ protected void logi(String s) {
+ Log.i(mLogTag, "[" + getSlotId() + "|" + getSubId() + "] " + s);
+ mEventLog.log("[" + getSlotId() + "|" + getSubId() + "] " + s);
+ }
+
+ protected void loge(String s) {
+ Log.e(mLogTag, "[" + getSlotId() + "|" + getSubId() + "] " + s);
+ mEventLog.log("[" + getSlotId() + "|" + getSubId() + "] " + s);
+ }
+}
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
new file mode 100644
index 0000000..3388c97
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
@@ -0,0 +1,1549 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.CDMA2000;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.NGRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.UNKNOWN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.UTRAN;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.BarringInfo.BARRING_SERVICE_TYPE_EMERGENCY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.DOMAIN_CS;
+import static android.telephony.CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP;
+import static android.telephony.CarrierConfigManager.ImsEmergency.DOMAIN_PS_NON_3GPP;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_OVER_CS_ROAMING_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_OVER_IMS_ROAMING_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_SCAN_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE;
+import static android.telephony.CarrierConfigManager.ImsEmergency.VOWIFI_REQUIRES_SETTING_ENABLED;
+import static android.telephony.CarrierConfigManager.ImsEmergency.VOWIFI_REQUIRES_VALID_EID;
+import static android.telephony.CarrierConfigManager.ImsWfc.KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL;
+import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+import static android.telephony.PreciseDisconnectCause.EMERGENCY_PERM_FAILURE;
+import static android.telephony.PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.os.CancellationSignal;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.PowerManager;
+import android.os.SystemProperties;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.BarringInfo;
+import android.telephony.CarrierConfigManager;
+import android.telephony.DisconnectCause;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.EmergencyRegResult;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ProvisioningManager;
+import android.text.TextUtils;
+import android.util.LocalLog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.IntFunction;
+
+/**
+ * Selects the domain for emergency calling.
+ */
+public class EmergencyCallDomainSelector extends DomainSelectorBase
+ implements ImsStateTracker.BarringInfoListener, ImsStateTracker.ImsStateListener {
+ private static final String TAG = "DomainSelector-EmergencyCall";
+ private static final boolean DBG = (SystemProperties.getInt("ro.debuggable", 0) == 1);
+ private static final int LOG_SIZE = 50;
+
+ private static final int MSG_START_DOMAIN_SELECTION = 11;
+ @VisibleForTesting
+ public static final int MSG_NETWORK_SCAN_TIMEOUT = 12;
+ private static final int MSG_NETWORK_SCAN_RESULT = 13;
+ @VisibleForTesting
+ public static final int MSG_MAX_CELLULAR_TIMEOUT = 14;
+
+ private static final int NOT_SUPPORTED = -1;
+
+ private static final LocalLog sLocalLog = new LocalLog(LOG_SIZE);
+
+ private static final ArrayList<String> sAllowOnlyWithSimReady = new ArrayList<>();
+
+ static {
+ // b/177967010, JP
+ sAllowOnlyWithSimReady.add("jp"); // Japan
+ // b/198393826, DE
+ sAllowOnlyWithSimReady.add("de"); // Germany
+ // b/230443699, IN and SG
+ sAllowOnlyWithSimReady.add("in"); // India
+ sAllowOnlyWithSimReady.add("sg"); // Singapore
+ }
+
+ /**
+ * Network callback used to determine whether Wi-Fi is connected or not.
+ */
+ private ConnectivityManager.NetworkCallback mNetworkCallback =
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ logi("onAvailable: " + network);
+ mWiFiAvailable = true;
+ }
+
+ @Override
+ public void onLost(Network network) {
+ logi("onLost: " + network);
+ mWiFiAvailable = false;
+ }
+
+ @Override
+ public void onUnavailable() {
+ logi("onUnavailable");
+ mWiFiAvailable = false;
+ }
+ };
+
+ private boolean mIsEmergencyBarred;
+ private boolean mImsRegistered;
+ private boolean mIsVoiceCapable;
+ private boolean mBarringInfoReceived;
+ private boolean mImsRegStateReceived;
+ private boolean mMmTelCapabilitiesReceived;
+ private int mVoWifiTrialCount = 0;
+
+ private @RadioAccessNetworkType int mCsNetworkType = UNKNOWN;
+ private @RadioAccessNetworkType int mPsNetworkType = UNKNOWN;
+ private @RadioAccessNetworkType int mLastNetworkType = UNKNOWN;
+ private @TransportType int mLastTransportType = TRANSPORT_TYPE_INVALID;
+ private @DomainSelectionService.EmergencyScanType int mScanType;
+ private @RadioAccessNetworkType List<Integer> mLastPreferredNetworks;
+ private boolean mIsTestEmergencyNumber;
+
+ private CancellationSignal mCancelSignal;
+
+ private @RadioAccessNetworkType int[] mImsRatsConfig;
+ private @RadioAccessNetworkType int[] mCsRatsConfig;
+ private @RadioAccessNetworkType int[] mImsRoamRatsConfig;
+ private @RadioAccessNetworkType int[] mCsRoamRatsConfig;
+ private @CarrierConfigManager.ImsEmergency.EmergencyDomain int[] mDomainPreference;
+ private @CarrierConfigManager.ImsEmergency.EmergencyDomain int[] mDomainPreferenceRoam;
+ private List<String> mCdmaPreferredNumbers;
+ private boolean mPreferImsWhenCallsOnCs;
+ private int mVoWifiRequiresCondition;
+ private boolean mIsMonitoringConnectivity;
+ private boolean mWiFiAvailable;
+ private int mScanTimeout;
+ private int mMaxCellularTimeout;
+ private int mMaxNumOfVoWifiTries;
+ private boolean mVoWifiOverEmergencyPdn;
+ private @CarrierConfigManager.ImsEmergency.EmergencyScanType int mPreferredNetworkScanType;
+ private int mCallSetupTimerOnCurrentRat;
+ private boolean mRequiresImsRegistration;
+ private boolean mRequiresVoLteEnabled;
+ private boolean mLtePreferredAfterNrFailure;
+ private boolean mTryCsWhenPsFails;
+ private boolean mTryEpsFallback;
+ private int mModemCount;
+
+ /** Indicates whether this instance is deactivated. */
+ private boolean mDestroyed = false;
+ /** Indicates whether emergency network scan is requested. */
+ private boolean mIsScanRequested = false;
+ /** Indicates whether selected domain has been notified. */
+ private boolean mDomainSelected = false;
+ /** Indicates whether the cross sim redialing timer has expired. */
+ private boolean mCrossStackTimerExpired = false;
+ /** Indicates whether max cellular timer expired. */
+ private boolean mMaxCellularTimerExpired = false;
+
+ /**
+ * Indicates whether {@link #selectDomain(SelectionAttributes, TransportSelectionCallback)}
+ * is called or not.
+ */
+ private boolean mDomainSelectionRequested = false;
+
+ private final PowerManager.WakeLock mPartialWakeLock;
+ private final CrossSimRedialingController mCrossSimRedialingController;
+
+ /** Constructor. */
+ public EmergencyCallDomainSelector(Context context, int slotId, int subId,
+ @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
+ @NonNull DestroyListener destroyListener,
+ @NonNull CrossSimRedialingController csrController) {
+ super(context, slotId, subId, looper, imsStateTracker, destroyListener, TAG);
+
+ mImsStateTracker.addBarringInfoListener(this);
+ mImsStateTracker.addImsStateListener(this);
+
+ PowerManager pm = context.getSystemService(PowerManager.class);
+ mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ mCrossSimRedialingController = csrController;
+ acquireWakeLock();
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (mDestroyed) return;
+
+ switch(msg.what) {
+ case MSG_START_DOMAIN_SELECTION:
+ startDomainSelection();
+ break;
+
+ case MSG_NETWORK_SCAN_TIMEOUT:
+ handleNetworkScanTimeout();
+ break;
+
+ case MSG_NETWORK_SCAN_RESULT:
+ handleScanResult((EmergencyRegResult) msg.obj);
+ break;
+
+ case MSG_MAX_CELLULAR_TIMEOUT:
+ handleMaxCellularTimeout();
+ break;
+
+ default:
+ super.handleMessage(msg);
+ break;
+ }
+ }
+
+ /**
+ * Handles the scan result.
+ *
+ * @param result The scan result.
+ */
+ private void handleScanResult(EmergencyRegResult result) {
+ logi("handleScanResult result=" + result);
+
+ if (mLastTransportType == TRANSPORT_TYPE_WLAN) {
+ logi("handleScanResult timer expired, WLAN has been selected, ignore stale result");
+ return;
+ }
+
+ // Detected the country and found that emergency calls are not allowed with this slot.
+ if (!allowEmergencyCalls(result)) {
+ terminateSelectionPermanentlyForSlot();
+ return;
+ }
+
+ if (result.getAccessNetwork() == UNKNOWN) {
+ if ((mPreferredNetworkScanType == SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE)
+ && (mScanType == DomainSelectionService.SCAN_TYPE_FULL_SERVICE)) {
+ mScanType = DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE;
+ mWwanSelectorCallback.onRequestEmergencyNetworkScan(
+ mLastPreferredNetworks, mScanType, mCancelSignal,
+ (regResult) -> {
+ logi("requestScan-onComplete");
+ sendMessage(obtainMessage(MSG_NETWORK_SCAN_RESULT, regResult));
+ });
+ } else {
+ // Continuous scan, do not start a new timer.
+ requestScan(false);
+ }
+ return;
+ }
+
+ removeMessages(MSG_NETWORK_SCAN_TIMEOUT);
+ onWwanNetworkTypeSelected(getAccessNetworkType(result));
+ mCancelSignal = null;
+ }
+
+ /**
+ * Determines the scanned network type.
+ *
+ * @param result The result of network scan.
+ * @return The selected network type.
+ */
+ private @RadioAccessNetworkType int getAccessNetworkType(EmergencyRegResult result) {
+ int accessNetworkType = result.getAccessNetwork();
+ if (accessNetworkType != EUTRAN) return accessNetworkType;
+
+ int regState = result.getRegState();
+ int domain = result.getDomain();
+
+ // Emergency is not supported with LTE, but CSFB is possible.
+ if ((regState == REGISTRATION_STATE_HOME || regState == REGISTRATION_STATE_ROAMING)
+ && (domain == NetworkRegistrationInfo.DOMAIN_CS)) {
+ logi("getAccessNetworkType emergency not supported but CSFB is possible");
+ accessNetworkType = UTRAN;
+ }
+
+ return accessNetworkType;
+ }
+
+ @Override
+ public void cancelSelection() {
+ logi("cancelSelection");
+ finishSelection();
+ }
+
+ @Override
+ public void reselectDomain(SelectionAttributes attr) {
+ logi("reselectDomain attr=" + attr);
+ mSelectionAttributes = attr;
+ post(() -> { reselectDomain(); });
+ }
+
+ private void reselectDomain() {
+ logi("reselectDomain tryCsWhenPsFails=" + mTryCsWhenPsFails);
+
+ int cause = mSelectionAttributes.getCsDisconnectCause();
+ mCrossSimRedialingController.notifyCallFailure(cause);
+
+ // TODO(b/258112541) make EMERGENCY_PERM_FAILURE and EMERGENCY_TEMP_FAILURE public api
+ if (cause == EMERGENCY_PERM_FAILURE
+ || cause == EMERGENCY_TEMP_FAILURE) {
+ logi("reselectDomain should redial on the other subscription");
+ terminateSelectionForCrossSimRedialing(cause == EMERGENCY_PERM_FAILURE);
+ return;
+ }
+
+ if (mCrossStackTimerExpired) {
+ logi("reselectDomain cross stack timer expired");
+ terminateSelectionForCrossSimRedialing(false);
+ return;
+ }
+
+ if (mIsTestEmergencyNumber) {
+ selectDomainForTestEmergencyNumber();
+ return;
+ }
+
+ if (mTryCsWhenPsFails) {
+ mTryCsWhenPsFails = false;
+ // Initial state was CSFB available and dial PS failed.
+ // Dial CS for CSFB instead of scanning with CS preferred network list.
+ logi("reselectDomain tryCs=" + accessNetworkTypeToString(mCsNetworkType));
+ if (mCsNetworkType != UNKNOWN) {
+ onWwanNetworkTypeSelected(mCsNetworkType);
+ return;
+ }
+ }
+
+ if (mMaxCellularTimerExpired) {
+ if (mLastTransportType == TRANSPORT_TYPE_WWAN
+ && maybeDialOverWlan()) {
+ // Cellular call failed and max cellular search timer expired, so redial on Wi-Fi.
+ // If this VoWi-Fi fails, the timer shall be restarted on next reselectDomain().
+ return;
+ } else if (mLastTransportType == TRANSPORT_TYPE_WLAN) {
+ // Since VoWi-Fi failed, allow for requestScan to restart max cellular timer.
+ mMaxCellularTimerExpired = false;
+ }
+ }
+
+ if (mLastTransportType == TRANSPORT_TYPE_WLAN) {
+ // Dialing over Wi-Fi failed. Try scanning cellular networks.
+ onWwanSelected(this::reselectDomainInternal);
+ return;
+ }
+
+ requestScan(true);
+ mDomainSelected = false;
+ }
+
+ private void reselectDomainInternal() {
+ post(() -> {
+ requestScan(true, false, true);
+ mDomainSelected = false;
+ });
+ }
+
+ @Override
+ public void finishSelection() {
+ logi("finishSelection");
+ destroy();
+ }
+
+ @Override
+ public void onBarringInfoUpdated(BarringInfo barringInfo) {
+ if (mDestroyed) return;
+
+ mBarringInfoReceived = true;
+ BarringInfo.BarringServiceInfo serviceInfo =
+ barringInfo.getBarringServiceInfo(BARRING_SERVICE_TYPE_EMERGENCY);
+ mIsEmergencyBarred = serviceInfo.isBarred();
+ logi("onBarringInfoUpdated emergencyBarred=" + mIsEmergencyBarred
+ + ", serviceInfo=" + serviceInfo);
+ selectDomain();
+ }
+
+ @Override
+ public void selectDomain(SelectionAttributes attr, TransportSelectorCallback cb) {
+ logi("selectDomain attr=" + attr);
+ mTransportSelectorCallback = cb;
+ mSelectionAttributes = attr;
+ mIsTestEmergencyNumber = isTestEmergencyNumber(attr.getNumber());
+
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ mModemCount = tm.getActiveModemCount();
+
+ sendEmptyMessage(MSG_START_DOMAIN_SELECTION);
+ }
+
+ private void startDomainSelection() {
+ logi("startDomainSelection modemCount=" + mModemCount);
+ updateCarrierConfiguration();
+ mDomainSelectionRequested = true;
+ startCrossStackTimer();
+ if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ selectDomain();
+ } else {
+ logi("startDomainSelection invalid subId");
+ onImsRegistrationStateChanged();
+ onImsMmTelCapabilitiesChanged();
+ }
+ }
+
+ @Override
+ public void onImsMmTelFeatureAvailableChanged() {
+ // DOMAIN_CS shall be selected when ImsService is not available.
+ // TODO(b/258289015) Recover the temporary failure in ImsService connection.
+ }
+
+ @Override
+ public void onImsRegistrationStateChanged() {
+ mImsRegStateReceived = true;
+ mImsRegistered = mImsStateTracker.isImsRegistered();
+ logi("onImsRegistrationStateChanged " + mImsRegistered);
+ selectDomain();
+ }
+
+ @Override
+ public void onImsMmTelCapabilitiesChanged() {
+ mMmTelCapabilitiesReceived = true;
+ mIsVoiceCapable = mImsStateTracker.isImsVoiceCapable();
+ logi("onImsMmTelCapabilitiesChanged " + mIsVoiceCapable);
+ selectDomain();
+ }
+
+ /**
+ * Caches the configuration.
+ */
+ private void updateCarrierConfiguration() {
+ CarrierConfigManager configMgr = mContext.getSystemService(CarrierConfigManager.class);
+ PersistableBundle b = configMgr.getConfigForSubId(getSubId());
+ if (b == null) {
+ b = CarrierConfigManager.getDefaultConfig();
+ }
+
+ mImsRatsConfig =
+ b.getIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY);
+ mImsRoamRatsConfig = b.getIntArray(
+ KEY_EMERGENCY_OVER_IMS_ROAMING_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY);
+ if (!SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ // Default configuration includes only EUTRAN . In case of no SIM, add NGRAN.
+ mImsRatsConfig = new int[] { EUTRAN, NGRAN };
+ mImsRoamRatsConfig = new int[] { EUTRAN, NGRAN };
+ }
+
+ mCsRatsConfig =
+ b.getIntArray(KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY);
+ mCsRoamRatsConfig = b.getIntArray(
+ KEY_EMERGENCY_OVER_CS_ROAMING_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY);
+ mDomainPreference = b.getIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY);
+ mDomainPreferenceRoam = b.getIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY);
+ mPreferImsWhenCallsOnCs = b.getBoolean(
+ KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL);
+ mVoWifiRequiresCondition = b.getInt(KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT);
+ mScanTimeout = b.getInt(KEY_EMERGENCY_SCAN_TIMER_SEC_INT) * 1000;
+ mMaxCellularTimeout = b.getInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT) * 1000;
+ mMaxNumOfVoWifiTries = b.getInt(KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT);
+ mVoWifiOverEmergencyPdn = b.getBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL);
+ mPreferredNetworkScanType = b.getInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT);
+ mCallSetupTimerOnCurrentRat = b.getInt(
+ KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT) * 1000;
+ mRequiresImsRegistration = b.getBoolean(KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL);
+ mRequiresVoLteEnabled = b.getBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL);
+ mLtePreferredAfterNrFailure = b.getBoolean(
+ KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL);
+ String[] numbers = b.getStringArray(KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY);
+
+ if (mImsRatsConfig == null) mImsRatsConfig = new int[0];
+ if (mCsRatsConfig == null) mCsRatsConfig = new int[0];
+ if (mImsRoamRatsConfig == null) mImsRoamRatsConfig = new int[0];
+ if (mCsRoamRatsConfig == null) mCsRoamRatsConfig = new int[0];
+ if (mDomainPreference == null) mDomainPreference = new int[0];
+ if (mDomainPreferenceRoam == null) mDomainPreferenceRoam = new int[0];
+ if (numbers == null) numbers = new String[0];
+
+ logi("updateCarrierConfiguration "
+ + "imsRats=" + arrayToString(mImsRatsConfig,
+ EmergencyCallDomainSelector::accessNetworkTypeToString)
+ + ", csRats=" + arrayToString(mCsRatsConfig,
+ EmergencyCallDomainSelector::accessNetworkTypeToString)
+ + ", imsRoamRats=" + arrayToString(mImsRoamRatsConfig,
+ EmergencyCallDomainSelector::accessNetworkTypeToString)
+ + ", csRoamRats=" + arrayToString(mCsRoamRatsConfig,
+ EmergencyCallDomainSelector::accessNetworkTypeToString)
+ + ", domainPref=" + arrayToString(mDomainPreference,
+ EmergencyCallDomainSelector::domainPreferenceToString)
+ + ", domainPrefRoam=" + arrayToString(mDomainPreferenceRoam,
+ EmergencyCallDomainSelector::domainPreferenceToString)
+ + ", preferImsOnCs=" + mPreferImsWhenCallsOnCs
+ + ", voWifiRequiresCondition=" + mVoWifiRequiresCondition
+ + ", scanTimeout=" + mScanTimeout
+ + ", maxCellularTimeout=" + mMaxCellularTimeout
+ + ", maxNumOfVoWifiTries=" + mMaxNumOfVoWifiTries
+ + ", voWifiOverEmergencyPdn=" + mVoWifiOverEmergencyPdn
+ + ", preferredScanType=" + carrierConfigNetworkScanTypeToString(
+ mPreferredNetworkScanType)
+ + ", callSetupTimer=" + mCallSetupTimerOnCurrentRat
+ + ", requiresImsReg=" + mRequiresImsRegistration
+ + ", requiresVoLteEnabled=" + mRequiresVoLteEnabled
+ + ", ltePreferredAfterNr=" + mLtePreferredAfterNrFailure
+ + ", cdmaPreferredNumbers=" + arrayToString(numbers));
+
+ mCdmaPreferredNumbers = Arrays.asList(numbers);
+
+ if ((mPreferredNetworkScanType == CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE)
+ || (mPreferredNetworkScanType
+ == SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE)) {
+ mScanType = DomainSelectionService.SCAN_TYPE_FULL_SERVICE;
+ } else {
+ mScanType = DomainSelectionService.SCAN_TYPE_NO_PREFERENCE;
+ }
+ }
+
+ private void selectDomain() {
+ // State updated right after creation.
+ if (!mDomainSelectionRequested) return;
+
+ // Emergency network scan requested has not been completed.
+ if (mIsScanRequested) return;
+
+ // Domain selection completed, {@link #reselectDomain()} will restart domain selection.
+ if (mDomainSelected) return;
+
+ if (!mBarringInfoReceived || !mImsRegStateReceived || !mMmTelCapabilitiesReceived) {
+ logi("selectDomain not received"
+ + " BarringInfo, IMS registration state, or MMTEL capabilities");
+ return;
+ }
+
+ if (!allowEmergencyCalls(mSelectionAttributes.getEmergencyRegResult())) {
+ // Detected the country and found that emergency calls are not allowed with this slot.
+ terminateSelectionPermanentlyForSlot();
+ return;
+ }
+
+ if (isWifiPreferred()) {
+ onWlanSelected();
+ return;
+ }
+
+ onWwanSelected(this::selectDomainInternal);
+ }
+
+ private void selectDomainInternal() {
+ post(this::selectDomainFromInitialState);
+ }
+
+ private void selectDomainFromInitialState() {
+ if (mIsTestEmergencyNumber) {
+ selectDomainForTestEmergencyNumber();
+ return;
+ }
+
+ boolean csInService = isCsInService();
+ boolean psInService = isPsInService();
+
+ if (!csInService && !psInService) {
+ mPsNetworkType = getSelectablePsNetworkType(false);
+ logi("selectDomain limited service ps=" + accessNetworkTypeToString(mPsNetworkType));
+ if (mPsNetworkType == UNKNOWN) {
+ requestScan(true);
+ } else {
+ onWwanNetworkTypeSelected(mPsNetworkType);
+ }
+ return;
+ }
+
+ // Domain selection per 3GPP TS 23.167 Table H.1.
+ // PS is preferred in case selection between CS and PS is implementation option.
+ mCsNetworkType = UNKNOWN;
+ mPsNetworkType = UNKNOWN;
+ if (csInService) mCsNetworkType = getSelectableCsNetworkType();
+ if (psInService) mPsNetworkType = getSelectablePsNetworkType(true);
+
+ boolean csAvailable = mCsNetworkType != UNKNOWN;
+ boolean psAvailable = mPsNetworkType != UNKNOWN;
+
+ logi("selectDomain CS={" + csInService + ", " + accessNetworkTypeToString(mCsNetworkType)
+ + "}, PS={" + psInService + ", " + accessNetworkTypeToString(mPsNetworkType) + "}");
+ if (csAvailable && psAvailable) {
+ if (mPreferImsWhenCallsOnCs || isImsRegisteredWithVoiceCapability()) {
+ mTryCsWhenPsFails = true;
+ onWwanNetworkTypeSelected(mPsNetworkType);
+ } else if (isDeactivatedSim()) {
+ // Deactivated SIM but PS is in service and supports emergency calls.
+ onWwanNetworkTypeSelected(mPsNetworkType);
+ } else {
+ onWwanNetworkTypeSelected(mCsNetworkType);
+ }
+ } else if (psAvailable) {
+ mTryEpsFallback = (mPsNetworkType == NGRAN) && isEpsFallbackAvailable();
+ if (!mRequiresImsRegistration || isImsRegisteredWithVoiceCapability()) {
+ onWwanNetworkTypeSelected(mPsNetworkType);
+ } else if (isDeactivatedSim()) {
+ // Deactivated SIM but PS is in service and supports emergency calls.
+ onWwanNetworkTypeSelected(mPsNetworkType);
+ } else {
+ // Carrier configuration requires IMS registration for emergency services over PS,
+ // but not registered. Try CS emergency call.
+ mTryEpsFallback = false;
+ requestScan(true, true);
+ }
+ } else if (csAvailable) {
+ onWwanNetworkTypeSelected(mCsNetworkType);
+ } else {
+ // PS is in service but not supports emergency calls.
+ if (mRequiresImsRegistration && !isImsRegisteredWithVoiceCapability()) {
+ // Carrier configuration requires IMS registration for emergency services over PS,
+ // but not registered. Try CS emergency call.
+ requestScan(true, true);
+ } else {
+ mTryEpsFallback = isEpsFallbackAvailable();
+ requestScan(true);
+ }
+ }
+ }
+
+ /**
+ * Requests network scan.
+ *
+ * @param startVoWifiTimer Indicates whether a VoWifi timer will be started.
+ */
+ private void requestScan(boolean startVoWifiTimer) {
+ requestScan(startVoWifiTimer, false);
+ }
+
+ /**
+ * Requests network scan.
+ *
+ * @param startVoWifiTimer Indicates whether a VoWifi timer will be started.
+ * @param csPreferred Indicates whether CS preferred scan is requested.
+ */
+ private void requestScan(boolean startVoWifiTimer, boolean csPreferred) {
+ requestScan(startVoWifiTimer, csPreferred, false);
+ }
+
+ /**
+ * Requests network scan.
+ *
+ * @param startVoWifiTimer Indicates whether a VoWifi timer will be started.
+ * @param csPreferred Indicates whether CS preferred scan is requested.
+ * @param wifiFailed Indicates dialing over Wi-Fi has failed.
+ */
+ private void requestScan(boolean startVoWifiTimer, boolean csPreferred, boolean wifiFailed) {
+ logi("requestScan timer=" + startVoWifiTimer + ", csPreferred=" + csPreferred
+ + ", wifiFailed=" + wifiFailed);
+
+ mCancelSignal = new CancellationSignal();
+ // In case dialing over Wi-Fi has failed, do not the change the domain preference.
+ if (!wifiFailed) {
+ mLastPreferredNetworks = getNextPreferredNetworks(csPreferred, mTryEpsFallback,
+ !startVoWifiTimer);
+ }
+ mTryEpsFallback = false;
+
+ if (isInRoaming()
+ && (mPreferredNetworkScanType == DomainSelectionService.SCAN_TYPE_FULL_SERVICE)) {
+ // FULL_SERVICE only preference is available only when not in roaming.
+ mScanType = DomainSelectionService.SCAN_TYPE_NO_PREFERENCE;
+ }
+
+ mIsScanRequested = true;
+ mWwanSelectorCallback.onRequestEmergencyNetworkScan(
+ mLastPreferredNetworks, mScanType, mCancelSignal,
+ (result) -> {
+ logi("requestScan-onComplete");
+ sendMessage(obtainMessage(MSG_NETWORK_SCAN_RESULT, result));
+ });
+
+ if (startVoWifiTimer && SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ if (isEmcOverWifiSupported()
+ && mScanTimeout > 0 && mVoWifiTrialCount < mMaxNumOfVoWifiTries) {
+ logi("requestScan start scan timer");
+ // remove any pending timers.
+ removeMessages(MSG_NETWORK_SCAN_TIMEOUT);
+ sendEmptyMessageDelayed(MSG_NETWORK_SCAN_TIMEOUT, mScanTimeout);
+ registerForConnectivityChanges();
+ }
+ }
+ if (!mMaxCellularTimerExpired && !hasMessages(MSG_MAX_CELLULAR_TIMEOUT)) {
+ startMaxCellularTimer();
+ }
+ }
+
+ /**
+ * Gets the list of preferred network type for the new scan request.
+ *
+ * @param csPreferred Indicates whether CS preferred scan is requested.
+ * @param tryEpsFallback Indicates whether scan requested for EPS fallback.
+ * @param lastScanFailed Indicates whether this a scan request due to the failure of last scan
+ * request.
+ * @return The list of preferred network types.
+ */
+ @VisibleForTesting
+ public @RadioAccessNetworkType List<Integer> getNextPreferredNetworks(boolean csPreferred,
+ boolean tryEpsFallback, boolean lastScanFailed) {
+ if (mRequiresVoLteEnabled && !isAdvancedCallingSettingEnabled()) {
+ // Emergency call over IMS is not supported.
+ logi("getNextPreferredNetworks VoLte setting is not enabled.");
+ return generatePreferredNetworks(getCsNetworkTypeConfiguration());
+ }
+
+ List<Integer> preferredNetworks = new ArrayList<>();
+
+ List<Integer> domains = getDomainPreference();
+ int psPriority = domains.indexOf(DOMAIN_PS_3GPP);
+ int csPriority = domains.indexOf(DOMAIN_CS);
+ logi("getNextPreferredNetworks psPriority=" + psPriority + ", csPriority=" + csPriority
+ + ", csPreferred=" + csPreferred + ", epsFallback=" + tryEpsFallback
+ + ", lastNetworkType=" + accessNetworkTypeToString(mLastNetworkType));
+
+ if (!csPreferred && (mLastNetworkType == UNKNOWN || tryEpsFallback)) {
+ // Generate the list per the domain preference.
+
+ if (psPriority == NOT_SUPPORTED && csPriority == NOT_SUPPORTED) {
+ // should not reach here. However, to avoid unexpected problems.
+ preferredNetworks = generatePreferredNetworks(getCsNetworkTypeConfiguration(),
+ getImsNetworkTypeConfiguration());
+ } else if (psPriority == NOT_SUPPORTED && csPriority > NOT_SUPPORTED) {
+ // CS networks only.
+ preferredNetworks = generatePreferredNetworks(getCsNetworkTypeConfiguration());
+ } else if (psPriority > NOT_SUPPORTED && csPriority == NOT_SUPPORTED) {
+ // PS networks only.
+ preferredNetworks = generatePreferredNetworks(getImsNetworkTypeConfiguration());
+ } else if (psPriority < csPriority) {
+ // PS preferred.
+ preferredNetworks = generatePreferredNetworks(getImsNetworkTypeConfiguration(),
+ getCsNetworkTypeConfiguration());
+ } else {
+ // CS preferred.
+ preferredNetworks = generatePreferredNetworks(getCsNetworkTypeConfiguration(),
+ getImsNetworkTypeConfiguration());
+ }
+
+ // Make NGRAN have the lowest priority
+ if (tryEpsFallback && preferredNetworks.contains(NGRAN)) {
+ preferredNetworks.remove(Integer.valueOf(NGRAN));
+ preferredNetworks.add(NGRAN);
+ }
+ } else if (csPreferred || mLastNetworkType == EUTRAN || mLastNetworkType == NGRAN) {
+ if (!csPreferred && mLastNetworkType == NGRAN && mLtePreferredAfterNrFailure) {
+ // LTE is preferred after dialing over NR failed.
+ List<Integer> imsRats = getImsNetworkTypeConfiguration();
+ imsRats.remove(Integer.valueOf(NGRAN));
+ preferredNetworks = generatePreferredNetworks(imsRats,
+ getCsNetworkTypeConfiguration());
+ } else if (csPriority > NOT_SUPPORTED) {
+ // PS tried, generate the list with CS preferred.
+ preferredNetworks = generatePreferredNetworks(getCsNetworkTypeConfiguration(),
+ getImsNetworkTypeConfiguration());
+ } else {
+ // CS not suppored.
+ preferredNetworks = generatePreferredNetworks(getImsNetworkTypeConfiguration());
+ }
+ } else {
+ // CS tried, generate the list with PS preferred.
+ if (psPriority > NOT_SUPPORTED) {
+ preferredNetworks = generatePreferredNetworks(getImsNetworkTypeConfiguration(),
+ getCsNetworkTypeConfiguration());
+ } else {
+ // PS not suppored.
+ preferredNetworks = generatePreferredNetworks(getCsNetworkTypeConfiguration());
+ }
+ }
+
+ // There can be cases that dialing IMS call failed but the modem doesn't know this
+ // situation with some vendor solutions. For example, dialing failure due to the
+ // emergency registration failure.
+ // Remove the current RAT from the scan list to avoid modem select current PLMN.
+ // If the scan fails, the next scan will include this RAT again.
+ //
+ // TODO (b/278183420) Replace this with a better solution by adding indication
+ // of call setup failure to the scan request.
+ ImsReasonInfo reasonInfo = mSelectionAttributes.getPsDisconnectCause();
+ if (!lastScanFailed && reasonInfo != null
+ && reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED) {
+ logi("getNextPreferredNetworks remove " + mLastNetworkType);
+ if (preferredNetworks.size() > 1) {
+ preferredNetworks.remove(Integer.valueOf(mLastNetworkType));
+ }
+ }
+
+ return preferredNetworks;
+ }
+
+ private @RadioAccessNetworkType List<Integer> generatePreferredNetworks(List<Integer>...lists) {
+ List<Integer> preferredNetworks = new ArrayList<>();
+ for (List<Integer> list : lists) {
+ preferredNetworks.addAll(list);
+ }
+
+ return preferredNetworks;
+ }
+
+ private void handleMaxCellularTimeout() {
+ logi("handleMaxCellularTimeout");
+ if (mVoWifiTrialCount >= mMaxNumOfVoWifiTries) {
+ logi("handleMaxCellularTimeout already tried maximum");
+ return;
+ }
+
+ mMaxCellularTimerExpired = true;
+
+ if (mDomainSelected) {
+ // Dialing is already requested.
+ logi("handleMaxCellularTimeout wait for reselectDomain");
+ return;
+ }
+
+ if (!maybeDialOverWlan()) {
+ logd("handleMaxCellularTimeout VoWi-Fi is not available");
+ }
+ }
+
+ private void handleNetworkScanTimeout() {
+ logi("handleNetworkScanTimeout");
+ maybeDialOverWlan();
+ }
+
+ private boolean maybeDialOverWlan() {
+ logi("maybeDialOverWlan overEmergencyPdn=" + mVoWifiOverEmergencyPdn
+ + ", wifiAvailable=" + mWiFiAvailable);
+ boolean available = mWiFiAvailable;
+ if (mVoWifiOverEmergencyPdn) {
+ // SOS APN
+ if (!available && isImsRegisteredOverCrossSim()) {
+ available = true;
+ }
+ if (available) {
+ switch (mVoWifiRequiresCondition) {
+ case VOWIFI_REQUIRES_SETTING_ENABLED:
+ available = isWifiCallingSettingEnabled();
+ break;
+ case VOWIFI_REQUIRES_VALID_EID:
+ available = isWifiCallingActivated();
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ // IMS APN. When IMS is already registered over Wi-Fi.
+ available = isImsRegisteredWithVoiceCapability() && isImsRegisteredOverWifi();
+ }
+
+ logi("maybeDialOverWlan VoWi-Fi available=" + available);
+ if (available) {
+ if (mCancelSignal != null) {
+ mCancelSignal.cancel();
+ mCancelSignal = null;
+ }
+ onWlanSelected();
+ }
+
+ return available;
+ }
+
+ /**
+ * Determines whether CS is in service.
+ *
+ * @return {@code true} if CS is in service.
+ */
+ private boolean isCsInService() {
+ EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+ if (regResult == null) return false;
+
+ int regState = regResult.getRegState();
+ int domain = regResult.getDomain();
+
+ if ((regState == REGISTRATION_STATE_HOME || regState == REGISTRATION_STATE_ROAMING)
+ && ((domain & NetworkRegistrationInfo.DOMAIN_CS) > 0)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines the network type of the circuit-switched(CS) network.
+ *
+ * @return The network type of the CS network.
+ */
+ private @RadioAccessNetworkType int getSelectableCsNetworkType() {
+ EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+ logi("getSelectableCsNetworkType regResult=" + regResult);
+ if (regResult == null) return UNKNOWN;
+
+ int accessNetwork = regResult.getAccessNetwork();
+
+ List<Integer> ratList = getCsNetworkTypeConfiguration();
+ if (ratList.contains(accessNetwork)) {
+ return accessNetwork;
+ }
+
+ if ((regResult.getAccessNetwork() == EUTRAN)
+ && ((regResult.getDomain() & NetworkRegistrationInfo.DOMAIN_CS) > 0)) {
+ if (ratList.contains(UTRAN)) return UTRAN;
+ }
+
+ return UNKNOWN;
+ }
+
+ /**
+ * Determines whether PS is in service.
+ *
+ * @return {@code true} if PS is in service.
+ */
+ private boolean isPsInService() {
+ EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+ if (regResult == null) return false;
+
+ int regState = regResult.getRegState();
+ int domain = regResult.getDomain();
+
+ if ((regState == REGISTRATION_STATE_HOME || regState == REGISTRATION_STATE_ROAMING)
+ && ((domain & NetworkRegistrationInfo.DOMAIN_PS) > 0)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines the network type supporting emergency services over packet-switched(PS) network.
+ *
+ * @param inService Indicates whether PS is IN_SERVICE state.
+ * @return The network type if the network supports emergency services over PS network.
+ */
+ private @RadioAccessNetworkType int getSelectablePsNetworkType(boolean inService) {
+ EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+ logi("getSelectablePsNetworkType regResult=" + regResult);
+ if (regResult == null) return UNKNOWN;
+ if (mRequiresVoLteEnabled && !isAdvancedCallingSettingEnabled()) {
+ // Emergency call over IMS is not supported.
+ logi("getSelectablePsNetworkType VoLte setting is not enabled.");
+ return UNKNOWN;
+ }
+
+ int accessNetwork = regResult.getAccessNetwork();
+ List<Integer> ratList = getImsNetworkTypeConfiguration();
+ if (ratList.contains(accessNetwork)) {
+ if (mIsEmergencyBarred) {
+ logi("getSelectablePsNetworkType barred");
+ return UNKNOWN;
+ }
+ if (accessNetwork == NGRAN) {
+ return (regResult.getNwProvidedEmc() > 0 && regResult.isVopsSupported())
+ ? NGRAN : UNKNOWN;
+ } else if (accessNetwork == EUTRAN) {
+ return (regResult.isEmcBearerSupported()
+ && (regResult.isVopsSupported() || !inService))
+ ? EUTRAN : UNKNOWN;
+ }
+ }
+
+ return UNKNOWN;
+ }
+
+ private boolean isEpsFallbackAvailable() {
+ EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+ if (regResult == null) return false;
+
+ List<Integer> ratList = getImsNetworkTypeConfiguration();
+ if (ratList.contains(EUTRAN)) {
+ return (regResult.getNwProvidedEmf() > 0);
+ }
+ return false;
+ }
+
+ /**
+ * Determines whether the SIM is a deactivated one.
+ *
+ * @return {@code true} if the SIM is a deactivated one.
+ */
+ private boolean isDeactivatedSim() {
+ if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ tm = tm.createForSubscriptionId(getSubId());
+ int state = tm.getDataActivationState();
+ logi("isDeactivatedSim state=" + state);
+ return (state == TelephonyManager.SIM_ACTIVATION_STATE_DEACTIVATED);
+ }
+ return false;
+ }
+
+ /**
+ * Determines whether emergency call over Wi-Fi is allowed.
+ *
+ * @return {@code true} if emergency call over Wi-Fi allowed.
+ */
+ private boolean isEmcOverWifiSupported() {
+ if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ List<Integer> domains = getDomainPreference();
+ boolean ret = domains.contains(DOMAIN_PS_NON_3GPP);
+ logi("isEmcOverWifiSupported " + ret);
+ return ret;
+ } else {
+ logi("isEmcOverWifiSupported invalid subId");
+ }
+ return false;
+ }
+
+ /**
+ * Determines whether Wi-Fi is preferred when IMS registered over Wi-Fi.
+ *
+ * @return {@code true} if Wi-Fi is preferred when IMS registered over Wi-Fi.
+ */
+ private boolean isWifiPreferred() {
+ if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ List<Integer> domains = getDomainPreference();
+ int priority = domains.indexOf(DOMAIN_PS_NON_3GPP);
+ logi("isWifiPreferred priority=" + priority);
+
+ if ((priority == 0)
+ && isImsRegisteredWithVoiceCapability()
+ && isImsRegisteredOverWifi()) {
+ logi("isWifiPreferred try emergency call over Wi-Fi");
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean isAdvancedCallingSettingEnabled() {
+ try {
+ if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ ImsManager imsMngr = mContext.getSystemService(ImsManager.class);
+ ImsMmTelManager mmTelManager = imsMngr.getImsMmTelManager(getSubId());
+ boolean result = mmTelManager.isAdvancedCallingSettingEnabled();
+ logi("isAdvancedCallingSettingEnabled " + result);
+ return result;
+ }
+ } catch (Exception e) {
+ logi("isAdvancedCallingSettingEnabled e=" + e);
+ }
+ return true;
+ }
+
+ private boolean isWifiCallingActivated() {
+ try {
+ ImsManager imsMngr = mContext.getSystemService(ImsManager.class);
+ ProvisioningManager pm = imsMngr.getProvisioningManager(getSubId());
+ String eid = pm.getProvisioningStringValue(
+ ProvisioningManager.KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID);
+ boolean activated = (!TextUtils.isEmpty(eid)) && (!TextUtils.equals("0", eid));
+ logi("isWifiCallingActivated " + activated);
+ return activated;
+ } catch (Exception e) {
+ logi("isWifiCallingActivated e=" + e);
+ }
+ return false;
+ }
+
+ private boolean isWifiCallingSettingEnabled() {
+ boolean result = false;
+ try {
+ if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ ImsManager imsMngr = mContext.getSystemService(ImsManager.class);
+ ImsMmTelManager mmTelManager = imsMngr.getImsMmTelManager(getSubId());
+ if (isInRoaming()) {
+ result = mmTelManager.isVoWiFiRoamingSettingEnabled();
+ } else {
+ result = mmTelManager.isVoWiFiSettingEnabled();
+ }
+ logi("isWifiCallingSettingEnabled " + result);
+ return result;
+ }
+ } catch (Exception e) {
+ logi("isWifiCallingSettingEnabled e=" + e);
+ }
+ return result;
+ }
+
+ private @NonNull List<Integer> getImsNetworkTypeConfiguration() {
+ int[] rats = mImsRatsConfig;
+ if (isInRoaming()) rats = mImsRoamRatsConfig;
+
+ List<Integer> ratList = new ArrayList<Integer>();
+ for (int i = 0; i < rats.length; i++) {
+ ratList.add(rats[i]);
+ }
+ return ratList;
+ }
+
+ private @NonNull List<Integer> getCsNetworkTypeConfiguration() {
+ int[] rats = mCsRatsConfig;
+ if (isInRoaming()) rats = mCsRoamRatsConfig;
+
+ List<Integer> ratList = new ArrayList<Integer>();
+ for (int i = 0; i < rats.length; i++) {
+ ratList.add(rats[i]);
+ }
+
+ if (!mCdmaPreferredNumbers.isEmpty()) {
+ if (mCdmaPreferredNumbers.contains(mSelectionAttributes.getNumber())) {
+ // The number will be dialed over CDMA.
+ ratList.clear();
+ ratList.add(new Integer(CDMA2000));
+ } else {
+ // The number will be dialed over UTRAN or GERAN.
+ ratList.remove(new Integer(CDMA2000));
+ }
+ }
+
+ return ratList;
+ }
+
+ private @NonNull List<Integer> getDomainPreference() {
+ int[] domains = mDomainPreference;
+ if (isInRoaming()) domains = mDomainPreferenceRoam;
+
+ List<Integer> domainList = new ArrayList<Integer>();
+ for (int i = 0; i < domains.length; i++) {
+ domainList.add(domains[i]);
+ }
+ return domainList;
+ }
+
+ private boolean isInRoaming() {
+ if (!SubscriptionManager.isValidSubscriptionId(getSubId())) return false;
+
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ tm = tm.createForSubscriptionId(getSubId());
+ String netIso = tm.getNetworkCountryIso();
+
+ EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+ if (regResult != null) {
+ if (regResult.getRegState() == REGISTRATION_STATE_HOME) return false;
+ if (regResult.getRegState() == REGISTRATION_STATE_ROAMING) return true;
+
+ String iso = regResult.getIso();
+ if (!TextUtils.isEmpty(iso)) netIso = iso;
+ }
+
+ String simIso = tm.getSimCountryIso();
+ logi("isInRoaming simIso=" + simIso + ", netIso=" + netIso);
+
+ if (TextUtils.isEmpty(simIso)) return false;
+ if (TextUtils.isEmpty(netIso)) return false;
+
+ return !(TextUtils.equals(simIso, netIso));
+ }
+
+ /**
+ * Determines whether IMS is registered over Wi-Fi.
+ *
+ * @return {@code true} if IMS is registered over Wi-Fi.
+ */
+ private boolean isImsRegisteredOverWifi() {
+ boolean ret = false;
+ if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ ret = mImsStateTracker.isImsRegisteredOverWlan();
+ }
+
+ logi("isImsRegisteredOverWifi " + ret);
+ return ret;
+ }
+
+ /**
+ * Determines whether IMS is registered over the mobile data of another subscription.
+ *
+ * @return {@code true} if IMS is registered over the mobile data of another subscription.
+ */
+ private boolean isImsRegisteredOverCrossSim() {
+ boolean ret = false;
+ if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ ret = mImsStateTracker.isImsRegisteredOverCrossSim();
+ }
+
+ logi("isImsRegisteredOverCrossSim " + ret);
+ return ret;
+ }
+
+ /**
+ * Determines whether IMS is registered with voice capability.
+ *
+ * @return {@code true} if IMS is registered with voice capability.
+ */
+ private boolean isImsRegisteredWithVoiceCapability() {
+ boolean ret = mImsRegistered && mIsVoiceCapable;
+
+ logi("isImsRegisteredWithVoiceCapability " + ret);
+ return ret;
+ }
+
+ private void onWlanSelected() {
+ logi("onWlanSelected");
+ if (mLastTransportType == TRANSPORT_TYPE_WLAN) {
+ logi("onWlanSelected ignore duplicated callback");
+ return;
+ }
+
+ mDomainSelected = true;
+ mLastTransportType = TRANSPORT_TYPE_WLAN;
+ mVoWifiTrialCount++;
+ mTransportSelectorCallback.onWlanSelected(mVoWifiOverEmergencyPdn);
+ mWwanSelectorCallback = null;
+ removeMessages(MSG_NETWORK_SCAN_TIMEOUT);
+ removeMessages(MSG_MAX_CELLULAR_TIMEOUT);
+ }
+
+ private void onWwanSelected(Runnable runnable) {
+ logi("onWwanSelected");
+ if (mLastTransportType == TRANSPORT_TYPE_WWAN) {
+ logi("onWwanSelected ignore duplicated callback");
+ return;
+ }
+
+ mLastTransportType = TRANSPORT_TYPE_WWAN;
+ mTransportSelectorCallback.onWwanSelected((callback) -> {
+ mWwanSelectorCallback = callback;
+ runnable.run();
+ });
+ }
+
+ private void onWwanNetworkTypeSelected(@RadioAccessNetworkType int accessNetworkType) {
+ logi("onWwanNetworkTypeSelected " + accessNetworkTypeToString(accessNetworkType));
+ if (mWwanSelectorCallback == null) {
+ logi("onWwanNetworkTypeSelected callback is null");
+ return;
+ }
+
+ mDomainSelected = true;
+ mLastNetworkType = accessNetworkType;
+ int domain = NetworkRegistrationInfo.DOMAIN_CS;
+ if (accessNetworkType == EUTRAN || accessNetworkType == NGRAN) {
+ domain = NetworkRegistrationInfo.DOMAIN_PS;
+ }
+ mWwanSelectorCallback.onDomainSelected(domain,
+ (domain == NetworkRegistrationInfo.DOMAIN_PS));
+ }
+
+ /**
+ * Registers for changes to network connectivity.
+ */
+ private void registerForConnectivityChanges() {
+ if (mIsMonitoringConnectivity) {
+ return;
+ }
+
+ ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ if (cm != null) {
+ logi("registerForConnectivityChanges");
+ NetworkRequest.Builder builder = new NetworkRequest.Builder();
+ builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ cm.registerNetworkCallback(builder.build(), mNetworkCallback);
+ mIsMonitoringConnectivity = true;
+ }
+ }
+
+ /**
+ * Unregisters for connectivity changes.
+ */
+ private void unregisterForConnectivityChanges() {
+ if (!mIsMonitoringConnectivity) {
+ return;
+ }
+
+ ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ if (cm != null) {
+ logi("unregisterForConnectivityChanges");
+ cm.unregisterNetworkCallback(mNetworkCallback);
+ mIsMonitoringConnectivity = false;
+ }
+ }
+
+ /** Starts the max cellular timer. */
+ private void startMaxCellularTimer() {
+ logd("startMaxCellularTimer tried=" + mVoWifiTrialCount
+ + ", max=" + mMaxNumOfVoWifiTries);
+ if (isEmcOverWifiSupported()
+ && (mMaxCellularTimeout > 0)
+ && (mVoWifiTrialCount < mMaxNumOfVoWifiTries)) {
+ logi("startMaxCellularTimer start timer");
+ sendEmptyMessageDelayed(MSG_MAX_CELLULAR_TIMEOUT, mMaxCellularTimeout);
+ registerForConnectivityChanges();
+ }
+ }
+
+ private boolean allowEmergencyCalls(EmergencyRegResult regResult) {
+ if (mModemCount < 2) return true;
+ if (regResult == null) {
+ loge("allowEmergencyCalls null regResult");
+ return true;
+ }
+
+ String iso = regResult.getIso();
+ if (sAllowOnlyWithSimReady.contains(iso)) {
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ int simState = tm.getSimState(getSlotId());
+ if (simState != TelephonyManager.SIM_STATE_READY) {
+ logi("allowEmergencyCalls not ready, simState=" + simState + ", iso=" + iso);
+ if (mCrossSimRedialingController.isThereOtherSlot()) {
+ return false;
+ }
+ logi("allowEmergencyCalls there is no other slot available");
+ }
+ }
+
+ return true;
+ }
+
+ private void terminateSelectionPermanentlyForSlot() {
+ logi("terminateSelectionPermanentlyForSlot");
+ terminateSelection(true);
+ }
+
+ private void terminateSelectionForCrossSimRedialing(boolean permanent) {
+ logi("terminateSelectionForCrossSimRedialing perm=" + permanent);
+ terminateSelection(permanent);
+ }
+
+ private void terminateSelection(boolean permanent) {
+ mTransportSelectorCallback.onSelectionTerminated(permanent
+ ? DisconnectCause.EMERGENCY_PERM_FAILURE
+ : DisconnectCause.EMERGENCY_TEMP_FAILURE);
+
+ if (mIsScanRequested && mCancelSignal != null) {
+ mCancelSignal.cancel();
+ mCancelSignal = null;
+ }
+ }
+
+ /** Starts the cross stack timer. */
+ public void startCrossStackTimer() {
+ boolean inService = false;
+ boolean inRoaming = false;
+
+ if (mModemCount == 1) return;
+
+ EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+ if (regResult != null) {
+ int regState = regResult.getRegState();
+
+ if ((regResult.getDomain() > 0)
+ && (regState == REGISTRATION_STATE_HOME
+ || regState == REGISTRATION_STATE_ROAMING)) {
+ inService = true;
+ }
+ inRoaming = (regState == REGISTRATION_STATE_ROAMING) || isInRoaming();
+ }
+
+ mCrossSimRedialingController.startTimer(mContext, this, mSelectionAttributes.getCallId(),
+ mSelectionAttributes.getNumber(), inService, inRoaming, mModemCount);
+ }
+
+ /** Notifies that the cross stack redilaing timer has been expired. */
+ public void notifyCrossStackTimerExpired() {
+ logi("notifyCrossStackTimerExpired");
+
+ mCrossStackTimerExpired = true;
+ if (mDomainSelected) {
+ // When reselecting domain, terminateSelection will be called.
+ return;
+ }
+ terminateSelectionForCrossSimRedialing(false);
+ }
+
+ private static String arrayToString(int[] intArray, IntFunction<String> func) {
+ int length = intArray.length;
+ StringBuilder sb = new StringBuilder("{");
+ if (length > 0) {
+ int i = 0;
+ sb.append(func.apply(intArray[i++]));
+ while (i < length) {
+ sb.append(", ").append(func.apply(intArray[i++]));
+ }
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private static String arrayToString(String[] stringArray) {
+ StringBuilder sb;
+ int length = stringArray.length;
+ sb = new StringBuilder("{");
+ if (length > 0) {
+ int i = 0;
+ sb.append(stringArray[i++]);
+ while (i < length) {
+ sb.append(", ").append(stringArray[i++]);
+ }
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private static String domainPreferenceToString(
+ @CarrierConfigManager.ImsEmergency.EmergencyDomain int domain) {
+ switch (domain) {
+ case DOMAIN_CS: return "CS";
+ case DOMAIN_PS_3GPP: return "PS_3GPP";
+ case DOMAIN_PS_NON_3GPP: return "PS_NON_3GPP";
+ default: return "UNKNOWN";
+ }
+ }
+
+ private static String carrierConfigNetworkScanTypeToString(
+ @CarrierConfigManager.ImsEmergency.EmergencyScanType int scanType) {
+ switch (scanType) {
+ case CarrierConfigManager.ImsEmergency.SCAN_TYPE_NO_PREFERENCE: return "NO_PREF";
+ case CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE: return "FULL";
+ case SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE: return "FULL_N_LIMITED";
+ default: return "UNKNOWN";
+ }
+ }
+
+ private static String accessNetworkTypeToString(
+ @RadioAccessNetworkType int accessNetworkType) {
+ switch (accessNetworkType) {
+ case AccessNetworkType.UNKNOWN: return "UNKNOWN";
+ case AccessNetworkType.GERAN: return "GERAN";
+ case AccessNetworkType.UTRAN: return "UTRAN";
+ case AccessNetworkType.EUTRAN: return "EUTRAN";
+ case AccessNetworkType.CDMA2000: return "CDMA2000";
+ case AccessNetworkType.IWLAN: return "IWLAN";
+ case AccessNetworkType.NGRAN: return "NGRAN";
+ default: return Integer.toString(accessNetworkType);
+ }
+ }
+
+ /**
+ * Destroys the instance.
+ */
+ @VisibleForTesting
+ public void destroy() {
+ if (DBG) logd("destroy");
+
+ mCrossSimRedialingController.stopTimer();
+ releaseWakeLock();
+
+ mDestroyed = true;
+ mImsStateTracker.removeBarringInfoListener(this);
+ mImsStateTracker.removeImsStateListener(this);
+ unregisterForConnectivityChanges();
+
+ super.destroy();
+ }
+
+ private void acquireWakeLock() {
+ if (mPartialWakeLock != null) {
+ synchronized (mPartialWakeLock) {
+ logi("acquireWakeLock");
+ mPartialWakeLock.acquire();
+ }
+ }
+ }
+
+ private void releaseWakeLock() {
+ if (mPartialWakeLock != null) {
+ synchronized (mPartialWakeLock) {
+ if (mPartialWakeLock.isHeld()) {
+ logi("releaseWakeLock");
+ mPartialWakeLock.release();
+ }
+ }
+ }
+ }
+
+ private void selectDomainForTestEmergencyNumber() {
+ logi("selectDomainForTestEmergencyNumber");
+ if (isImsRegisteredWithVoiceCapability()) {
+ onWwanNetworkTypeSelected(EUTRAN);
+ } else {
+ onWwanNetworkTypeSelected(UTRAN);
+ }
+ }
+
+ private boolean isTestEmergencyNumber(String number) {
+ number = PhoneNumberUtils.stripSeparators(number);
+ Map<Integer, List<EmergencyNumber>> list = new HashMap<>();
+ try {
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ list = tm.getEmergencyNumberList();
+ } catch (IllegalStateException ise) {
+ loge("isTestEmergencyNumber ise=" + ise);
+ }
+
+ for (Integer sub : list.keySet()) {
+ for (EmergencyNumber eNumber : list.get(sub)) {
+ if (number.equals(eNumber.getNumber())
+ && eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST)) {
+ logd("isTestEmergencyNumber: " + number + " is a test emergency number.");
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void logi(String msg) {
+ super.logi(msg);
+ sLocalLog.log(msg);
+ }
+
+ @Override
+ protected void loge(String msg) {
+ super.loge(msg);
+ sLocalLog.log(msg);
+ }
+}
diff --git a/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelector.java
new file mode 100644
index 0000000..aef193b
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelector.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.BarringInfo;
+import android.telephony.CarrierConfigManager;
+import android.telephony.DataSpecificRegistrationInfo;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.VopsSupportInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Implements an emergency SMS domain selector for sending an emergency SMS.
+ */
+public class EmergencySmsDomainSelector extends SmsDomainSelector implements
+ ImsStateTracker.BarringInfoListener, ImsStateTracker.ServiceStateListener {
+ /**
+ * Stores the configuration value of
+ * {@link CarrierConfigManager#KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL}.
+ * This value is always updated whenever the domain selection is requested.
+ */
+ private Boolean mEmergencySmsOverImsSupportedByConfig;
+ private ServiceState mServiceState;
+ private boolean mServiceStateReceived;
+ private BarringInfo mBarringInfo;
+ private boolean mBarringInfoReceived;
+
+ public EmergencySmsDomainSelector(Context context, int slotId, int subId,
+ @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
+ @NonNull DestroyListener listener) {
+ super(context, slotId, subId, looper, imsStateTracker, listener,
+ "DomainSelector-EmergencySMS");
+
+ mImsStateTracker.addServiceStateListener(this);
+ mImsStateTracker.addBarringInfoListener(this);
+ }
+
+ @Override
+ public void destroy() {
+ if (mDestroyed) {
+ return;
+ }
+ mImsStateTracker.removeServiceStateListener(this);
+ mImsStateTracker.removeBarringInfoListener(this);
+ super.destroy();
+ }
+
+ @Override
+ public void finishSelection() {
+ super.finishSelection();
+ mServiceStateReceived = false;
+ mServiceState = null;
+ mBarringInfoReceived = false;
+ mBarringInfo = null;
+ mEmergencySmsOverImsSupportedByConfig = null;
+ }
+
+ @Override
+ public void onBarringInfoUpdated(BarringInfo barringInfo) {
+ mBarringInfoReceived = true;
+ mBarringInfo = barringInfo;
+ sendMessageForDomainSelection();
+ }
+
+ @Override
+ public void onServiceStateUpdated(ServiceState serviceState) {
+ mServiceStateReceived = true;
+ mServiceState = serviceState;
+ sendMessageForDomainSelection();
+ }
+
+ /**
+ * Checks whether the domain selector is ready to select the domain or not.
+ * The emergency SMS requires to be updated for the {@link ServiceState} and
+ * {@link BarringInfo} to confirm that the cellular network supports to send emergency SMS
+ * messages over IMS.
+ */
+ @VisibleForTesting
+ public boolean isDomainSelectionReady() {
+ return mServiceStateReceived && mBarringInfoReceived;
+ }
+
+ @Override
+ protected boolean isSmsOverImsAvailable() {
+ if (super.isSmsOverImsAvailable()) {
+ /**
+ * Even though IMS is successfully registered, the cellular domain should be
+ * available for the emergency SMS according to the carrier's requirement
+ * when {@link CarrierConfigManager#KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL} is set
+ * to true.
+ */
+ if (isEmergencySmsOverImsSupportedIfLteLimitedOrInService()) {
+ /**
+ * Emergency SMS should be supported via emergency PDN.
+ * If this condition is false, then need to fallback to CS network
+ * because the current PS network does not allow the emergency service.
+ */
+ return isNetworkAvailableForImsEmergencySms();
+ }
+
+ // Emergency SMS is supported via IMS PDN.
+ return true;
+ }
+
+ return isImsEmergencySmsAvailable();
+ }
+
+ @Override
+ protected void selectDomain() {
+ if (!isDomainSelectionRequested()) {
+ logi("Domain selection is not requested!");
+ return;
+ }
+
+ if (!isDomainSelectionReady()) {
+ logd("Wait for the readiness of the domain selection!");
+ return;
+ }
+
+ logi("selectDomain: " + mImsStateTracker.imsStateToString());
+
+ if (isSmsOverImsAvailable()) {
+ boolean isEmergencySmsOverImsSupportedIfLteLimitedOrInService =
+ isEmergencySmsOverImsSupportedIfLteLimitedOrInService();
+
+ if (mImsStateTracker.isImsRegisteredOverWlan()) {
+ /**
+ * When {@link CarrierConfigManager#KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL}
+ * is set to true, the emergency SMS supports on the LTE network using the
+ * emergency PDN. As of now, since the emergency SMS doesn't use the emergency PDN
+ * over WLAN, the domain selector reports the domain as WLAN only if
+ * {@code isEmergencySmsOverImsSupportedIfLteLimitedOrInService} is set to false
+ * and IMS is registered over WLAN.
+ * Otherwise, the domain selector reports the domain as WWAN.
+ */
+ if (!isEmergencySmsOverImsSupportedIfLteLimitedOrInService) {
+ notifyWlanSelected(false);
+ return;
+ }
+
+ logi("DomainSelected: WLAN >> WWAN");
+ }
+ notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_PS,
+ isEmergencySmsOverImsSupportedIfLteLimitedOrInService);
+ } else {
+ notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_CS, false);
+ }
+ }
+
+ /**
+ * Checks if the emergency SMS messages over IMS is available according to the carrier
+ * configuration and the current network states.
+ */
+ private boolean isImsEmergencySmsAvailable() {
+ boolean isEmergencySmsOverImsSupportedIfLteLimitedOrInService =
+ isEmergencySmsOverImsSupportedIfLteLimitedOrInService();
+ boolean networkAvailable = isNetworkAvailableForImsEmergencySms();
+
+ logi("isImsEmergencySmsAvailable: "
+ + "emergencySmsOverIms=" + isEmergencySmsOverImsSupportedIfLteLimitedOrInService
+ + ", mmTelFeatureAvailable=" + mImsStateTracker.isMmTelFeatureAvailable()
+ + ", networkAvailable=" + networkAvailable);
+
+ return isEmergencySmsOverImsSupportedIfLteLimitedOrInService
+ && mImsStateTracker.isMmTelFeatureAvailable()
+ && networkAvailable;
+ }
+
+ /**
+ * Checks if sending emergency SMS messages over IMS is supported when in LTE/limited LTE
+ * (Emergency only) service mode from the carrier configuration.
+ */
+ private boolean isEmergencySmsOverImsSupportedIfLteLimitedOrInService() {
+ if (mEmergencySmsOverImsSupportedByConfig == null) {
+ CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
+
+ if (ccm == null) {
+ loge("CarrierConfigManager is null");
+ return false;
+ }
+
+ PersistableBundle b = ccm.getConfigForSubId(getSubId());
+
+ if (b == null) {
+ loge("PersistableBundle is null");
+ return false;
+ }
+
+ mEmergencySmsOverImsSupportedByConfig = b.getBoolean(
+ CarrierConfigManager.KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL);
+ }
+
+ return mEmergencySmsOverImsSupportedByConfig;
+ }
+
+ /**
+ * Checks if the emergency service is available in the LTE service mode.
+ */
+ private boolean isLteEmergencyAvailableInService() {
+ if (mServiceState == null) {
+ return false;
+ }
+
+ final NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ if (regInfo != null
+ && regInfo.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_LTE
+ && regInfo.isRegistered()) {
+ return isEmergencyServiceSupported(regInfo) && isEmergencyServiceAllowed();
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the emergency service is available in the limited LTE service(Emergency only) mode.
+ */
+ private boolean isLteEmergencyAvailableInLimitedService() {
+ if (mServiceState == null) {
+ return false;
+ }
+
+ final NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (regInfo != null
+ && regInfo.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_LTE
+ && regInfo.isEmergencyEnabled()) {
+ return isEmergencyServiceSupported(regInfo) && isEmergencyServiceAllowed();
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the network is available for the IMS emergency SMS.
+ */
+ private boolean isNetworkAvailableForImsEmergencySms() {
+ return isLteEmergencyAvailableInService()
+ || isLteEmergencyAvailableInLimitedService();
+ }
+
+ /**
+ * Checks if the emergency service is supported by the network.
+ *
+ * This checks if "Emergency bearer services indicator (EMC-BS)" field (bits) set to
+ * the "Emergency bearer services in S1 mode supported".
+ *
+ * @return {@code true} if the emergency service is supported by the network,
+ * {@code false} otherwise.
+ */
+ private boolean isEmergencyServiceSupported(@NonNull NetworkRegistrationInfo regInfo) {
+ final DataSpecificRegistrationInfo dsRegInfo = regInfo.getDataSpecificInfo();
+ if (dsRegInfo != null) {
+ final VopsSupportInfo vopsSupportInfo = dsRegInfo.getVopsSupportInfo();
+ return vopsSupportInfo != null
+ && vopsSupportInfo.isEmergencyServiceSupported();
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the emergency service is allowed (not barred) by the network.
+ *
+ * This checks if SystemInformationBlockType2 includes the ac-BarringInfo and
+ * with the ac-BarringForEmergency set to FALSE or
+ * if the SystemInformationBlockType2 does not include the ac-BarringInfo.
+ *
+ * @return {@code true} if the emergency service is allowed by the network,
+ * {@code false} otherwise.
+ */
+ private boolean isEmergencyServiceAllowed() {
+ if (mBarringInfo == null) {
+ return true;
+ }
+ final BarringInfo.BarringServiceInfo bsi =
+ mBarringInfo.getBarringServiceInfo(BarringInfo.BARRING_SERVICE_TYPE_EMERGENCY);
+ return !bsi.isBarred();
+ }
+}
diff --git a/src/com/android/services/telephony/domainselection/ImsStateTracker.java b/src/com/android/services/telephony/domainselection/ImsStateTracker.java
new file mode 100644
index 0000000..fc3f811
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/ImsStateTracker.java
@@ -0,0 +1,878 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
+import android.telephony.BarringInfo;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.ImsStateCallback;
+import android.telephony.ims.ImsStateCallback.DisconnectedReason;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.Keep;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A class for tracking the IMS related information like IMS registration state, MMTEL capabilities.
+ * And, it also tracks the {@link ServiceState} and {@link BarringInfo} to identify the current
+ * network state to which the device is attached.
+ */
+@Keep
+public class ImsStateTracker {
+ /**
+ * A listener used to be notified of the {@link ServiceState} change.
+ */
+ public interface ServiceStateListener {
+ /**
+ * Called when the {@link ServiceState} is updated.
+ */
+ void onServiceStateUpdated(ServiceState serviceState);
+ }
+
+ /**
+ * A listener used to be notified of the {@link BarringInfo} change.
+ */
+ public interface BarringInfoListener {
+ /**
+ * Called when the {@link BarringInfo} is updated.
+ */
+ void onBarringInfoUpdated(BarringInfo barringInfo);
+ }
+
+ /**
+ * A listener used to be notified of the change for MMTEL connection state, IMS registration
+ * state, and MMTEL capabilities.
+ */
+ public interface ImsStateListener {
+ /**
+ * Called when MMTEL feature connection state is changed.
+ */
+ void onImsMmTelFeatureAvailableChanged();
+
+ /**
+ * Called when IMS registration state is changed.
+ */
+ void onImsRegistrationStateChanged();
+
+ /**
+ * Called when MMTEL capability is changed - IMS is registered
+ * and the service is currently available over IMS.
+ */
+ void onImsMmTelCapabilitiesChanged();
+ }
+
+ private static final String TAG = ImsStateTracker.class.getSimpleName();
+ /**
+ * When MMTEL feature connection is unavailable temporarily,
+ * the IMS state will be set to unavailable after waiting for this time.
+ */
+ @VisibleForTesting
+ protected static final long MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS = 1000; // 1 seconds
+
+ // Persistent Logging
+ private final LocalLog mEventLog = new LocalLog(30);
+ private final Context mContext;
+ private final int mSlotId;
+ private final Handler mHandler;
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ /** For tracking the ServiceState and its related listeners. */
+ private ServiceState mServiceState;
+ private final Set<ServiceStateListener> mServiceStateListeners = new ArraySet<>(2);
+
+ /** For tracking the BarringInfo and its related listeners. */
+ private BarringInfo mBarringInfo;
+ private final Set<BarringInfoListener> mBarringInfoListeners = new ArraySet<>(2);
+
+ /** For tracking IMS states and callbacks. */
+ private final Set<ImsStateListener> mImsStateListeners = new ArraySet<>(5);
+ private ImsMmTelManager mMmTelManager;
+ private ImsStateCallback mImsStateCallback;
+ private RegistrationManager.RegistrationCallback mImsRegistrationCallback;
+ private ImsMmTelManager.CapabilityCallback mMmTelCapabilityCallback;
+ /** The availability of MmTelFeature. */
+ private Boolean mMmTelFeatureAvailable;
+ /** The IMS registration state and the network type that performed IMS registration. */
+ private Boolean mImsRegistered;
+ private @RadioAccessNetworkType int mImsAccessNetworkType = AccessNetworkType.UNKNOWN;
+ private Boolean mImsRegisteredOverCrossSim;
+ /** The MMTEL capabilities - Voice, Video, SMS, and Ut. */
+ private MmTelCapabilities mMmTelCapabilities;
+ private final Runnable mMmTelFeatureUnavailableRunnable = new Runnable() {
+ @Override
+ public void run() {
+ setImsStateAsUnavailable();
+ notifyImsMmTelFeatureAvailableChanged();
+ }
+ };
+
+ public ImsStateTracker(@NonNull Context context, int slotId, @NonNull Looper looper) {
+ mContext = context;
+ mSlotId = slotId;
+ mHandler = new Handler(looper);
+ }
+
+ /**
+ * Destroys this tracker.
+ */
+ public void destroy() {
+ stopListeningForImsState();
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
+ /**
+ * Returns the slot index for this tracker.
+ */
+ public int getSlotId() {
+ return mSlotId;
+ }
+
+ /**
+ * Returns the current subscription index for this tracker.
+ */
+ public int getSubId() {
+ return mSubId;
+ }
+
+ /**
+ * Returns the Handler instance of this tracker.
+ */
+ @VisibleForTesting
+ public @NonNull Handler getHandler() {
+ return mHandler;
+ }
+
+ /**
+ * Starts monitoring the IMS states with the specified subscription.
+ * This method will be called whenever the subscription index for this tracker is changed.
+ * If the subscription index for this tracker is same as previously set, it will be ignored.
+ *
+ * @param subId The subscription index to be started.
+ */
+ public void start(int subId) {
+ if (mSubId == subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ setImsStateAsUnavailable();
+ return;
+ } else if (mImsStateCallback != null) {
+ // If start() is called with the same subscription index and the ImsStateCallback
+ // was already registered, we don't need to unregister and register this callback
+ // again. So, this request should be ignored if the subscription index is same.
+ logd("start: ignored for same subscription(" + mSubId + ")");
+ return;
+ }
+ } else {
+ logi("start: subscription changed from " + mSubId + " to " + subId);
+ mSubId = subId;
+ }
+
+ stopListeningForImsState();
+ startListeningForImsState();
+ }
+
+ /**
+ * Updates the service state of the network to which the device is currently attached.
+ * This method should be run on the same thread as the Handler.
+ *
+ * @param serviceState The {@link ServiceState} to be updated.
+ */
+ public void updateServiceState(ServiceState serviceState) {
+ mServiceState = serviceState;
+
+ for (ServiceStateListener listener : mServiceStateListeners) {
+ listener.onServiceStateUpdated(serviceState);
+ }
+ }
+
+ /**
+ * Adds a listener to be notified of the {@link ServiceState} change.
+ * The newly added listener is notified if the current {@link ServiceState} is present.
+ *
+ * @param listener The listener to be added.
+ */
+ public void addServiceStateListener(@NonNull ServiceStateListener listener) {
+ mServiceStateListeners.add(listener);
+
+ final ServiceState serviceState = mServiceState;
+ if (serviceState != null) {
+ mHandler.post(() -> notifyServiceStateUpdated(listener, serviceState));
+ }
+ }
+
+ /**
+ * Removes a listener to be notified of the {@link ServiceState} change.
+ *
+ * @param listener The listener to be removed.
+ */
+ public void removeServiceStateListener(@NonNull ServiceStateListener listener) {
+ mServiceStateListeners.remove(listener);
+ }
+
+ /**
+ * Notifies the specified listener of a change to {@link ServiceState}.
+ *
+ * @param listener The listener to be notified.
+ * @param serviceState The {@link ServiceState} to be reported.
+ */
+ private void notifyServiceStateUpdated(ServiceStateListener listener,
+ ServiceState serviceState) {
+ if (!mServiceStateListeners.contains(listener)) {
+ return;
+ }
+ listener.onServiceStateUpdated(serviceState);
+ }
+
+ /**
+ * Updates the barring information received from the network to which the device is currently
+ * attached.
+ * This method should be run on the same thread as the Handler.
+ *
+ * @param barringInfo The {@link BarringInfo} to be updated.
+ */
+ public void updateBarringInfo(BarringInfo barringInfo) {
+ mBarringInfo = barringInfo;
+
+ for (BarringInfoListener listener : mBarringInfoListeners) {
+ listener.onBarringInfoUpdated(barringInfo);
+ }
+ }
+
+ /**
+ * Adds a listener to be notified of the {@link BarringInfo} change.
+ * The newly added listener is notified if the current {@link BarringInfo} is present.
+ *
+ * @param listener The listener to be added.
+ */
+ public void addBarringInfoListener(@NonNull BarringInfoListener listener) {
+ mBarringInfoListeners.add(listener);
+
+ final BarringInfo barringInfo = mBarringInfo;
+ if (barringInfo != null) {
+ mHandler.post(() -> notifyBarringInfoUpdated(listener, barringInfo));
+ }
+ }
+
+ /**
+ * Removes a listener to be notified of the {@link BarringInfo} change.
+ *
+ * @param listener The listener to be removed.
+ */
+ public void removeBarringInfoListener(@NonNull BarringInfoListener listener) {
+ mBarringInfoListeners.remove(listener);
+ }
+
+ /**
+ * Notifies the specified listener of a change to {@link BarringInfo}.
+ *
+ * @param listener The listener to be notified.
+ * @param barringInfo The {@link BarringInfo} to be reported.
+ */
+ private void notifyBarringInfoUpdated(BarringInfoListener listener, BarringInfo barringInfo) {
+ if (!mBarringInfoListeners.contains(listener)) {
+ return;
+ }
+ listener.onBarringInfoUpdated(barringInfo);
+ }
+
+ /**
+ * Adds a listener to be notified of the IMS state change.
+ * If each state was already received from the IMS service, the newly added listener
+ * is notified once.
+ *
+ * @param listener The listener to be added.
+ */
+ public void addImsStateListener(@NonNull ImsStateListener listener) {
+ mImsStateListeners.add(listener);
+ mHandler.post(() -> notifyImsStateChangeIfValid(listener));
+ }
+
+ /**
+ * Removes a listener to be notified of the IMS state change.
+ *
+ * @param listener The listener to be removed.
+ */
+ public void removeImsStateListener(@NonNull ImsStateListener listener) {
+ mImsStateListeners.remove(listener);
+ }
+
+ /**
+ * Returns {@code true} if all IMS states are ready, {@code false} otherwise.
+ */
+ @VisibleForTesting
+ public boolean isImsStateReady() {
+ return mMmTelFeatureAvailable != null
+ && mImsRegistered != null
+ && mMmTelCapabilities != null;
+ }
+
+ /**
+ * Returns {@code true} if MMTEL feature connection is available, {@code false} otherwise.
+ */
+ public boolean isMmTelFeatureAvailable() {
+ return mMmTelFeatureAvailable != null && mMmTelFeatureAvailable;
+ }
+
+ /**
+ * Returns {@code true} if IMS is registered, {@code false} otherwise.
+ */
+ public boolean isImsRegistered() {
+ return mImsRegistered != null && mImsRegistered;
+ }
+
+ /**
+ * Returns {@code true} if IMS is registered over Wi-Fi (IWLAN), {@code false} otherwise.
+ */
+ public boolean isImsRegisteredOverWlan() {
+ return mImsAccessNetworkType == AccessNetworkType.IWLAN;
+ }
+
+ /**
+ * Returns {@code true} if IMS is registered over the mobile data of another subscription.
+ */
+ public boolean isImsRegisteredOverCrossSim() {
+ return mImsRegisteredOverCrossSim != null && mImsRegisteredOverCrossSim;
+ }
+
+ /**
+ * Returns {@code true} if IMS voice call is capable, {@code false} otherwise.
+ */
+ public boolean isImsVoiceCapable() {
+ return mMmTelCapabilities != null
+ && mMmTelCapabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+ }
+
+ /**
+ * Returns {@code true} if IMS video call is capable, {@code false} otherwise.
+ */
+ public boolean isImsVideoCapable() {
+ return mMmTelCapabilities != null
+ && mMmTelCapabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+ }
+
+ /**
+ * Returns {@code true} if IMS SMS is capable, {@code false} otherwise.
+ */
+ public boolean isImsSmsCapable() {
+ return mMmTelCapabilities != null
+ && mMmTelCapabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_SMS);
+ }
+
+ /**
+ * Returns {@code true} if IMS UT is capable, {@code false} otherwise.
+ */
+ public boolean isImsUtCapable() {
+ return mMmTelCapabilities != null
+ && mMmTelCapabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_UT);
+ }
+
+ /**
+ * Returns the access network type to which IMS is registered.
+ */
+ public @RadioAccessNetworkType int getImsAccessNetworkType() {
+ return mImsAccessNetworkType;
+ }
+
+ /**
+ * Sets the IMS states to the initial values.
+ */
+ private void initImsState() {
+ mMmTelFeatureAvailable = null;
+ mImsRegistered = null;
+ mImsAccessNetworkType = AccessNetworkType.UNKNOWN;
+ mImsRegisteredOverCrossSim = null;
+ mMmTelCapabilities = null;
+ }
+
+ /**
+ * Sets the IMS states to unavailable to notify the readiness of the IMS state
+ * when the subscription is not valid.
+ */
+ private void setImsStateAsUnavailable() {
+ logd("setImsStateAsUnavailable");
+ setMmTelFeatureAvailable(false);
+ setImsRegistered(false);
+ setImsAccessNetworkType(AccessNetworkType.UNKNOWN);
+ setImsRegisteredOverCrossSim(false);
+ setMmTelCapabilities(new MmTelCapabilities());
+ }
+
+ private void setMmTelFeatureAvailable(boolean available) {
+ if (!Objects.equals(mMmTelFeatureAvailable, Boolean.valueOf(available))) {
+ logi("setMmTelFeatureAvailable: " + mMmTelFeatureAvailable + " >> " + available);
+ mMmTelFeatureAvailable = Boolean.valueOf(available);
+ }
+ }
+
+ private void setImsRegistered(boolean registered) {
+ if (!Objects.equals(mImsRegistered, Boolean.valueOf(registered))) {
+ logi("setImsRegistered: " + mImsRegistered + " >> " + registered);
+ mImsRegistered = Boolean.valueOf(registered);
+ }
+ }
+
+ private void setImsAccessNetworkType(int accessNetworkType) {
+ if (mImsAccessNetworkType != accessNetworkType) {
+ logi("setImsAccessNetworkType: " + accessNetworkTypeToString(mImsAccessNetworkType)
+ + " >> " + accessNetworkTypeToString(accessNetworkType));
+ mImsAccessNetworkType = accessNetworkType;
+ }
+ }
+
+ private void setMmTelCapabilities(@NonNull MmTelCapabilities capabilities) {
+ if (!Objects.equals(mMmTelCapabilities, capabilities)) {
+ logi("MMTEL capabilities: " + mMmTelCapabilities + " >> " + capabilities);
+ mMmTelCapabilities = capabilities;
+ }
+ }
+
+ private void setImsRegisteredOverCrossSim(boolean crossSim) {
+ if (!Objects.equals(mImsRegisteredOverCrossSim, Boolean.valueOf(crossSim))) {
+ logi("setImsRegisteredOverCrossSim: " + mImsRegisteredOverCrossSim + " >> " + crossSim);
+ mImsRegisteredOverCrossSim = Boolean.valueOf(crossSim);
+ }
+ }
+
+ /**
+ * Notifies the specified listener of the current IMS state if it's valid.
+ *
+ * @param listener The {@link ImsStateListener} to be notified.
+ */
+ private void notifyImsStateChangeIfValid(@NonNull ImsStateListener listener) {
+ if (!mImsStateListeners.contains(listener)) {
+ return;
+ }
+
+ if (mMmTelFeatureAvailable != null) {
+ listener.onImsMmTelFeatureAvailableChanged();
+ }
+
+ if (mImsRegistered != null) {
+ listener.onImsRegistrationStateChanged();
+ }
+
+ if (mMmTelCapabilities != null) {
+ listener.onImsMmTelCapabilitiesChanged();
+ }
+ }
+
+ /**
+ * Notifies the application that MMTEL feature connection state is changed.
+ */
+ private void notifyImsMmTelFeatureAvailableChanged() {
+ for (ImsStateListener l : mImsStateListeners) {
+ l.onImsMmTelFeatureAvailableChanged();
+ }
+ }
+
+ /**
+ * Notifies the application that IMS registration state is changed.
+ */
+ private void notifyImsRegistrationStateChanged() {
+ logi("ImsState: " + imsStateToString());
+ for (ImsStateListener l : mImsStateListeners) {
+ l.onImsRegistrationStateChanged();
+ }
+ }
+
+ /**
+ * Notifies the application that MMTEL capabilities is changed.
+ */
+ private void notifyImsMmTelCapabilitiesChanged() {
+ logi("ImsState: " + imsStateToString());
+ for (ImsStateListener l : mImsStateListeners) {
+ l.onImsMmTelCapabilitiesChanged();
+ }
+ }
+
+ /**
+ * Called when MMTEL feature connection state is available.
+ */
+ private void onMmTelFeatureAvailable() {
+ logd("onMmTelFeatureAvailable");
+ mHandler.removeCallbacks(mMmTelFeatureUnavailableRunnable);
+ setMmTelFeatureAvailable(true);
+ registerImsRegistrationCallback();
+ registerMmTelCapabilityCallback();
+ notifyImsMmTelFeatureAvailableChanged();
+ }
+
+ /**
+ * Called when MMTEL feature connection state is unavailable.
+ */
+ private void onMmTelFeatureUnavailable(@DisconnectedReason int reason) {
+ logd("onMmTelFeatureUnavailable: reason=" + disconnectedCauseToString(reason));
+
+ if (reason == ImsStateCallback.REASON_UNKNOWN_TEMPORARY_ERROR
+ || reason == ImsStateCallback.REASON_IMS_SERVICE_NOT_READY) {
+ // Wait for onAvailable for some times and
+ // if it's not available, the IMS state will be set to unavailable.
+ initImsState();
+ setMmTelFeatureAvailable(false);
+ mHandler.postDelayed(mMmTelFeatureUnavailableRunnable,
+ MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS);
+ } else if (reason == ImsStateCallback.REASON_UNKNOWN_PERMANENT_ERROR
+ || reason == ImsStateCallback.REASON_NO_IMS_SERVICE_CONFIGURED) {
+ // Permanently blocked for this subscription.
+ setImsStateAsUnavailable();
+ notifyImsMmTelFeatureAvailableChanged();
+ } else if (reason == ImsStateCallback.REASON_IMS_SERVICE_DISCONNECTED) {
+ // Wait for onAvailable for some times and
+ // if it's not available, the IMS state will be set to unavailable.
+ initImsState();
+ setMmTelFeatureAvailable(false);
+ unregisterImsRegistrationCallback();
+ unregisterMmTelCapabilityCallback();
+ mHandler.postDelayed(mMmTelFeatureUnavailableRunnable,
+ MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS);
+ } else if (reason == ImsStateCallback.REASON_SUBSCRIPTION_INACTIVE) {
+ // The {@link TelephonyDomainSelectionService} will call ImsStateTracker#start
+ // when the subscription changes to register new callbacks.
+ setImsStateAsUnavailable();
+ unregisterImsRegistrationCallback();
+ unregisterMmTelCapabilityCallback();
+ // ImsStateCallback has already been removed after calling onUnavailable.
+ mImsStateCallback = null;
+ notifyImsMmTelFeatureAvailableChanged();
+ } else {
+ logw("onMmTelFeatureUnavailable: unexpected reason=" + reason);
+ }
+ }
+
+ /**
+ * Called when IMS is registered to the IMS network.
+ */
+ private void onImsRegistered(@NonNull ImsRegistrationAttributes attributes) {
+ logd("onImsRegistered: " + attributes);
+
+ setImsRegistered(true);
+ setImsAccessNetworkType(
+ imsRegTechToAccessNetworkType(attributes.getRegistrationTechnology()));
+ setImsRegisteredOverCrossSim(attributes.getRegistrationTechnology()
+ == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM);
+ notifyImsRegistrationStateChanged();
+ }
+
+ /**
+ * Called when IMS is unregistered from the IMS network.
+ */
+ private void onImsUnregistered(@NonNull ImsReasonInfo info) {
+ logd("onImsUnregistered: " + info);
+ setImsRegistered(false);
+ setImsAccessNetworkType(AccessNetworkType.UNKNOWN);
+ setImsRegisteredOverCrossSim(false);
+ setMmTelCapabilities(new MmTelCapabilities());
+ notifyImsRegistrationStateChanged();
+ }
+
+ /**
+ * Called when MMTEL capability is changed - IMS is registered
+ * and the service is currently available over IMS.
+ */
+ private void onMmTelCapabilitiesChanged(@NonNull MmTelCapabilities capabilities) {
+ logd("onMmTelCapabilitiesChanged: " + capabilities);
+ setMmTelCapabilities(capabilities);
+ notifyImsMmTelCapabilitiesChanged();
+ }
+
+ /**
+ * Starts listening to monitor the IMS states -
+ * connection state, IMS registration state, and MMTEL capabilities.
+ */
+ private void startListeningForImsState() {
+ if (!SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ setImsStateAsUnavailable();
+ return;
+ }
+
+ ImsManager imsMngr = mContext.getSystemService(ImsManager.class);
+ mMmTelManager = imsMngr.getImsMmTelManager(getSubId());
+ initImsState();
+ registerImsStateCallback();
+ }
+
+ /**
+ * Stops listening to monitor the IMS states -
+ * connection state, IMS registration state, and MMTEL capabilities.
+ */
+ private void stopListeningForImsState() {
+ mHandler.removeCallbacks(mMmTelFeatureUnavailableRunnable);
+
+ if (mMmTelManager != null) {
+ unregisterMmTelCapabilityCallback();
+ unregisterImsRegistrationCallback();
+ unregisterImsStateCallback();
+ mMmTelManager = null;
+ }
+ }
+
+ private void registerImsStateCallback() {
+ if (mImsStateCallback != null) {
+ loge("ImsStateCallback is already registered for sub-" + getSubId());
+ return;
+ }
+ /**
+ * Listens to the IMS connection state change.
+ */
+ mImsStateCallback = new ImsStateCallback() {
+ @Override
+ public void onUnavailable(@DisconnectedReason int reason) {
+ onMmTelFeatureUnavailable(reason);
+ }
+
+ @Override
+ public void onAvailable() {
+ onMmTelFeatureAvailable();
+ }
+
+ @Override
+ public void onError() {
+ // This case will not be happened because this domain selection service
+ // is running on the Telephony service.
+ }
+ };
+
+ try {
+ mMmTelManager.registerImsStateCallback(mHandler::post, mImsStateCallback);
+ } catch (ImsException e) {
+ loge("Exception when registering ImsStateCallback: " + e);
+ mImsStateCallback = null;
+ }
+ }
+
+ private void unregisterImsStateCallback() {
+ if (mImsStateCallback != null) {
+ try {
+ mMmTelManager.unregisterImsStateCallback(mImsStateCallback);
+ } catch (Exception ignored) {
+ // Ignore the runtime exception while unregistering callback.
+ logd("Exception when unregistering ImsStateCallback: " + ignored);
+ }
+ mImsStateCallback = null;
+ }
+ }
+
+ private void registerImsRegistrationCallback() {
+ if (mImsRegistrationCallback != null) {
+ logd("RegistrationCallback is already registered for sub-" + getSubId());
+ return;
+ }
+ /**
+ * Listens to the IMS registration state change.
+ */
+ mImsRegistrationCallback = new RegistrationManager.RegistrationCallback() {
+ @Override
+ public void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
+ onImsRegistered(attributes);
+ }
+
+ @Override
+ public void onUnregistered(@NonNull ImsReasonInfo info) {
+ onImsUnregistered(info);
+ }
+ };
+
+ try {
+ mMmTelManager.registerImsRegistrationCallback(mHandler::post, mImsRegistrationCallback);
+ } catch (ImsException e) {
+ loge("Exception when registering RegistrationCallback: " + e);
+ mImsRegistrationCallback = null;
+ }
+ }
+
+ private void unregisterImsRegistrationCallback() {
+ if (mImsRegistrationCallback != null) {
+ try {
+ mMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
+ } catch (Exception ignored) {
+ // Ignore the runtime exception while unregistering callback.
+ logd("Exception when unregistering RegistrationCallback: " + ignored);
+ }
+ mImsRegistrationCallback = null;
+ }
+ }
+
+ private void registerMmTelCapabilityCallback() {
+ if (mMmTelCapabilityCallback != null) {
+ logd("CapabilityCallback is already registered for sub-" + getSubId());
+ return;
+ }
+ /**
+ * Listens to the MmTel feature capabilities change.
+ */
+ mMmTelCapabilityCallback = new ImsMmTelManager.CapabilityCallback() {
+ @Override
+ public void onCapabilitiesStatusChanged(@NonNull MmTelCapabilities capabilities) {
+ onMmTelCapabilitiesChanged(capabilities);
+ }
+ };
+
+ try {
+ mMmTelManager.registerMmTelCapabilityCallback(mHandler::post, mMmTelCapabilityCallback);
+ } catch (ImsException e) {
+ loge("Exception when registering CapabilityCallback: " + e);
+ mMmTelCapabilityCallback = null;
+ }
+ }
+
+ private void unregisterMmTelCapabilityCallback() {
+ if (mMmTelCapabilityCallback != null) {
+ try {
+ mMmTelManager.unregisterMmTelCapabilityCallback(mMmTelCapabilityCallback);
+ } catch (Exception ignored) {
+ // Ignore the runtime exception while unregistering callback.
+ logd("Exception when unregistering CapabilityCallback: " + ignored);
+ }
+ mMmTelCapabilityCallback = null;
+ }
+ }
+
+ /** Returns a string representation of IMS states. */
+ public String imsStateToString() {
+ StringBuilder sb = new StringBuilder("{ ");
+ sb.append("MMTEL: featureAvailable=").append(booleanToString(mMmTelFeatureAvailable));
+ sb.append(", registered=").append(booleanToString(mImsRegistered));
+ sb.append(", accessNetworkType=").append(accessNetworkTypeToString(mImsAccessNetworkType));
+ sb.append(", capabilities=").append(mmTelCapabilitiesToString(mMmTelCapabilities));
+ sb.append(" }");
+ return sb.toString();
+ }
+
+ protected static String accessNetworkTypeToString(
+ @RadioAccessNetworkType int accessNetworkType) {
+ switch (accessNetworkType) {
+ case AccessNetworkType.UNKNOWN: return "UNKNOWN";
+ case AccessNetworkType.GERAN: return "GERAN";
+ case AccessNetworkType.UTRAN: return "UTRAN";
+ case AccessNetworkType.EUTRAN: return "EUTRAN";
+ case AccessNetworkType.CDMA2000: return "CDMA2000";
+ case AccessNetworkType.IWLAN: return "IWLAN";
+ case AccessNetworkType.NGRAN: return "NGRAN";
+ default: return Integer.toString(accessNetworkType);
+ }
+ }
+
+ /** Converts the IMS registration technology to the access network type. */
+ private static @RadioAccessNetworkType int imsRegTechToAccessNetworkType(
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+ switch (imsRegTech) {
+ case ImsRegistrationImplBase.REGISTRATION_TECH_LTE:
+ return AccessNetworkType.EUTRAN;
+ case ImsRegistrationImplBase.REGISTRATION_TECH_NR:
+ return AccessNetworkType.NGRAN;
+ case ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN:
+ case ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM:
+ return AccessNetworkType.IWLAN;
+ default:
+ return AccessNetworkType.UNKNOWN;
+ }
+ }
+
+ private static String booleanToString(Boolean b) {
+ return b == null ? "null" : b.toString();
+ }
+
+ private static String mmTelCapabilitiesToString(MmTelCapabilities c) {
+ if (c == null) {
+ return "null";
+ }
+ StringBuilder sb = new StringBuilder("[");
+ sb.append("voice=").append(c.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VOICE));
+ sb.append(", video=").append(c.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VIDEO));
+ sb.append(", ut=").append(c.isCapable(MmTelCapabilities.CAPABILITY_TYPE_UT));
+ sb.append(", sms=").append(c.isCapable(MmTelCapabilities.CAPABILITY_TYPE_SMS));
+ sb.append("]");
+ return sb.toString();
+ }
+
+ private static String disconnectedCauseToString(@DisconnectedReason int reason) {
+ switch (reason) {
+ case ImsStateCallback.REASON_UNKNOWN_TEMPORARY_ERROR:
+ return "UNKNOWN_TEMPORARY_ERROR";
+ case ImsStateCallback.REASON_UNKNOWN_PERMANENT_ERROR:
+ return "UNKNOWN_PERMANENT_ERROR";
+ case ImsStateCallback.REASON_IMS_SERVICE_DISCONNECTED:
+ return "IMS_SERVICE_DISCONNECTED";
+ case ImsStateCallback.REASON_NO_IMS_SERVICE_CONFIGURED:
+ return "NO_IMS_SERVICE_CONFIGURED";
+ case ImsStateCallback.REASON_SUBSCRIPTION_INACTIVE:
+ return "SUBSCRIPTION_INACTIVE";
+ case ImsStateCallback.REASON_IMS_SERVICE_NOT_READY:
+ return "IMS_SERVICE_NOT_READY";
+ default:
+ return Integer.toString(reason);
+ }
+ }
+
+ /**
+ * Dumps this instance into a readable format for dumpsys usage.
+ */
+ public void dump(@NonNull PrintWriter pw) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println("ImsStateTracker:");
+ ipw.increaseIndent();
+ ipw.println("SlotId: " + getSlotId());
+ ipw.println("SubId: " + getSubId());
+ ipw.println("ServiceState: " + mServiceState);
+ ipw.println("BarringInfo: " + mBarringInfo);
+ ipw.println("ImsState: " + imsStateToString());
+ ipw.println("Event Log:");
+ ipw.increaseIndent();
+ mEventLog.dump(ipw);
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
+ }
+
+ private void logd(String s) {
+ Log.d(TAG, "[" + getSlotId() + "|" + getSubId() + "] " + s);
+ }
+
+ private void logi(String s) {
+ Log.i(TAG, "[" + getSlotId() + "|" + getSubId() + "] " + s);
+ mEventLog.log("[" + getSlotId() + "|" + getSubId() + "] " + s);
+ }
+
+ private void loge(String s) {
+ Log.e(TAG, "[" + getSlotId() + "|" + getSubId() + "] " + s);
+ mEventLog.log("[" + getSlotId() + "|" + getSubId() + "] " + s);
+ }
+
+ private void logw(String s) {
+ Log.w(TAG, "[" + getSlotId() + "|" + getSubId() + "] " + s);
+ mEventLog.log("[" + getSlotId() + "|" + getSubId() + "] " + s);
+ }
+}
diff --git a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
new file mode 100644
index 0000000..f176d90
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telecom.TelecomManager;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.CarrierConfigManager;
+import android.telephony.DisconnectCause;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.ims.ImsReasonInfo;
+
+import com.android.internal.telephony.domainselection.NormalCallDomainSelectionConnection;
+
+/**
+ * Implements domain selector for outgoing non-emergency calls.
+ */
+public class NormalCallDomainSelector extends DomainSelectorBase implements
+ ImsStateTracker.ImsStateListener, ImsStateTracker.ServiceStateListener {
+
+ private static final String LOG_TAG = "NCDS";
+
+ private boolean mStopDomainSelection = true;
+ private ServiceState mServiceState;
+ private boolean mImsRegStateReceived;
+ private boolean mMmTelCapabilitiesReceived;
+ private boolean mReselectDomain;
+
+ public NormalCallDomainSelector(Context context, int slotId, int subId, @NonNull Looper looper,
+ @NonNull ImsStateTracker imsStateTracker,
+ @NonNull DestroyListener destroyListener) {
+ super(context, slotId, subId, looper, imsStateTracker, destroyListener, LOG_TAG);
+
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ logd("Subscribing to state callbacks. Subid:" + subId);
+ mImsStateTracker.addServiceStateListener(this);
+ mImsStateTracker.addImsStateListener(this);
+ } else {
+ loge("Invalid Subscription. Subid:" + subId);
+ }
+ }
+
+ @Override
+ public void selectDomain(SelectionAttributes attributes, TransportSelectorCallback callback) {
+ mSelectionAttributes = attributes;
+ mTransportSelectorCallback = callback;
+ mStopDomainSelection = false;
+
+ if (callback == null) {
+ loge("Invalid params: TransportSelectorCallback is null");
+ return;
+ }
+
+ if (attributes == null) {
+ loge("Invalid params: SelectionAttributes are null");
+ notifySelectionTerminated(DisconnectCause.OUTGOING_FAILURE);
+ return;
+ }
+
+ int subId = attributes.getSubId();
+ boolean validSubscriptionId = SubscriptionManager.isValidSubscriptionId(subId);
+ if (attributes.getSelectorType() != SELECTOR_TYPE_CALLING || attributes.isEmergency()
+ || !validSubscriptionId) {
+ loge("Domain Selection stopped. SelectorType:" + attributes.getSelectorType()
+ + ", isEmergency:" + attributes.isEmergency()
+ + ", ValidSubscriptionId:" + validSubscriptionId);
+
+ notifySelectionTerminated(DisconnectCause.OUTGOING_FAILURE);
+ return;
+ }
+
+ if (subId == getSubId()) {
+ logd("NormalCallDomainSelection triggered. Sub-id:" + subId);
+ post(() -> selectDomain());
+ } else {
+ loge("Subscription-ids doesn't match. This instance is associated with sub-id:"
+ + getSubId() + ", requested sub-id:" + subId);
+ // TODO: Throw anamoly here. This condition should never occur.
+ }
+ }
+
+ @Override
+ public void reselectDomain(SelectionAttributes attributes) {
+ logd("reselectDomain called");
+ mReselectDomain = true;
+ selectDomain(attributes, mTransportSelectorCallback);
+ }
+
+ @Override
+ public synchronized void finishSelection() {
+ logd("finishSelection");
+ mStopDomainSelection = true;
+ mImsStateTracker.removeServiceStateListener(this);
+ mImsStateTracker.removeImsStateListener(this);
+ mSelectionAttributes = null;
+ mTransportSelectorCallback = null;
+ }
+
+ /**
+ * Cancel an ongoing selection operation. It is up to the DomainSelectionService
+ * to clean up all ongoing operations with the framework.
+ */
+ @Override
+ public void cancelSelection() {
+ logd("cancelSelection");
+ mStopDomainSelection = true;
+ mReselectDomain = false;
+ if (mTransportSelectorCallback != null) {
+ mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.OUTGOING_CANCELED);
+ }
+ finishSelection();
+ }
+
+ @Override
+ public void onImsRegistrationStateChanged() {
+ logd("onImsRegistrationStateChanged. IsImsRegistered: "
+ + mImsStateTracker.isImsRegistered());
+ mImsRegStateReceived = true;
+ selectDomain();
+ }
+
+ @Override
+ public void onImsMmTelCapabilitiesChanged() {
+ logd("onImsMmTelCapabilitiesChanged. ImsVoiceCap: " + mImsStateTracker.isImsVoiceCapable()
+ + " ImsVideoCap: " + mImsStateTracker.isImsVideoCapable());
+ mMmTelCapabilitiesReceived = true;
+ selectDomain();
+ }
+
+ @Override
+ public void onImsMmTelFeatureAvailableChanged() {
+ logd("onImsMmTelFeatureAvailableChanged");
+ selectDomain();
+ }
+
+ @Override
+ public void onServiceStateUpdated(ServiceState serviceState) {
+ logd("onServiceStateUpdated");
+ mServiceState = serviceState;
+ selectDomain();
+ }
+
+ private void notifyPsSelected() {
+ logd("notifyPsSelected");
+ mStopDomainSelection = true;
+ if (mImsStateTracker.isImsRegisteredOverWlan()) {
+ logd("WLAN selected");
+ mTransportSelectorCallback.onWlanSelected(false);
+ } else {
+ if (mWwanSelectorCallback == null) {
+ mTransportSelectorCallback.onWwanSelected((callback) -> {
+ mWwanSelectorCallback = callback;
+ notifyPsSelectedInternal();
+ });
+ } else {
+ notifyPsSelectedInternal();
+ }
+ }
+ }
+
+ private void notifyPsSelectedInternal() {
+ if (mWwanSelectorCallback != null) {
+ logd("notifyPsSelected - onWwanSelected");
+ mWwanSelectorCallback.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS, false);
+ } else {
+ loge("wwanSelectorCallback is null");
+ mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.OUTGOING_FAILURE);
+ }
+ }
+
+ private void notifyCsSelected() {
+ logd("notifyCsSelected");
+ mStopDomainSelection = true;
+ if (mWwanSelectorCallback == null) {
+ mTransportSelectorCallback.onWwanSelected((callback) -> {
+ mWwanSelectorCallback = callback;
+ notifyCsSelectedInternal();
+ });
+ } else {
+ notifyCsSelectedInternal();
+ }
+ }
+
+ private void notifyCsSelectedInternal() {
+ if (mWwanSelectorCallback != null) {
+ logd("wwanSelectorCallback -> onDomainSelected(DOMAIN_CS)");
+ mWwanSelectorCallback.onDomainSelected(NetworkRegistrationInfo.DOMAIN_CS, false);
+ } else {
+ loge("wwanSelectorCallback is null");
+ mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.OUTGOING_FAILURE);
+ }
+ }
+
+ private void notifySelectionTerminated(@DisconnectCauses int cause) {
+ mStopDomainSelection = true;
+ if (mTransportSelectorCallback != null) {
+ mTransportSelectorCallback.onSelectionTerminated(cause);
+ finishSelection();
+ }
+ }
+
+ private boolean isOutOfService() {
+ return (mServiceState.getState() == ServiceState.STATE_OUT_OF_SERVICE
+ || mServiceState.getState() == ServiceState.STATE_POWER_OFF
+ || mServiceState.getState() == ServiceState.STATE_EMERGENCY_ONLY);
+ }
+
+ private boolean isWpsCallSupportedByIms() {
+ CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
+
+ PersistableBundle config = null;
+ if (configManager != null) {
+ config = configManager.getConfigForSubId(mSelectionAttributes.getSubId());
+ }
+
+ return (config != null)
+ ? config.getBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL) : false;
+ }
+
+ private void handleWpsCall() {
+ if (isWpsCallSupportedByIms()) {
+ logd("WPS call placed over PS");
+ notifyPsSelected();
+ } else {
+ if (isOutOfService()) {
+ loge("Cannot place call in current ServiceState: " + mServiceState.getState());
+ notifySelectionTerminated(DisconnectCause.OUT_OF_SERVICE);
+ } else {
+ logd("WPS call placed over CS");
+ notifyCsSelected();
+ }
+ }
+ }
+
+ private boolean isTtySupportedByIms() {
+ CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
+
+ PersistableBundle config = null;
+ if (configManager != null) {
+ config = configManager.getConfigForSubId(mSelectionAttributes.getSubId());
+ }
+
+ return (config != null)
+ && config.getBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL);
+ }
+
+ private boolean isTtyModeEnabled() {
+ TelecomManager tm = mContext.getSystemService(TelecomManager.class);
+ if (tm == null) {
+ loge("isTtyModeEnabled: telecom not available");
+ return false;
+ }
+ return tm.getCurrentTtyMode() != TelecomManager.TTY_MODE_OFF;
+ }
+
+ private synchronized void selectDomain() {
+ if (mStopDomainSelection || mSelectionAttributes == null
+ || mTransportSelectorCallback == null) {
+ logd("Domain Selection is stopped.");
+ return;
+ }
+
+ if (mServiceState == null) {
+ logd("Waiting for ServiceState callback.");
+ return;
+ }
+
+ // Check if this is a re-dial scenario
+ // IMS -> CS
+ ImsReasonInfo imsReasonInfo = mSelectionAttributes.getPsDisconnectCause();
+ if (mReselectDomain && imsReasonInfo != null) {
+ logd("PsDisconnectCause:" + imsReasonInfo.mCode);
+ mReselectDomain = false;
+ if (imsReasonInfo.mCode == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED) {
+ if (isOutOfService()) {
+ loge("Cannot place call in current ServiceState: " + mServiceState.getState());
+ notifySelectionTerminated(DisconnectCause.OUT_OF_SERVICE);
+ } else {
+ logd("Redialing over CS");
+ notifyCsSelected();
+ }
+ return;
+ } else {
+ logd("Redialing cancelled.");
+ // Not a valid redial
+ notifySelectionTerminated(DisconnectCause.NOT_VALID);
+ return;
+ }
+ }
+
+ // CS -> IMS
+ // TODO: @PreciseDisconnectCauses doesn't contain cause code related to redial on IMS.
+ if (mReselectDomain /*mSelectionAttributes.getCsDisconnectCause() == IMS_REDIAL_CODE*/) {
+ logd("Redialing cancelled.");
+ // Not a valid redial
+ notifySelectionTerminated(DisconnectCause.NOT_VALID);
+ return;
+ }
+
+ if (!mImsStateTracker.isMmTelFeatureAvailable()) {
+ logd("MmTelFeatureAvailable unavailable");
+ if (isOutOfService()) {
+ loge("Cannot place call in current ServiceState: " + mServiceState.getState());
+ notifySelectionTerminated(DisconnectCause.OUT_OF_SERVICE);
+ } else {
+ notifyCsSelected();
+ }
+ return;
+ }
+
+ if (!mImsRegStateReceived || !mMmTelCapabilitiesReceived) {
+ loge("Waiting for ImsState and MmTelCapabilities callbacks");
+ return;
+ }
+
+ // Check IMS registration state.
+ if (!mImsStateTracker.isImsRegistered()) {
+ logd("IMS is NOT registered");
+ if (isOutOfService()) {
+ loge("Cannot place call in current ServiceState: " + mServiceState.getState());
+ notifySelectionTerminated(DisconnectCause.OUT_OF_SERVICE);
+ } else {
+ notifyCsSelected();
+ }
+ return;
+ }
+
+ // Check TTY
+ if (isTtyModeEnabled() && !isTtySupportedByIms()) {
+ if (isOutOfService()) {
+ loge("Cannot place call in current ServiceState: " + mServiceState.getState());
+ notifySelectionTerminated(DisconnectCause.OUT_OF_SERVICE);
+ } else {
+ notifyCsSelected();
+ }
+ return;
+ }
+
+ // Handle video call.
+ if (mSelectionAttributes.isVideoCall()) {
+ logd("It's a video call");
+ if (mImsStateTracker.isImsVideoCapable()) {
+ logd("IMS is video capable");
+ notifyPsSelected();
+ } else {
+ logd("IMS is not video capable. Ending the call");
+ notifySelectionTerminated(DisconnectCause.OUTGOING_FAILURE);
+ }
+ return;
+ }
+
+ // Handle voice call.
+ if (mImsStateTracker.isImsVoiceCapable()) {
+ logd("IMS is voice capable");
+ // TODO(b/266175810) Remove this dependency.
+ if (NormalCallDomainSelectionConnection
+ .isWpsCall(mSelectionAttributes.getNumber())) {
+ handleWpsCall();
+ } else {
+ notifyPsSelected();
+ }
+ } else {
+ logd("IMS is not voice capable");
+ // Voice call CS fallback
+ if (isOutOfService()) {
+ loge("Cannot place call in current ServiceState: " + mServiceState.getState());
+ notifySelectionTerminated(DisconnectCause.OUT_OF_SERVICE);
+ } else {
+ notifyCsSelected();
+ }
+ }
+ }
+}
diff --git a/src/com/android/services/telephony/domainselection/SmsDomainSelector.java b/src/com/android/services/telephony/domainselection/SmsDomainSelector.java
new file mode 100644
index 0000000..95b04e2
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/SmsDomainSelector.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.DisconnectCause;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.TransportSelectorCallback;
+
+/**
+ * Implements SMS domain selector for sending MO SMS.
+ */
+public class SmsDomainSelector extends DomainSelectorBase implements
+ ImsStateTracker.ImsStateListener {
+ protected static final int EVENT_SELECT_DOMAIN = 101;
+
+ protected boolean mDestroyed = false;
+ private boolean mDomainSelectionRequested = false;
+
+ public SmsDomainSelector(Context context, int slotId, int subId, @NonNull Looper looper,
+ @NonNull ImsStateTracker imsStateTracker, @NonNull DestroyListener listener) {
+ this(context, slotId, subId, looper, imsStateTracker, listener, "DomainSelector-SMS");
+ }
+
+ protected SmsDomainSelector(Context context, int slotId, int subId, @NonNull Looper looper,
+ @NonNull ImsStateTracker imsStateTracker, @NonNull DestroyListener listener,
+ String logTag) {
+ super(context, slotId, subId, looper, imsStateTracker, listener, logTag);
+ }
+
+ @Override
+ public void destroy() {
+ if (mDestroyed) {
+ return;
+ }
+ logd("destroy");
+ mDestroyed = true;
+ mImsStateTracker.removeImsStateListener(this);
+ super.destroy();
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case EVENT_SELECT_DOMAIN:
+ selectDomain();
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
+ }
+ }
+
+ @Override
+ public void cancelSelection() {
+ logi("cancelSelection");
+ finishSelection();
+ }
+
+ @Override
+ public void reselectDomain(@NonNull SelectionAttributes attr) {
+ if (isDomainSelectionRequested()) {
+ // The domain selection is already requested,
+ // so we don't need to request it again before completing the previous task.
+ logi("Domain selection is already running.");
+ return;
+ }
+
+ logi("reselectDomain");
+ mSelectionAttributes = attr;
+ setDomainSelectionRequested(true);
+ obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget();
+ }
+
+ @Override
+ public void finishSelection() {
+ logi("finishSelection");
+ setDomainSelectionRequested(false);
+ mSelectionAttributes = null;
+ mTransportSelectorCallback = null;
+ mWwanSelectorCallback = null;
+ destroy();
+ }
+
+ @Override
+ public void selectDomain(SelectionAttributes attr, TransportSelectorCallback callback) {
+ if (isDomainSelectionRequested()) {
+ // The domain selection is already requested,
+ // so we don't need to request it again before completing the previous task.
+ logi("Domain selection is already running.");
+ return;
+ }
+ mSelectionAttributes = attr;
+ mTransportSelectorCallback = callback;
+ setDomainSelectionRequested(true);
+ mImsStateTracker.addImsStateListener(this);
+ obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget();
+ }
+
+ @Override
+ public void onImsMmTelFeatureAvailableChanged() {
+ sendMessageForDomainSelection();
+ }
+
+ @Override
+ public void onImsRegistrationStateChanged() {
+ sendMessageForDomainSelection();
+ }
+
+ @Override
+ public void onImsMmTelCapabilitiesChanged() {
+ sendMessageForDomainSelection();
+ }
+
+ protected boolean isSmsOverImsAvailable() {
+ return mImsStateTracker.isImsSmsCapable()
+ && mImsStateTracker.isImsRegistered()
+ && mImsStateTracker.isMmTelFeatureAvailable();
+ }
+
+ protected void selectDomain() {
+ if (!isDomainSelectionRequested()) {
+ logi("Domain selection is not requested!");
+ return;
+ }
+
+ logi("selectDomain: " + mImsStateTracker.imsStateToString());
+
+ if (isSmsOverImsAvailable()) {
+ if (mImsStateTracker.isImsRegisteredOverWlan()) {
+ notifyWlanSelected(false);
+ return;
+ }
+ notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_PS, false);
+ } else {
+ notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_CS, false);
+ }
+ }
+
+ protected void sendMessageForDomainSelection() {
+ // If the event is already queued to this handler,
+ // it will be removed first to avoid the duplicate operation.
+ removeMessages(EVENT_SELECT_DOMAIN);
+ // Since the IMS state may have already been posted,
+ // proceed with the domain selection after processing all pending messages.
+ obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget();
+ }
+
+ protected boolean isDomainSelectionRequested() {
+ return mDomainSelectionRequested;
+ }
+
+ protected void setDomainSelectionRequested(boolean requested) {
+ if (mDomainSelectionRequested != requested) {
+ logd("DomainSelectionRequested: " + mDomainSelectionRequested + " >> " + requested);
+ mDomainSelectionRequested = requested;
+ }
+ }
+
+ protected void notifyWlanSelected(boolean useEmergencyPdn) {
+ logi("DomainSelected: WLAN, E-PDN=" + useEmergencyPdn);
+ mTransportSelectorCallback.onWlanSelected(useEmergencyPdn);
+ setDomainSelectionRequested(false);
+ }
+
+ protected void notifyWwanSelected(@NetworkRegistrationInfo.Domain int domain,
+ boolean useEmergencyPdn) {
+ if (mWwanSelectorCallback == null) {
+ mTransportSelectorCallback.onWwanSelected((callback) -> {
+ mWwanSelectorCallback = callback;
+ notifyWwanSelectedInternal(domain, useEmergencyPdn);
+ });
+ } else {
+ notifyWwanSelectedInternal(domain, useEmergencyPdn);
+ }
+
+ setDomainSelectionRequested(false);
+ }
+
+ protected void notifyWwanSelectedInternal(@NetworkRegistrationInfo.Domain int domain,
+ boolean useEmergencyPdn) {
+ logi("DomainSelected: WWAN/" + DomainSelectionService.getDomainName(domain)
+ + ", E-PDN=" + useEmergencyPdn);
+
+ if (mWwanSelectorCallback != null) {
+ mWwanSelectorCallback.onDomainSelected(domain, useEmergencyPdn);
+ } else {
+ mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.LOCAL);
+ }
+ }
+}
diff --git a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
new file mode 100644
index 0000000..3a8fc86
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.telephony.BarringInfo;
+import android.telephony.DisconnectCause;
+import android.telephony.DomainSelectionService;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyManager;
+import android.telephony.TransportSelectorCallback;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Implements the telephony domain selection for various telephony features.
+ */
+public class TelephonyDomainSelectionService extends DomainSelectionService {
+ /**
+ * Testing interface for injecting mock ImsStateTracker.
+ */
+ @VisibleForTesting
+ public interface ImsStateTrackerFactory {
+ /**
+ * @return The {@link ImsStateTracker} created for the specified slot.
+ */
+ ImsStateTracker create(Context context, int slotId, @NonNull Looper looper);
+ }
+
+ /**
+ * Testing interface for injecting mock DomainSelector.
+ */
+ @VisibleForTesting
+ public interface DomainSelectorFactory {
+ /**
+ * @return The {@link DomainSelectorBase} created using the specified arguments.
+ */
+ DomainSelectorBase create(Context context, int slotId, int subId,
+ @SelectorType int selectorType, boolean isEmergency, @NonNull Looper looper,
+ @NonNull ImsStateTracker imsStateTracker,
+ @NonNull DomainSelectorBase.DestroyListener listener,
+ @NonNull CrossSimRedialingController crossSimRedialingController);
+ }
+
+ private static final class DefaultDomainSelectorFactory implements DomainSelectorFactory {
+ @Override
+ public DomainSelectorBase create(Context context, int slotId, int subId,
+ @SelectorType int selectorType, boolean isEmergency, @NonNull Looper looper,
+ @NonNull ImsStateTracker imsStateTracker,
+ @NonNull DomainSelectorBase.DestroyListener listener,
+ @NonNull CrossSimRedialingController crossSimRedialingController) {
+ DomainSelectorBase selector = null;
+
+ logi("create-DomainSelector: slotId=" + slotId + ", subId=" + subId
+ + ", selectorType=" + selectorTypeToString(selectorType)
+ + ", emergency=" + isEmergency);
+
+ switch (selectorType) {
+ case SELECTOR_TYPE_CALLING:
+ if (isEmergency) {
+ selector = new EmergencyCallDomainSelector(context, slotId, subId, looper,
+ imsStateTracker, listener, crossSimRedialingController);
+ } else {
+ selector = new NormalCallDomainSelector(context, slotId, subId, looper,
+ imsStateTracker, listener);
+ }
+ break;
+ case SELECTOR_TYPE_SMS:
+ if (isEmergency) {
+ selector = new EmergencySmsDomainSelector(context, slotId, subId, looper,
+ imsStateTracker, listener);
+ } else {
+ selector = new SmsDomainSelector(context, slotId, subId, looper,
+ imsStateTracker, listener);
+ }
+ break;
+ default:
+ // Not reachable.
+ break;
+ }
+
+ return selector;
+ }
+ };
+
+ /**
+ * A container class to manage the domain selector per a slot and selector type.
+ * If the domain selector is not null and reusable, the same domain selector will be used
+ * for the specific slot.
+ */
+ private static final class DomainSelectorContainer {
+ private final int mSlotId;
+ private final @SelectorType int mSelectorType;
+ private final boolean mIsEmergency;
+ private final @NonNull DomainSelectorBase mSelector;
+
+ DomainSelectorContainer(int slotId, @SelectorType int selectorType, boolean isEmergency,
+ @NonNull DomainSelectorBase selector) {
+ mSlotId = slotId;
+ mSelectorType = selectorType;
+ mIsEmergency = isEmergency;
+ mSelector = selector;
+ }
+
+ public int getSlotId() {
+ return mSlotId;
+ }
+
+ public @SelectorType int getSelectorType() {
+ return mSelectorType;
+ }
+
+ public DomainSelectorBase getDomainSelector() {
+ return mSelector;
+ }
+
+ public boolean isEmergency() {
+ return mIsEmergency;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("{ ")
+ .append("slotId=").append(mSlotId)
+ .append(", selectorType=").append(selectorTypeToString(mSelectorType))
+ .append(", isEmergency=").append(mIsEmergency)
+ .append(", selector=").append(mSelector)
+ .append(" }").toString();
+ }
+ }
+
+ private final DomainSelectorBase.DestroyListener mDestroyListener =
+ new DomainSelectorBase.DestroyListener() {
+ @Override
+ public void onDomainSelectorDestroyed(DomainSelectorBase selector) {
+ logd("DomainSelector destroyed: " + selector);
+ removeDomainSelector(selector);
+ }
+ };
+
+ /**
+ * A class to listen for the subscription change for starting {@link ImsStateTracker}
+ * to monitor the IMS states.
+ */
+ private final OnSubscriptionsChangedListener mSubscriptionsChangedListener =
+ new OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ handleSubscriptionsChanged();
+ }
+ };
+
+ private static final String TAG = TelephonyDomainSelectionService.class.getSimpleName();
+
+ // Persistent Logging
+ private static final LocalLog sEventLog = new LocalLog(20);
+ private final Context mContext;
+ // Map of slotId -> ImsStateTracker
+ private final SparseArray<ImsStateTracker> mImsStateTrackers = new SparseArray<>(2);
+ private final List<DomainSelectorContainer> mDomainSelectorContainers = new ArrayList<>();
+ private final ImsStateTrackerFactory mImsStateTrackerFactory;
+ private final DomainSelectorFactory mDomainSelectorFactory;
+ private Handler mServiceHandler;
+ private CrossSimRedialingController mCrossSimRedialingController;
+
+ public TelephonyDomainSelectionService(Context context) {
+ this(context, ImsStateTracker::new, new DefaultDomainSelectorFactory());
+ }
+
+ @VisibleForTesting
+ public TelephonyDomainSelectionService(Context context,
+ @NonNull ImsStateTrackerFactory imsStateTrackerFactory,
+ @NonNull DomainSelectorFactory domainSelectorFactory) {
+ mContext = context;
+ mImsStateTrackerFactory = imsStateTrackerFactory;
+ mDomainSelectorFactory = domainSelectorFactory;
+
+ // Create a worker thread for this domain selection service.
+ getExecutor();
+
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ int activeModemCount = (tm != null) ? tm.getActiveModemCount() : 1;
+ for (int i = 0; i < activeModemCount; ++i) {
+ mImsStateTrackers.put(i, mImsStateTrackerFactory.create(mContext, i, getLooper()));
+ }
+
+ SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
+ if (sm != null) {
+ sm.addOnSubscriptionsChangedListener(getExecutor(), mSubscriptionsChangedListener);
+ } else {
+ loge("Adding OnSubscriptionChangedListener failed");
+ }
+
+ mCrossSimRedialingController = new CrossSimRedialingController(context, getLooper());
+
+ logi("TelephonyDomainSelectionService created");
+ }
+
+ @Override
+ public void onDestroy() {
+ logd("onDestroy");
+
+ List<DomainSelectorContainer> domainSelectorContainers;
+
+ synchronized (mDomainSelectorContainers) {
+ domainSelectorContainers = new ArrayList<>(mDomainSelectorContainers);
+ mDomainSelectorContainers.clear();
+ }
+
+ for (DomainSelectorContainer dsc : domainSelectorContainers) {
+ DomainSelectorBase selector = dsc.getDomainSelector();
+ if (selector != null) {
+ selector.destroy();
+ }
+ }
+ domainSelectorContainers.clear();
+
+ synchronized (mImsStateTrackers) {
+ for (int i = 0; i < mImsStateTrackers.size(); ++i) {
+ ImsStateTracker ist = mImsStateTrackers.get(i);
+ if (ist != null) {
+ ist.destroy();
+ }
+ }
+ mImsStateTrackers.clear();
+ }
+
+ SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
+ if (sm != null) {
+ sm.removeOnSubscriptionsChangedListener(mSubscriptionsChangedListener);
+ }
+
+ if (mCrossSimRedialingController != null) {
+ mCrossSimRedialingController.destroy();
+ mCrossSimRedialingController = null;
+ }
+
+ if (mServiceHandler != null) {
+ mServiceHandler.getLooper().quit();
+ mServiceHandler = null;
+ }
+ }
+
+ /**
+ * Selects a domain for the given attributes and callback.
+ *
+ * @param attr required to determine the domain.
+ * @param callback the callback instance being registered.
+ */
+ @Override
+ public void onDomainSelection(@NonNull SelectionAttributes attr,
+ @NonNull TransportSelectorCallback callback) {
+ final int slotId = attr.getSlotId();
+ final int subId = attr.getSubId();
+ final int selectorType = attr.getSelectorType();
+ final boolean isEmergency = attr.isEmergency();
+ ImsStateTracker ist = getImsStateTracker(slotId);
+ DomainSelectorBase selector = mDomainSelectorFactory.create(mContext, slotId, subId,
+ selectorType, isEmergency, getLooper(), ist, mDestroyListener,
+ mCrossSimRedialingController);
+
+ if (selector != null) {
+ // Ensures that ImsStateTracker is started before selecting the domain if not started
+ // for the specified subscription index.
+ ist.start(subId);
+ addDomainSelector(slotId, selectorType, isEmergency, selector);
+ } else {
+ loge("No proper domain selector: " + selectorTypeToString(selectorType));
+ callback.onSelectionTerminated(DisconnectCause.ERROR_UNSPECIFIED);
+ return;
+ }
+
+ // Notify the caller that the domain selector is created.
+ callback.onCreated(selector);
+
+ // Performs the domain selection.
+ selector.selectDomain(attr, callback);
+ }
+
+ /**
+ * Called when the {@link ServiceState} needs to be updated for the specified slot and
+ * subcription index.
+ *
+ * @param slotId for which the service state changed.
+ * @param subId The current subscription for a specified slot.
+ * @param serviceState The {@link ServiceState} to be updated.
+ */
+ @Override
+ public void onServiceStateUpdated(int slotId, int subId, @NonNull ServiceState serviceState) {
+ ImsStateTracker ist = getImsStateTracker(slotId);
+ if (ist != null) {
+ ist.updateServiceState(serviceState);
+ }
+ }
+
+ /**
+ * Called when the {@link BarringInfo} needs to be updated for the specified slot and
+ * subscription index.
+ *
+ * @param slotId The slot the BarringInfo is updated for.
+ * @param subId The current subscription for a specified slot.
+ * @param barringInfo The {@link BarringInfo} to be updated.
+ */
+ @Override
+ public void onBarringInfoUpdated(int slotId, int subId, @NonNull BarringInfo barringInfo) {
+ ImsStateTracker ist = getImsStateTracker(slotId);
+ if (ist != null) {
+ ist.updateBarringInfo(barringInfo);
+ }
+ }
+
+ /**
+ * Returns an Executor used to execute methods called remotely by the framework.
+ */
+ @SuppressLint("OnNameExpected")
+ @Override
+ public @NonNull Executor getExecutor() {
+ if (mServiceHandler == null) {
+ HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ mServiceHandler = new Handler(handlerThread.getLooper());
+ }
+
+ return mServiceHandler::post;
+ }
+
+ /**
+ * Returns a Looper instance.
+ */
+ @VisibleForTesting
+ public Looper getLooper() {
+ getExecutor();
+ return mServiceHandler.getLooper();
+ }
+
+ /**
+ * Handles the subscriptions change.
+ */
+ private void handleSubscriptionsChanged() {
+ SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
+ List<SubscriptionInfo> subsInfoList =
+ (sm != null) ? sm.getActiveSubscriptionInfoList() : null;
+
+ if (subsInfoList == null || subsInfoList.isEmpty()) {
+ logd("handleSubscriptionsChanged: No valid SubscriptionInfo");
+ return;
+ }
+
+ for (int i = 0; i < subsInfoList.size(); ++i) {
+ SubscriptionInfo subsInfo = subsInfoList.get(i);
+ int slotId = subsInfo.getSimSlotIndex();
+
+ if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ logd("handleSubscriptionsChanged: slotId=" + slotId);
+ ImsStateTracker ist = getImsStateTracker(slotId);
+ ist.start(subsInfo.getSubscriptionId());
+ }
+ }
+ }
+
+ /**
+ * Adds the {@link DomainSelectorBase} to the list of domain selector container.
+ */
+ private void addDomainSelector(int slotId, @SelectorType int selectorType,
+ boolean isEmergency, @NonNull DomainSelectorBase selector) {
+ synchronized (mDomainSelectorContainers) {
+ // If the domain selector already exists, remove the previous one first.
+ for (int i = 0; i < mDomainSelectorContainers.size(); ++i) {
+ DomainSelectorContainer dsc = mDomainSelectorContainers.get(i);
+
+ if (dsc.getSlotId() == slotId
+ && dsc.getSelectorType() == selectorType
+ && dsc.isEmergency() == isEmergency) {
+ mDomainSelectorContainers.remove(i);
+ DomainSelectorBase oldSelector = dsc.getDomainSelector();
+ if (oldSelector != null) {
+ logw("DomainSelector destroyed by new domain selection request: " + dsc);
+ oldSelector.destroy();
+ }
+ break;
+ }
+ }
+
+ DomainSelectorContainer dsc =
+ new DomainSelectorContainer(slotId, selectorType, isEmergency, selector);
+ mDomainSelectorContainers.add(dsc);
+
+ logi("DomainSelector added: " + dsc + ", count=" + mDomainSelectorContainers.size());
+ }
+ }
+
+ /**
+ * Removes the domain selector container that matches with the specified
+ * {@link DomainSelectorBase}.
+ */
+ private void removeDomainSelector(@NonNull DomainSelectorBase selector) {
+ synchronized (mDomainSelectorContainers) {
+ for (int i = 0; i < mDomainSelectorContainers.size(); ++i) {
+ DomainSelectorContainer dsc = mDomainSelectorContainers.get(i);
+
+ if (dsc.getDomainSelector() == selector) {
+ mDomainSelectorContainers.remove(i);
+ logi("DomainSelector removed: " + dsc
+ + ", count=" + mDomainSelectorContainers.size());
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the {@link ImsStateTracker} instance for the specified slot.
+ * If the {@link ImsStateTracker} does not exist for the slot, it creates new instance
+ * and returns.
+ */
+ private ImsStateTracker getImsStateTracker(int slotId) {
+ synchronized (mImsStateTrackers) {
+ ImsStateTracker ist = mImsStateTrackers.get(slotId);
+
+ if (ist == null) {
+ ist = mImsStateTrackerFactory.create(mContext, slotId, getLooper());
+ mImsStateTrackers.put(slotId, ist);
+ }
+
+ return ist;
+ }
+ }
+
+ private static String selectorTypeToString(@SelectorType int selectorType) {
+ switch (selectorType) {
+ case SELECTOR_TYPE_CALLING: return "CALLING";
+ case SELECTOR_TYPE_SMS: return "SMS";
+ case SELECTOR_TYPE_UT: return "UT";
+ default: return Integer.toString(selectorType);
+ }
+ }
+
+ private static void logd(String s) {
+ Log.d(TAG, s);
+ }
+
+ private static void logi(String s) {
+ Log.i(TAG, s);
+ sEventLog.log(s);
+ }
+
+ private static void loge(String s) {
+ Log.e(TAG, s);
+ sEventLog.log(s);
+ }
+
+ private static void logw(String s) {
+ Log.w(TAG, s);
+ sEventLog.log(s);
+ }
+
+ /**
+ * Dumps this instance into a readable format for dumpsys usage.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println("TelephonyDomainSelectionService:");
+ ipw.increaseIndent();
+ ipw.println("ImsStateTrackers:");
+ synchronized (mImsStateTrackers) {
+ for (int i = 0; i < mImsStateTrackers.size(); ++i) {
+ ImsStateTracker ist = mImsStateTrackers.valueAt(i);
+ ist.dump(ipw);
+ }
+ }
+ ipw.decreaseIndent();
+ ipw.increaseIndent();
+ synchronized (mDomainSelectorContainers) {
+ for (int i = 0; i < mDomainSelectorContainers.size(); ++i) {
+ DomainSelectorContainer dsc = mDomainSelectorContainers.get(i);
+ ipw.println("DomainSelector: " + dsc.toString());
+ ipw.increaseIndent();
+ DomainSelectorBase selector = dsc.getDomainSelector();
+ if (selector != null) {
+ selector.dump(ipw);
+ }
+ ipw.decreaseIndent();
+ }
+ }
+ ipw.decreaseIndent();
+ ipw.increaseIndent();
+ ipw.println("Event Log:");
+ ipw.increaseIndent();
+ sEventLog.dump(ipw);
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
+ ipw.println("________________________________");
+ }
+}
diff --git a/src/com/android/services/telephony/rcs/MessageTransportWrapper.java b/src/com/android/services/telephony/rcs/MessageTransportWrapper.java
index 45f7d95..d992883 100644
--- a/src/com/android/services/telephony/rcs/MessageTransportWrapper.java
+++ b/src/com/android/services/telephony/rcs/MessageTransportWrapper.java
@@ -481,6 +481,16 @@
}
}
+ /**
+ * This is a listener to handle SipDialog state of delegate
+ * @param listener {@link SipDialogsStateListener}
+ * @param isNeedNotify It indicates whether the current dialogs state should be notified.
+ */
+ public void setSipDialogsListener(SipDialogsStateListener listener,
+ boolean isNeedNotify) {
+ mSipSessionTracker.setSipDialogsListener(listener, isNeedNotify);
+ }
+
private void logi(String log) {
Log.i(SipTransportController.LOG_TAG, TAG + "[" + mSubId + "] " + log);
mLocalLog.log("[I] " + log);
diff --git a/src/com/android/services/telephony/rcs/RcsFeatureController.java b/src/com/android/services/telephony/rcs/RcsFeatureController.java
index 0e1cb4b..a2f435c 100644
--- a/src/com/android/services/telephony/rcs/RcsFeatureController.java
+++ b/src/com/android/services/telephony/rcs/RcsFeatureController.java
@@ -17,10 +17,13 @@
package com.android.services.telephony.rcs;
import android.annotation.AnyThread;
+import android.annotation.NonNull;
import android.content.Context;
import android.net.Uri;
+import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -165,7 +168,7 @@
private ImsRegistrationCallbackHelper.ImsRegistrationUpdate mRcsRegistrationUpdate = new
ImsRegistrationCallbackHelper.ImsRegistrationUpdate() {
@Override
- public void handleImsRegistered(int imsRadioTech) {
+ public void handleImsRegistered(@NonNull ImsRegistrationAttributes attributes) {
}
@Override
@@ -173,7 +176,8 @@
}
@Override
- public void handleImsUnregistered(ImsReasonInfo imsReasonInfo) {
+ public void handleImsUnregistered(ImsReasonInfo imsReasonInfo,
+ int suggestedAction, int imsRadioTech) {
}
@Override
@@ -402,6 +406,17 @@
callback.accept(mImsRcsRegistrationHelper.getImsRegistrationState());
}
+ /**
+ * @return the subscription ID that is currently associated with this RCS feature.
+ */
+ public int getAssociatedSubId() {
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager != null) {
+ return manager.getSubId();
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
private void updateCapabilities() {
RcsFeatureManager manager = getFeatureManager();
if (manager != null) {
diff --git a/src/com/android/services/telephony/rcs/SipDelegateController.java b/src/com/android/services/telephony/rcs/SipDelegateController.java
index 860a6d9..f7fc359 100644
--- a/src/com/android/services/telephony/rcs/SipDelegateController.java
+++ b/src/com/android/services/telephony/rcs/SipDelegateController.java
@@ -391,6 +391,16 @@
}
/**
+ * This is a listener to handle SipDialog state of delegate
+ * @param listener {@link SipDialogsStateListener}
+ * @param isNeedNotify It indicates whether the current dialogs state should be notified.
+ */
+ public void setSipDialogsListener(SipDialogsStateListener listener,
+ boolean isNeedNotify) {
+ mMessageTransportWrapper.setSipDialogsListener(listener, isNeedNotify);
+ }
+
+ /**
* Write the current state of this controller in String format using the PrintWriter provided
* for dumpsys.
*/
diff --git a/src/com/android/services/telephony/rcs/SipDialogsStateListener.java b/src/com/android/services/telephony/rcs/SipDialogsStateListener.java
new file mode 100644
index 0000000..bbd3bd1
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/SipDialogsStateListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.rcs;
+
+import android.telephony.ims.SipDialogState;
+
+import java.util.List;
+
+/**
+ * The listener interface for notifying the state of sip dialogs to SipDialogsStateHandle.
+ * refer to {@link SipTransportController}
+ */
+public interface SipDialogsStateListener {
+ /**
+ * To map dialog state information of available delegates
+ * @param key This is an ID of SipSessionTracker for distinguishing whose delegate is
+ * during dialog mapping.
+ * @param dialogStates This is dialog state information of delegate
+ */
+ void reMappingSipDelegateState(String key, List<SipDialogState> dialogStates);
+
+ /**
+ * Notify SipDialogState information with
+ * {@link com.android.internal.telephony.ISipDialogStateCallback}
+ */
+ void notifySipDialogState();
+}
diff --git a/src/com/android/services/telephony/rcs/SipSessionTracker.java b/src/com/android/services/telephony/rcs/SipSessionTracker.java
index 68e3065..2d48a9f 100644
--- a/src/com/android/services/telephony/rcs/SipSessionTracker.java
+++ b/src/com/android/services/telephony/rcs/SipSessionTracker.java
@@ -16,6 +16,7 @@
package com.android.services.telephony.rcs;
+import android.telephony.ims.SipDialogState;
import android.telephony.ims.SipMessage;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -33,6 +34,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.UUID;
import java.util.stream.Collectors;
/**
@@ -69,10 +71,13 @@
private final RcsStats mRcsStats;
int mSubId;
+ private SipDialogsStateListener mSipDialogsListener;
+ private String mDelegateKey;
public SipSessionTracker(int subId, RcsStats rcsStats) {
mSubId = subId;
mRcsStats = rcsStats;
+ mDelegateKey = String.valueOf(UUID.randomUUID());
}
/**
@@ -152,6 +157,7 @@
logi("Dialog closed: " + d);
}
mTrackedDialogs.removeAll(dialogsToCleanup);
+ notifySipDialogState();
}
/**
@@ -213,6 +219,7 @@
}
mTrackedDialogs.clear();
mPendingAck.clear();
+ notifySipDialogState();
}
/**
@@ -308,6 +315,7 @@
d.close();
logi("Dialog closed: " + d);
}
+ notifySipDialogState();
};
}
@@ -365,16 +373,47 @@
if (statusCode >= 300) {
mRcsStats.onSipTransportSessionClosed(mSubId, m.getCallIdParameter(), statusCode, true);
d.close();
+ notifySipDialogState();
return;
}
if (toTag == null) logw("updateSipDialogState: No to tag for message: " + m);
if (statusCode >= 200) {
mRcsStats.confirmedSipTransportSession(m.getCallIdParameter(), statusCode);
d.confirm(toTag);
+ notifySipDialogState();
return;
}
// 1XX responses still require updates to dialogs.
d.earlyResponse(toTag);
+ notifySipDialogState();
+ }
+
+ /**
+ * This is a listener to handle SipDialog state of delegate
+ * @param listener {@link SipDialogsStateListener}
+ * @param isNeedNotify It indicates whether the current dialogs state should be notified.
+ */
+ public void setSipDialogsListener(SipDialogsStateListener listener,
+ boolean isNeedNotify) {
+ mSipDialogsListener = listener;
+ if (listener == null) {
+ return;
+ }
+ if (isNeedNotify) {
+ notifySipDialogState();
+ }
+ }
+
+ private void notifySipDialogState() {
+ if (mSipDialogsListener == null) {
+ return;
+ }
+ List<SipDialogState> dialogStates = new ArrayList<>();
+ for (SipDialog d : mTrackedDialogs) {
+ SipDialogState dialog = new SipDialogState.Builder(d.getState()).build();
+ dialogStates.add(dialog);
+ }
+ mSipDialogsListener.reMappingSipDelegateState(mDelegateKey, dialogStates);
}
private void logi(String log) {
diff --git a/src/com/android/services/telephony/rcs/SipTransportController.java b/src/com/android/services/telephony/rcs/SipTransportController.java
index 0aa3aa0..2f090d5 100644
--- a/src/com/android/services/telephony/rcs/SipTransportController.java
+++ b/src/com/android/services/telephony/rcs/SipTransportController.java
@@ -29,6 +29,7 @@
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsService;
import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipDialogState;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.ISipDelegate;
import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
@@ -46,6 +47,8 @@
import com.android.ims.RcsFeatureManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.ISipDialogStateCallback;
+import com.android.internal.telephony.util.RemoteCallbackListExt;
import com.android.internal.util.IndentingPrintWriter;
import com.android.phone.RcsProvisioningMonitor;
@@ -54,8 +57,11 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
@@ -238,6 +244,65 @@
}
/**
+ * This is to handle with dialogs of all available delegates that have dialogs.
+ */
+ private final class SipDialogsStateHandle implements SipDialogsStateListener {
+
+ private Executor mExecutor = Runnable::run;
+ Map<String, List<SipDialogState>> mMapDialogState = new HashMap<>();
+
+ /**
+ * This will be called using the {@link SipDialogsStateListener}
+ * @param key This is the ID of the SipSessionTracker for handling the dialogs of
+ * each created delegates.
+ * @param dialogStates This is a list of dialog states tracked in SipSessionTracker.
+ */
+ @Override
+ public void reMappingSipDelegateState(String key,
+ List<SipDialogState> dialogStates) {
+ mExecutor.execute(()->processReMappingSipDelegateState(key, dialogStates));
+ }
+
+ /**
+ * Notify SipDialogState information with
+ * {@link com.android.internal.telephony.ISipDialogStateCallback}
+ */
+ @Override
+ public void notifySipDialogState() {
+ mExecutor.execute(()->processNotifySipDialogState());
+ }
+
+ private void processReMappingSipDelegateState(String key,
+ List<SipDialogState> dialogStates) {
+ if (dialogStates.isEmpty()) {
+ mMapDialogState.remove(key);
+ } else {
+ mMapDialogState.put(key, dialogStates);
+ }
+ notifySipDialogState();
+ }
+
+ private void processNotifySipDialogState() {
+ List<SipDialogState> finalDialogStates = new ArrayList<>();
+ for (List<SipDialogState> d : mMapDialogState.values()) {
+ finalDialogStates.addAll(d);
+ }
+
+ if (mSipDialogStateCallbacks.getRegisteredCallbackCount() == 0) {
+ return;
+ }
+ mSipDialogStateCallbacks.broadcastAction((c) -> {
+ try {
+ c.onActiveSipDialogsChanged(finalDialogStates);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG,
+ "onActiveSipDialogsChanged() - Skipping callback." + e);
+ }
+ });
+ }
+ }
+
+ /**
* Allow the ability for tests to easily mock out the SipDelegateController for testing.
*/
@VisibleForTesting
@@ -265,6 +330,12 @@
private final List<SipDelegateController> mDelegatePendingCreate = new ArrayList<>();
// SipDelegateControllers that are pending to be destroyed.
private final List<DestroyRequest> mDelegatePendingDestroy = new ArrayList<>();
+ // SipDialogStateCallback that are adding to use callback.
+ private final RemoteCallbackListExt<ISipDialogStateCallback> mSipDialogStateCallbacks =
+ new RemoteCallbackListExt<>();
+ // To listen the state information if the dialog status is changed from the SipSessionTracker.
+ private final SipDialogsStateListener mSipDialogsListener = new SipDialogsStateHandle();
+
// Cache of Binders to remote IMS applications for tracking their potential death
private final TrackedAppBinders mActiveAppBinders = new TrackedAppBinders();
@@ -457,6 +528,10 @@
logi("createSipDelegateInternal: request= " + request + ", packageName= " + packageName
+ ", controller created: " + c);
addPendingCreateAndEvaluate(c);
+ // If SipDialogStateCallback is registered, listener will be set.
+ if (mSipDialogStateCallbacks.getRegisteredCallbackCount() > 0) {
+ c.setSipDialogsListener(mSipDialogsListener, false);
+ }
}
private void destroySipDelegateInternal(int subId, ISipDelegate connection, int reason) {
@@ -917,7 +992,7 @@
it.remove();
deniedTags.add(new FeatureTagState(tag,
SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
- } else if (!mFeatureTagsAllowed.contains(tag.trim().toLowerCase())) {
+ } else if (!mFeatureTagsAllowed.contains(tag.trim().toLowerCase(Locale.ROOT))) {
logi(tag + " is not allowed per config.");
it.remove();
deniedTags.add(new FeatureTagState(tag,
@@ -1032,7 +1107,7 @@
CarrierConfigManager.Ims.KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY);
if (tagConfigs != null && tagConfigs.length > 0) {
for (String tag : tagConfigs) {
- mFeatureTagsAllowed.add(tag.trim().toLowerCase());
+ mFeatureTagsAllowed.add(tag.trim().toLowerCase(Locale.ROOT));
}
}
}
@@ -1056,6 +1131,56 @@
}
}
+ /**
+ * Adds a callback that gets called when SipDialog status has changed.
+ * @param subId The subId associated with the request.
+ * @param cb A {@link android.telephony.ims.SipDialogStateCallback} that will notify the caller
+ * when Dialog status has changed.
+ */
+ public void addCallbackForSipDialogState(int subId, ISipDialogStateCallback cb) {
+ if (subId != mSubId) {
+ logw("addCallbackForSipDialogState the subId is not supported");
+ return;
+ }
+ // callback register and no delegate : register this callback / notify (empty state)
+ // callback register and delegates : register this callback / release listener / notify
+ mSipDialogStateCallbacks.register(cb);
+ if (!mDelegatePriorityQueue.isEmpty()) {
+ for (SipDelegateController dc : mDelegatePriorityQueue) {
+ dc.setSipDialogsListener(mSipDialogsListener, true);
+ }
+ } else {
+ mSipDialogsListener.notifySipDialogState();
+ }
+ }
+
+ /**
+ * Unregister previously registered callback
+ * @param subId The subId associated with the request.
+ * @param cb A {@link android.telephony.ims.SipDialogStateCallback} that will be unregistering.
+ */
+ public void removeCallbackForSipDialogState(int subId, ISipDialogStateCallback cb) {
+ if (subId != mSubId) {
+ logw("addCallbackForSipDialogState the subId is not supported");
+ return;
+ }
+ if (cb == null) {
+ throw new IllegalArgumentException("callback is null");
+ }
+
+ // remove callback register and no delegate : only unregister this callback
+ // remove callback register and delegates :
+ // unregister this callback and setListener(null)
+ mSipDialogStateCallbacks.unregister(cb);
+ if (mSipDialogStateCallbacks.getRegisteredCallbackCount() == 0) {
+ if (!mDelegatePriorityQueue.isEmpty()) {
+ for (SipDelegateController dc : mDelegatePriorityQueue) {
+ dc.setSipDialogsListener(null, false);
+ }
+ }
+ }
+ }
+
@Override
public void dump(PrintWriter printWriter) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index dfcea74..f6ba40b 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -25,6 +25,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.util.Log;
@@ -244,6 +245,22 @@
}
/**
+ * Verifies the subId supplied is the active subId for the slotId specified.
+ * If we have not processed a CARRIER_CONFIG_CHANGED indication for this subscription yet,
+ * either the subscription is not active or we have not finished setting up the feature yet.
+ * @param slotId The slotId we are verifying
+ * @param subId The subId we are verifying
+ * @return true if the subId is the active subId we are tracking for the slotId specified.
+ */
+ public boolean verifyActiveSubId(int slotId, int subId) {
+ synchronized (mLock) {
+ int currId = mSlotToAssociatedSubIds.get(slotId,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ return subId == currId;
+ }
+ }
+
+ /**
* ACTION_CARRIER_CONFIG_CHANGED was received by this service for a specific slot.
* @param slotId The slotId associated with the event.
* @param subId The subId associated with the event. May cause the subId associated with the
@@ -335,36 +352,31 @@
private boolean doesSubscriptionSupportPresence(int subId) {
if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
- CarrierConfigManager carrierConfigManager =
- mContext.getSystemService(CarrierConfigManager.class);
- if (carrierConfigManager == null) return false;
- boolean supportsUce = carrierConfigManager.getConfigForSubId(subId).getBoolean(
- CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL);
- supportsUce |= carrierConfigManager.getConfigForSubId(subId).getBoolean(
- CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL);
+ boolean supportsUce = getConfig(subId,
+ CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false /*default*/);
+ supportsUce |= getConfig(subId,
+ CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, false /*default*/);
return supportsUce;
}
private boolean doesSubscriptionSupportSingleRegistration(int subId) {
if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
- CarrierConfigManager carrierConfigManager =
- mContext.getSystemService(CarrierConfigManager.class);
- if (carrierConfigManager == null) return false;
- return carrierConfigManager.getConfigForSubId(subId).getBoolean(
- CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL);
+ return getConfig(subId, CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL,
+ false /*defaultValue*/);
}
private int getSubscriptionFromSlot(int slotId) {
- SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
- if (manager == null) {
- Log.w(LOG_TAG, "Couldn't find SubscriptionManager for slotId=" + slotId);
- return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- }
- int[] subIds = manager.getSubscriptionIds(slotId);
- if (subIds != null && subIds.length > 0) {
- return subIds[0];
- }
- return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ return SubscriptionManager.getSubscriptionId(slotId);
+ }
+
+ /**
+ * @return the boolean result corresponding to a boolean {@link CarrierConfigManager} key.
+ */
+ private boolean getConfig(int subId, String key, boolean defaultValue) {
+ CarrierConfigManager c = mContext.getSystemService(CarrierConfigManager.class);
+ if (c == null) return defaultValue;
+ PersistableBundle b = c.getConfigForSubId(subId, key);
+ return b != null ? b.getBoolean(key, defaultValue) : defaultValue;
}
/**
diff --git a/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java b/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java
index 3b1b26d..b15992e 100644
--- a/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java
+++ b/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java
@@ -577,6 +577,16 @@
}
}
+ /**
+ * This is a listener to handle SipDialog state of delegate
+ * @param listener {@link SipDialogsStateListener}
+ * @param isNeedNotify It indicates whether the current dialogs state should be notified.
+ */
+ public void setSipDialogsListener(SipDialogsStateListener listener,
+ boolean isNeedNotify) {
+ mSipSessionTracker.setSipDialogsListener(listener, isNeedNotify);
+ }
+
private void logi(String log) {
Log.i(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log);
mLocalLog.log("[I] " + log);
diff --git a/src/com/android/services/telephony/rcs/validator/OutgoingTransportStateValidator.java b/src/com/android/services/telephony/rcs/validator/OutgoingTransportStateValidator.java
index 72d22f8..d532ea9 100644
--- a/src/com/android/services/telephony/rcs/validator/OutgoingTransportStateValidator.java
+++ b/src/com/android/services/telephony/rcs/validator/OutgoingTransportStateValidator.java
@@ -29,6 +29,7 @@
import java.util.Arrays;
import java.util.Collections;
+import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -166,7 +167,7 @@
}
// Only need to validate requests that start dialogs.
boolean startsDialog = Arrays.stream(SipSessionTracker.SIP_REQUEST_DIALOG_START_METHODS)
- .anyMatch(req -> req.equals(segments[0].trim().toLowerCase()));
+ .anyMatch(req -> req.equals(segments[0].trim().toLowerCase(Locale.ROOT)));
// If part of an existing dialog, then no need to validate.
boolean needsFeatureValidation = startsDialog && !getAllowedCallIds()
.contains(m.getCallIdParameter());
diff --git a/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSipRequestValidator.java b/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSipRequestValidator.java
index 2c2632f..afb86a0 100644
--- a/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSipRequestValidator.java
+++ b/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSipRequestValidator.java
@@ -22,6 +22,7 @@
import com.android.internal.telephony.SipMessageParsingUtils;
import java.util.Arrays;
+import java.util.Locale;
/**
* Validate that any outgoing SIP request message does not contain methods that are only generated
@@ -47,7 +48,7 @@
"malformed start line: " + message.getStartLine());
}
if (Arrays.stream(IMS_SERVICE_HANDLED_REQUEST_METHODS).anyMatch(
- s -> segments[0].toLowerCase().contains(s))) {
+ s -> segments[0].toLowerCase(Locale.ROOT).contains(s))) {
return new ValidationResult(
SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE,
"restricted method: " + segments[0]);
diff --git a/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSubscribeValidator.java b/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSubscribeValidator.java
index 41074ed..de6bfe7 100644
--- a/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSubscribeValidator.java
+++ b/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSubscribeValidator.java
@@ -24,6 +24,7 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
import java.util.stream.Collectors;
/**
@@ -60,7 +61,8 @@
return ValidationResult.SUCCESS;
}
boolean isRestricted = eventHeaders.stream().map(e -> e.second)
- .anyMatch(e -> Arrays.asList(RESTRICTED_EVENTS).contains(e.trim().toLowerCase()));
+ .anyMatch(e -> Arrays.asList(RESTRICTED_EVENTS).contains(e.trim().toLowerCase(
+ Locale.ROOT)));
return isRestricted ? new ValidationResult(
SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS,
diff --git a/testapps/GbaTestApp/res/values-am/strings.xml b/testapps/GbaTestApp/res/values-am/strings.xml
index b9be9d5..b78cd6c 100644
--- a/testapps/GbaTestApp/res/values-am/strings.xml
+++ b/testapps/GbaTestApp/res/values-am/strings.xml
@@ -16,7 +16,7 @@
<string name="title_activity_test_config" msgid="7585204485536359713">"TestConfigActivity"</string>
<string name="service_package_name" msgid="7204809439090483315">"የGBA አገልግሎት የጥቅል ስም"</string>
<string name="service_release_time" msgid="532937496122890892">"ጥሪ ከተደረገ በኋላ አገልግሎት የሚለቀቅበት ጊዜ"</string>
- <string name="request_app_type" msgid="3975978141673475612">"UICC የመተግበሪያ ዓይነት"</string>
+ <string name="request_app_type" msgid="3975978141673475612">"UICC የመተግበሪያ አይነት"</string>
<string name="request_naf_url" msgid="4487793541217737042">"የአውታረ መረብ የመተግበሪያ ተግባር (NAF) ዩአርኤል"</string>
<string name="request_force_bootstrapping" msgid="206043602616214325">"በማስነሻ ተገዶ ይነሳ?"</string>
<string name="request_org" msgid="8416693445448308975">"የድርጅት ኮድ"</string>
diff --git a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/TelephonyManagerTestApp.java b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/TelephonyManagerTestApp.java
index 760c3bd..815c7e8 100644
--- a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/TelephonyManagerTestApp.java
+++ b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/TelephonyManagerTestApp.java
@@ -35,6 +35,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
/**
* Main activity.
@@ -158,7 +159,8 @@
mFilteredMethods.addAll(mMethods);
} else {
for (Method method : mMethods) {
- if (method.getName().toLowerCase().contains(text.toLowerCase())) {
+ if (method.getName().toLowerCase(Locale.ROOT)
+ .contains(text.toLowerCase(Locale.ROOT))) {
mFilteredMethods.add(method);
}
}
diff --git a/testapps/TestRcsApp/TestApp/lint-baseline.xml b/testapps/TestRcsApp/TestApp/lint-baseline.xml
new file mode 100644
index 0000000..8971388
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/lint-baseline.xml
@@ -0,0 +1,829 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#bootstrapAuthenticationRequest`"
+ errorLine1=" telephonyManager.bootstrapAuthenticationRequest(mUiccType,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+ line="130"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#build`"
+ errorLine1=" UaSecurityProtocolIdentifier spId = builder.build();"
+ errorLine2=" ~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+ line="129"
+ column="57"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setOrg`"
+ errorLine1=" builder.setOrg(mOrganization)"
+ errorLine2=" ~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+ line="122"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setProtocol`"
+ errorLine1=" .setProtocol(mProtocol)"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+ line="123"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setTlsCipherSuite`"
+ errorLine1=" .setTlsCipherSuite(Integer.parseInt(mTlsCs.getText().toString()));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+ line="124"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
+ errorLine1=" Set<String> registeredFt = registrationState.getRegisteredFeatureTags();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="151"
+ column="66"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getFeatureTag`"
+ errorLine1=' stringBuilder.append(featureTagState.getFeatureTag()).append(" ").append('
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="148"
+ column="62"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getState`"
+ errorLine1=" featureTagState.getState());"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="149"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ImsManager#getSipDelegateManager`"
+ errorLine1=" mSipDelegateManager = imsManager.getSipDelegateManager(mDefaultSmsSubId);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="220"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
+ errorLine1=" boolean capable = mProvisioningManager.isRcsVolteSingleRegistrationCapable();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+ line="204"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
+ errorLine1=" mProvisioningManager.isRcsVolteSingleRegistrationCapable();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+ line="166"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
+ errorLine1=" mProvisioningManager.registerRcsProvisioningCallback(mExecutorService,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+ line="181"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
+ errorLine1=" mProvisioningManager.registerRcsProvisioningCallback(getMainExecutor(), mCallback);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+ line="221"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
+ errorLine1=" mProvisioningManager.setRcsClientConfiguration(getDefaultClientConfiguration());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+ line="180"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
+ errorLine1=" mProvisioningManager.setRcsClientConfiguration(getDefaultClientConfiguration());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+ line="220"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
+ errorLine1=" mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+ line="195"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
+ errorLine1=" mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+ line="348"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
+ errorLine1=" mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+ line="152"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getContactUri`"
+ errorLine1=" b.append(t.getContactUri());"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="220"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceCapabilities`"
+ errorLine1=" t.getServiceCapabilities();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="227"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceCapabilities`"
+ errorLine1=" if (t.getServiceCapabilities() != null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="225"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceId`"
+ errorLine1=" b.append(t.getServiceId());"
+ errorLine2=" ~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="222"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceVersion`"
+ errorLine1=" b.append(t.getServiceVersion());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="224"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#getSupportedDuplexModes`"
+ errorLine1=" b.append(servCaps.getSupportedDuplexModes());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="233"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#getUnsupportedDuplexModes`"
+ errorLine1=" b.append(servCaps.getUnsupportedDuplexModes());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="235"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#isAudioCapable`"
+ errorLine1=" b.append(servCaps.isAudioCapable());"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="229"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#isVideoCapable`"
+ errorLine1=" b.append(servCaps.isVideoCapable());"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="231"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getCapabilityMechanism`"
+ errorLine1=" if (c.getCapabilityMechanism() == RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="216"
+ column="15"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getCapabilityTuples`"
+ errorLine1=" for (RcsContactPresenceTuple t : c.getCapabilityTuples()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="218"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getContactUri`"
+ errorLine1=" b.append(c.getContactUri());"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="211"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getRequestResult`"
+ errorLine1=" b.append(c.getRequestResult());"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="213"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getSourceType`"
+ errorLine1=" b.append(c.getSourceType());"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="215"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter#requestAvailability`"
+ errorLine1=" mImsRcsManager.getUceAdapter().requestAvailability(contactList.get(0),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="135"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter#requestCapabilities`"
+ errorLine1=" mImsRcsManager.getUceAdapter().requestCapabilities(contactList, getMainExecutor(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="95"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
+ errorLine1=' + ", \n\tmHomeDomain=" + config.getHomeDomain()'
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="341"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
+ errorLine1=' + ", \n\tmImei=" + config.getImei()'
+ errorLine2=" ~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="342"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
+ errorLine1=' + ", \n\tmIpSecConfiguration=" + config.getIpSecConfiguration()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="354"
+ column="57"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+ errorLine1=' + ", \n\tmLocalIpAddr=" + config.getLocalAddress()'
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="334"
+ column="50"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+ errorLine1=' + ", \n\tmMaxUdpPayloadSize=" + config.getMaxUdpPayloadSizeBytes()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="338"
+ column="56"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getNatSocketAddress`"
+ errorLine1=" + ", \n\tmNatConfiguration=" + config.getNatSocketAddress() + '}';"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="355"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPrivateUserIdentifier`"
+ errorLine1=' + ", \n\tmPrivateUserIdentifier=" + config.getPrivateUserIdentifier()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="340"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicGruuUri`"
+ errorLine1=' + ", \n\tmGruu=" + config.getPublicGruuUri()'
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="343"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
+ errorLine1=' + ", \n\tmPublicUserIdentifier=" + config.getPublicUserIdentifier()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="339"
+ column="59"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
+ errorLine1=' + ", \n\tmAssociatedUriHeader=" + config.getSipAssociatedUriHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="353"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationHeader`"
+ errorLine1=' + ", \n\tmSipAuthHeader=" + config.getSipAuthenticationHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="344"
+ column="52"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationNonce`"
+ errorLine1=' + ", \n\tmSipAuthNonce=" + config.getSipAuthenticationNonce()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="345"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipCniHeader`"
+ errorLine1=' + ", \n\tmCniHeader=" + config.getSipCniHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="352"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
+ errorLine1=' + ", \n\tmContactUserParam=" + config.getSipContactUserParameter()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="349"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
+ errorLine1=' + ", \n\tmPaniHeader=" + config.getSipPaniHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="350"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPathHeader`"
+ errorLine1=' + ", \n\tmPathHeader=" + config.getSipPathHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="347"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
+ errorLine1=' + ", \n\tmPlaniHeader=" + config.getSipPlaniHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="351"
+ column="50"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+ errorLine1=' + ", \n\tmSipServerAddr=" + config.getSipServerAddress()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="335"
+ column="52"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
+ errorLine1=' + ", \n\tmServiceRouteHeader=" + config.getSipServiceRouteHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="346"
+ column="57"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
+ errorLine1=' + ", \n\tmUserAgentHeader=" + config.getSipUserAgentHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="348"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
+ errorLine1=' + ", \n\tmTransportType=" + config.getTransportType()'
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="333"
+ column="52"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+ errorLine1=' + "mVersion=" + config.getVersion()'
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="332"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipCompactFormEnabled`"
+ errorLine1=' + ", \n\tmIsSipCompactFormEnabled=" + config.isSipCompactFormEnabled()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="336"
+ column="62"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipKeepaliveEnabled`"
+ errorLine1=' + ", \n\tmIsSipKeepaliveEnabled=" + config.isSipKeepaliveEnabled()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="337"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#createSipDelegate`"
+ errorLine1=" mSipDelegateManager.createSipDelegate(new DelegateRequest(featureTags),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="231"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
+ errorLine1=" mSipDelegateManager.destroySipDelegate(mSipDelegateConnection,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="247"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
+ errorLine1=" mSipDelegateManager.destroySipDelegate(mSipDelegateConnection,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="322"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.telephony.gba.UaSecurityProtocolIdentifier.Builder`"
+ errorLine1=" new UaSecurityProtocolIdentifier.Builder();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+ line="120"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.telephony.ims.DelegateRequest`"
+ errorLine1=" mSipDelegateManager.createSipDelegate(new DelegateRequest(featureTags),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="231"
+ column="59"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
+ errorLine1=" return new RcsClientConfiguration("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+ line="231"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
+ errorLine1=" return new RcsClientConfiguration("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+ line="106"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Cast to `BootstrapAuthenticationCallback` requires API level 31 (current min is 30)"
+ errorLine1=" new BootstrapAuthenticationCallback() {"
+ errorLine2=" ^">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+ line="135"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Cast to `CapabilitiesCallback` requires API level 31 (current min is 30)"
+ errorLine1=" getMainExecutor(), new RcsUceAdapter.CapabilitiesCallback() {"
+ errorLine2=" ^">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="136"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Cast to `CapabilitiesCallback` requires API level 31 (current min is 30)"
+ errorLine1=" new RcsUceAdapter.CapabilitiesCallback() {"
+ errorLine2=" ^">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="96"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyManager.BootstrapAuthenticationCallback`"
+ errorLine1=" new BootstrapAuthenticationCallback() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+ line="135"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
+ errorLine1=" new RcsProvisioningCallback() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+ line="89"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
+ errorLine1=" new RcsProvisioningCallback() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+ line="80"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter.CapabilitiesCallback`"
+ errorLine1=" getMainExecutor(), new RcsUceAdapter.CapabilitiesCallback() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="136"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter.CapabilitiesCallback`"
+ errorLine1=" new RcsUceAdapter.CapabilitiesCallback() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+ line="96"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionMessageCallback`"
+ errorLine1=" new DelegateConnectionMessageCallback() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="87"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionStateCallback`"
+ errorLine1=" new DelegateConnectionStateCallback() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+ line="117"
+ column="17"/>
+ </issue>
+
+</issues>
\ No newline at end of file
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/NumberUtils.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/NumberUtils.java
index 14d3b9c..4a47718 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/NumberUtils.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/NumberUtils.java
@@ -24,6 +24,8 @@
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.Phonenumber;
+import java.util.Locale;
+
public class NumberUtils {
/**
@@ -33,7 +35,7 @@
*/
public static String formatNumber(Context context, String number) {
TelephonyManager manager = context.getSystemService(TelephonyManager.class);
- String simCountryIso = manager.getSimCountryIso().toUpperCase();
+ String simCountryIso = manager.getSimCountryIso().toUpperCase(Locale.ROOT);
PhoneNumberUtil util = PhoneNumberUtil.getInstance();
try {
Phonenumber.PhoneNumber phoneNumber = util.parse(number, simCountryIso);
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/lint-baseline.xml b/testapps/TestRcsApp/aosp_test_rcsclient/lint-baseline.xml
new file mode 100644
index 0000000..e0c7c3e
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/lint-baseline.xml
@@ -0,0 +1,928 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.net.ConnectivityManager#registerQosCallback`"
+ errorLine1=" connectivityManager.registerQosCallback(new QosSocketInfo(network, socket),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
+ line="118"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.net.ConnectivityManager#unregisterQosCallback`"
+ errorLine1=" connectivityManager.unregisterQosCallback(qosCallback);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
+ line="181"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#bootstrapAuthenticationRequest`"
+ errorLine1=" telephonyManager.bootstrapAuthenticationRequest("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+ line="97"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#build`"
+ errorLine1=" UaSecurityProtocolIdentifier spId = builder.build();"
+ errorLine2=" ~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+ line="79"
+ column="53"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setOrg`"
+ errorLine1=" builder.setOrg(organization)"
+ errorLine2=" ~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+ line="67"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setProtocol`"
+ errorLine1=" .setProtocol(protocol);"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+ line="68"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setTlsCipherSuite`"
+ errorLine1=" builder.setTlsCipherSuite(TlsParams.TLS_RSA_WITH_AES_128_CBC_SHA);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+ line="70"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setTlsCipherSuite`"
+ errorLine1=" builder.setTlsCipherSuite(cipherSuite);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+ line="72"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
+ errorLine1=" .getRegisteredFeatureTags()"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="139"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
+ errorLine1=" Set<String> registeredFt = registrationState.getRegisteredFeatureTags();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="223"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getFeatureTag`"
+ errorLine1=' stringBuilder.append(featureTagState.getFeatureTag()).append(" ").append('
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="220"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getState`"
+ errorLine1=" featureTagState.getState());"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="221"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ImsManager#getSipDelegateManager`"
+ errorLine1=" this.sipDelegateManager = imsManager.getSipDelegateManager(subscriptionId);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="77"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
+ errorLine1=" return provisioningManager.isRcsVolteSingleRegistrationCapable();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+ line="166"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
+ errorLine1=" provisioningManager.registerRcsProvisioningCallback(executorService, callback);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+ line="147"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
+ errorLine1=" provisioningManager.setRcsClientConfiguration(clientConfiguration);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+ line="111"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#triggerRcsReconfiguration`"
+ errorLine1=" provisioningManager.triggerRcsReconfiguration();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+ line="176"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
+ errorLine1=" provisioningManager.unregisterRcsProvisioningCallback(callback);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+ line="158"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
+ errorLine1=' + ", \n\tmHomeDomain=" + config.getHomeDomain()'
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="246"
+ column="53"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
+ errorLine1=" return mConfiguration.getHomeDomain();"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="317"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
+ errorLine1=' + ", \n\tmImei=" + config.getImei()'
+ errorLine2=" ~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="247"
+ column="47"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
+ errorLine1=" return mConfiguration.getImei();"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="357"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
+ errorLine1=' + ", \n\tmIpSecConfiguration=" + config.getIpSecConfiguration()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="259"
+ column="61"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
+ errorLine1=" SipDelegateConfiguration.IpSecConfiguration c = mConfiguration.getIpSecConfiguration();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="333"
+ column="76"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+ errorLine1=' + ", \n\tmLocalAddr=" + config.getLocalAddress()'
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="239"
+ column="52"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+ errorLine1=" return mConfiguration.getLocalAddress().getAddress().getHostAddress();"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="296"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+ errorLine1=" return mConfiguration.getLocalAddress().getPort();"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="301"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+ errorLine1=' + ", \n\tmMaxUdpPayloadSize=" + config.getMaxUdpPayloadSizeBytes()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="243"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+ errorLine1=" ? mConfiguration.getMaxUdpPayloadSizeBytes() : 1500;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="378"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+ errorLine1=" return mConfiguration.getMaxUdpPayloadSizeBytes() > 0"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="377"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getNatSocketAddress`"
+ errorLine1=" + ", \n\tmNatConfiguration=" + config.getNatSocketAddress() + '}';"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="260"
+ column="59"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPrivateUserIdentifier`"
+ errorLine1=' + ", \n\tmPrivateUserIdentifier=" + config.getPrivateUserIdentifier()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="245"
+ column="64"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicGruuUri`"
+ errorLine1=' + ", \n\tmGruu=" + config.getPublicGruuUri()'
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="248"
+ column="47"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
+ errorLine1=' + ", \n\tmPublicUserIdentifier=" + config.getPublicUserIdentifier()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="244"
+ column="63"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
+ errorLine1=" return mConfiguration.getPublicUserIdentifier();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="312"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
+ errorLine1=' + ", \n\tmAssociatedUriHeader=" + config.getSipAssociatedUriHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="258"
+ column="62"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
+ errorLine1=" String associatedUris = mConfiguration.getSipAssociatedUriHeader();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="322"
+ column="52"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationHeader`"
+ errorLine1=' + ", \n\tmSipAuthHeader=" + config.getSipAuthenticationHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="249"
+ column="56"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationNonce`"
+ errorLine1=' + ", \n\tmSipAuthNonce=" + config.getSipAuthenticationNonce()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="250"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipCniHeader`"
+ errorLine1=' + ", \n\tmCniHeader=" + config.getSipCniHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="257"
+ column="52"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
+ errorLine1=' + ", \n\tmContactUserParam=" + config.getSipContactUserParameter()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="254"
+ column="59"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
+ errorLine1=" return mConfiguration.getSipContactUserParameter();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="352"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
+ errorLine1=' + ", \n\tmPaniHeader=" + config.getSipPaniHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="255"
+ column="53"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
+ errorLine1=" return mConfiguration.getSipPaniHeader();"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="362"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPathHeader`"
+ errorLine1=' + ", \n\tmPathHeader=" + config.getSipPathHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="252"
+ column="53"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
+ errorLine1=' + ", \n\tmPlaniHeader=" + config.getSipPlaniHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="256"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
+ errorLine1=" return mConfiguration.getSipPlaniHeader();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="367"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+ errorLine1=' + ", \n\tmSipServerAddr=" + config.getSipServerAddress()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="240"
+ column="56"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+ errorLine1=" return mConfiguration.getSipServerAddress().getAddress().getHostAddress();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="286"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+ errorLine1=" return mConfiguration.getSipServerAddress().getPort();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="291"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
+ errorLine1=' + ", \n\tmServiceRouteHeader=" + config.getSipServiceRouteHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="251"
+ column="61"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
+ errorLine1=" mConfiguration.getSipServiceRouteHeader();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="343"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
+ errorLine1=' + ", \n\tmUserAgentHeader=" + config.getSipUserAgentHeader()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="253"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
+ errorLine1=" return mConfiguration.getSipUserAgentHeader();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="372"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
+ errorLine1=' + ", \n\tmTransportType=" + config.getTransportType()'
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="238"
+ column="56"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
+ errorLine1=" int sipTransport = mConfiguration.getTransportType();"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="306"
+ column="47"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+ errorLine1=" + registeredSipConfig.getVersion());"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="127"
+ column="63"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+ errorLine1=' + "mVersion=" + config.getVersion()'
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="237"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+ errorLine1=" return mConfiguration.getVersion();"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="281"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipCompactFormEnabled`"
+ errorLine1=' + ", \n\tmIsSipCompactFormEnabled=" + config.isSipCompactFormEnabled()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="241"
+ column="66"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipKeepaliveEnabled`"
+ errorLine1=' + ", \n\tmIsSipKeepaliveEnabled=" + config.isSipKeepaliveEnabled()'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="242"
+ column="64"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#getSipSecurityVerifyHeader`"
+ errorLine1=" return c.getSipSecurityVerifyHeader();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="337"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConnection#sendMessage`"
+ errorLine1=" sipDelegateConnection.sendMessage(MessageConverter.toPlatformMessage(message),"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="271"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#createSipDelegate`"
+ errorLine1=" controller.sipDelegateManager.createSipDelegate("
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="205"
+ column="47"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
+ errorLine1=" sipDelegateManager.destroySipDelegate(context.sipDelegateConnection,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="92"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getContent`"
+ errorLine1=" return new SipMessage(message.getStartLine(), headers, message.getContent());"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="395"
+ column="76"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getHeaderSection`"
+ errorLine1=' + message.getHeaderSection().substring(0, 10) + "->"'
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="392"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getHeaderSection`"
+ errorLine1=" String headers = message.getHeaderSection();"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="387"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getStartLine`"
+ errorLine1=" return new SipMessage(message.getStartLine(), headers, message.getContent());"
+ errorLine2=" ~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="395"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#toEncodedMessage`"
+ errorLine1=" message.toEncodedMessage(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java"
+ line="91"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#toEncodedMessage`"
+ errorLine1=" return (Message) method.invoke(new StringMsgParser(), message.toEncodedMessage());"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java"
+ line="101"
+ column="79"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.net.QosSocketInfo`"
+ errorLine1=" connectivityManager.registerQosCallback(new QosSocketInfo(network, socket),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
+ line="118"
+ column="53"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.telephony.gba.UaSecurityProtocolIdentifier.Builder`"
+ errorLine1=" new UaSecurityProtocolIdentifier.Builder();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+ line="55"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.telephony.ims.DelegateRequest`"
+ errorLine1=" DelegateRequest request = new DelegateRequest(imsService.getFeatureTags());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="203"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
+ errorLine1=" return new RcsClientConfiguration("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+ line="74"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.telephony.ims.SipMessage`"
+ errorLine1=" return new SipMessage(message.getStartLine(), headers, message.getContent());"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="395"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `new android.telephony.ims.SipMessage`"
+ errorLine1=" return new SipMessage(startLine, headers.toString(), rawContent);"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java"
+ line="72"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Cast to `BootstrapAuthenticationCallback` requires API level 31 (current min is 30)"
+ errorLine1=" new TelephonyManager.BootstrapAuthenticationCallback() {"
+ errorLine2=" ^">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+ line="81"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Cast to `RcsProvisioningCallback` requires API level 31 (current min is 30)"
+ errorLine1=" new RcsProvisioningCallback() {"
+ errorLine2=" ^">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+ line="114"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.net.QosCallback`"
+ errorLine1=" private final QosCallback qosCallback = new QosCallback() {"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
+ line="94"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyManager.BootstrapAuthenticationCallback`"
+ errorLine1=" new TelephonyManager.BootstrapAuthenticationCallback() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+ line="81"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
+ errorLine1=" new RcsProvisioningCallback() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+ line="114"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionMessageCallback`"
+ errorLine1=" new DelegateConnectionMessageCallback() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="158"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionStateCallback`"
+ errorLine1=" new DelegateConnectionStateCallback() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+ line="114"
+ column="21"/>
+ </issue>
+
+</issues>
\ No newline at end of file
diff --git a/testapps/TestServerApp/app/src/main/res/values-am/strings.xml b/testapps/TestServerApp/app/src/main/res/values-am/strings.xml
new file mode 100644
index 0000000..8ec544c
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-am/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"ቅንብሮች"</string>
+ <string name="server_running" msgid="2780193626090379172">"አገልጋይ እያሄደ ነው..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"አገልጋይን አቁም"</string>
+ <string name="server_down" msgid="1030249207496490556">"አገልጋይ አይሰራም"</string>
+ <string name="start_server" msgid="3878573341408591975">"አገልጋይን አስጀምር"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"ተሰናክሏል"</item>
+ <item msgid="3193389681837907872">"ነቅቷል"</item>
+ <item msgid="3124590179479393815">"ተኳሃኝ አይደለም"</item>
+ <item msgid="1606753456265236910">"በማቅረብ ላይ"</item>
+ <item msgid="3930807209231347454">"ተካትቷል"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"አልቀረበም"</item>
+ <item msgid="7598231293776486217">"ቀርቧል"</item>
+ <item msgid="3720547957514534185">"አያስፈልግም"</item>
+ <item msgid="1264673582354896949">"በሂደት ላይ"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-ar/strings.xml b/testapps/TestServerApp/app/src/main/res/values-ar/strings.xml
new file mode 100644
index 0000000..c901917
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-ar/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"الإعدادات"</string>
+ <string name="server_running" msgid="2780193626090379172">"الخادم قيد التشغيل…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"إيقاف الخادم"</string>
+ <string name="server_down" msgid="1030249207496490556">"الخادم معطّل"</string>
+ <string name="start_server" msgid="3878573341408591975">"بدء الخادم"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"غير مفعَّلة"</item>
+ <item msgid="3193389681837907872">"مفعّلة"</item>
+ <item msgid="3124590179479393815">"غير متوافق"</item>
+ <item msgid="1606753456265236910">"جارٍ توفير المتطلبات اللازمة"</item>
+ <item msgid="3930807209231347454">"متاحة"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"لم يتم توفير المتطلبات اللازمة"</item>
+ <item msgid="7598231293776486217">"توفير المتطلبات اللازمة"</item>
+ <item msgid="3720547957514534185">"غير مطلوب"</item>
+ <item msgid="1264673582354896949">"قيد التقدم"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-as/strings.xml b/testapps/TestServerApp/app/src/main/res/values-as/strings.xml
new file mode 100644
index 0000000..46ee915
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-as/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"ছেটিং"</string>
+ <string name="server_running" msgid="2780193626090379172">"ছাৰ্ভাৰটো চলি আছে..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"ছাৰ্ভাৰ বন্ধ কৰক"</string>
+ <string name="server_down" msgid="1030249207496490556">"ছাৰ্ভাৰটো কাৰ্যক্ষম হৈ থকা নাই"</string>
+ <string name="start_server" msgid="3878573341408591975">"ছাৰ্ভাৰ আৰম্ভ কৰক"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"অক্ষম কৰা আছে"</item>
+ <item msgid="3193389681837907872">"সক্ষম কৰা আছে"</item>
+ <item msgid="3124590179479393815">"অমিল"</item>
+ <item msgid="1606753456265236910">"প্ৰ’ভিজনিং"</item>
+ <item msgid="3930807209231347454">"অন্তৰ্ভুক্ত কৰা হ’ল"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"প্ৰ’ভিজন কৰা নাই"</item>
+ <item msgid="7598231293776486217">"প্ৰ’ভিজন কৰা হৈছে"</item>
+ <item msgid="3720547957514534185">"প্ৰয়োজনীয় নহয়"</item>
+ <item msgid="1264673582354896949">"চলি আছে"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-az/strings.xml b/testapps/TestServerApp/app/src/main/res/values-az/strings.xml
new file mode 100644
index 0000000..c7bdb24
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-az/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Ayarlar"</string>
+ <string name="server_running" msgid="2780193626090379172">"Server işləyir..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Serveri dayandırın"</string>
+ <string name="server_down" msgid="1030249207496490556">"Server işləmir"</string>
+ <string name="start_server" msgid="3878573341408591975">"Serveri başladın"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Deaktiv"</item>
+ <item msgid="3193389681837907872">"Aktiv"</item>
+ <item msgid="3124590179479393815">"Uyğun deyil"</item>
+ <item msgid="1606753456265236910">"Təmin edilir"</item>
+ <item msgid="3930807209231347454">"Daxildir"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Təmin edilmir"</item>
+ <item msgid="7598231293776486217">"Təmin edilib"</item>
+ <item msgid="3720547957514534185">"Tələb olunmur"</item>
+ <item msgid="1264673582354896949">"Davam edir"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-b+sr+Latn/strings.xml b/testapps/TestServerApp/app/src/main/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..62aeff1
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Podešavanja"</string>
+ <string name="server_running" msgid="2780193626090379172">"Server je pokrenut…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Zaustavi server"</string>
+ <string name="server_down" msgid="1030249207496490556">"Server je pao"</string>
+ <string name="start_server" msgid="3878573341408591975">"Pokreni server"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Onemogućeno"</item>
+ <item msgid="3193389681837907872">"Omogućeno"</item>
+ <item msgid="3124590179479393815">"Nekompatibilno"</item>
+ <item msgid="1606753456265236910">"Dodeljuje se"</item>
+ <item msgid="3930807209231347454">"Uvršteno"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Nije dodeljeno"</item>
+ <item msgid="7598231293776486217">"Dodeljeno"</item>
+ <item msgid="3720547957514534185">"Nije obavezno"</item>
+ <item msgid="1264673582354896949">"U toku"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-be/strings.xml b/testapps/TestServerApp/app/src/main/res/values-be/strings.xml
new file mode 100644
index 0000000..5f1f581
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-be/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Налады"</string>
+ <string name="server_running" msgid="2780193626090379172">"Сервер працуе..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Спыніць сервер"</string>
+ <string name="server_down" msgid="1030249207496490556">"Сервер не працуе"</string>
+ <string name="start_server" msgid="3878573341408591975">"Запусціць сервер"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Адключана"</item>
+ <item msgid="3193389681837907872">"Уключана"</item>
+ <item msgid="3124590179479393815">"Адсутнічае сумяшчальнасць"</item>
+ <item msgid="1606753456265236910">"Ініцыялізацыя"</item>
+ <item msgid="3930807209231347454">"Уключана"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Не ініцыялізавана"</item>
+ <item msgid="7598231293776486217">"Ініцыялізавана"</item>
+ <item msgid="3720547957514534185">"Не патрабуецца"</item>
+ <item msgid="1264673582354896949">"Выконваецца"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-bg/strings.xml b/testapps/TestServerApp/app/src/main/res/values-bg/strings.xml
new file mode 100644
index 0000000..542d0f7
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-bg/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Настройки"</string>
+ <string name="server_running" msgid="2780193626090379172">"Сървърът работи..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Спиране на сървъра"</string>
+ <string name="server_down" msgid="1030249207496490556">"Сървърът не работи"</string>
+ <string name="start_server" msgid="3878573341408591975">"Стартиране на сървъра"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Деактивирано"</item>
+ <item msgid="3193389681837907872">"Активирано"</item>
+ <item msgid="3124590179479393815">"Несъвместимо"</item>
+ <item msgid="1606753456265236910">"Обезпечава се"</item>
+ <item msgid="3930807209231347454">"Включено"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Не е обезпечено"</item>
+ <item msgid="7598231293776486217">"Обезпечено"</item>
+ <item msgid="3720547957514534185">"Не е задължително"</item>
+ <item msgid="1264673582354896949">"В ход"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-bn/strings.xml b/testapps/TestServerApp/app/src/main/res/values-bn/strings.xml
new file mode 100644
index 0000000..7244308
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-bn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"সেটিংস"</string>
+ <string name="server_running" msgid="2780193626090379172">"সার্ভার রান করছে..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"সার্ভার বন্ধ করুন"</string>
+ <string name="server_down" msgid="1030249207496490556">"সার্ভার কাজ করছে না"</string>
+ <string name="start_server" msgid="3878573341408591975">"সার্ভার চালু করুন"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"বন্ধ আছে"</item>
+ <item msgid="3193389681837907872">"চালু আছে"</item>
+ <item msgid="3124590179479393815">"মানানসই নয়"</item>
+ <item msgid="1606753456265236910">"প্রস্তুতি চলছে"</item>
+ <item msgid="3930807209231347454">"অন্তর্ভুক্ত আছে"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"প্রস্তুত নেই"</item>
+ <item msgid="7598231293776486217">"প্রস্তুত আছে"</item>
+ <item msgid="3720547957514534185">"প্রয়োজন নেই"</item>
+ <item msgid="1264673582354896949">"কাজ চলছে"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-cs/strings.xml b/testapps/TestServerApp/app/src/main/res/values-cs/strings.xml
new file mode 100644
index 0000000..c8dfd8d
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-cs/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Nastavení"</string>
+ <string name="server_running" msgid="2780193626090379172">"Server běží…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Zastavit server"</string>
+ <string name="server_down" msgid="1030249207496490556">"Server je nedostupný"</string>
+ <string name="start_server" msgid="3878573341408591975">"Spustit server"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Vypnuto"</item>
+ <item msgid="3193389681837907872">"Zapnuto"</item>
+ <item msgid="3124590179479393815">"Nekompatibilní"</item>
+ <item msgid="1606753456265236910">"Zajišťování"</item>
+ <item msgid="3930807209231347454">"Zahrnuto"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Nezajištěno"</item>
+ <item msgid="7598231293776486217">"Zajištěno"</item>
+ <item msgid="3720547957514534185">"Nepovinné"</item>
+ <item msgid="1264673582354896949">"Probíhá"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-da/strings.xml b/testapps/TestServerApp/app/src/main/res/values-da/strings.xml
new file mode 100644
index 0000000..1d8e029
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-da/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"Testserverapp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Indstillinger"</string>
+ <string name="server_running" msgid="2780193626090379172">"Serveren kører…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Stop server"</string>
+ <string name="server_down" msgid="1030249207496490556">"Serveren er nede"</string>
+ <string name="start_server" msgid="3878573341408591975">"Start server"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Deaktiveret"</item>
+ <item msgid="3193389681837907872">"Aktiveret"</item>
+ <item msgid="3124590179479393815">"Ikke kompatibel"</item>
+ <item msgid="1606753456265236910">"Provisionerer"</item>
+ <item msgid="3930807209231347454">"Inkluderet"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Ikke provisioneret"</item>
+ <item msgid="7598231293776486217">"Provisioneret"</item>
+ <item msgid="3720547957514534185">"Ikke påkrævet"</item>
+ <item msgid="1264673582354896949">"I gang"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-de/strings.xml b/testapps/TestServerApp/app/src/main/res/values-de/strings.xml
new file mode 100644
index 0000000..4adc332
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-de/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Einstellungen"</string>
+ <string name="server_running" msgid="2780193626090379172">"Server ist in Betrieb…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Server anhalten"</string>
+ <string name="server_down" msgid="1030249207496490556">"Server ist ausgefallen"</string>
+ <string name="start_server" msgid="3878573341408591975">"Server starten"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Deaktiviert"</item>
+ <item msgid="3193389681837907872">"Aktiviert"</item>
+ <item msgid="3124590179479393815">"Nicht kompatibel"</item>
+ <item msgid="1606753456265236910">"Nutzerverwaltung"</item>
+ <item msgid="3930807209231347454">"Enthalten"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Noch nicht von einem Nutzer verwaltet"</item>
+ <item msgid="7598231293776486217">"Von einem Nutzer verwaltet"</item>
+ <item msgid="3720547957514534185">"Nicht erforderlich"</item>
+ <item msgid="1264673582354896949">"In Bearbeitung"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-el/strings.xml b/testapps/TestServerApp/app/src/main/res/values-el/strings.xml
new file mode 100644
index 0000000..540500d
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-el/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Ρυθμίσεις"</string>
+ <string name="server_running" msgid="2780193626090379172">"Ο διακομιστής λειτουργεί…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Διακοπή διακομιστή"</string>
+ <string name="server_down" msgid="1030249207496490556">"Ο διακομιστής είναι εκτός λειτουργίας"</string>
+ <string name="start_server" msgid="3878573341408591975">"Έναρξη διακομιστή"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Ανενεργό"</item>
+ <item msgid="3193389681837907872">"Ενεργό"</item>
+ <item msgid="3124590179479393815">"Μη συμβατό"</item>
+ <item msgid="1606753456265236910">"Παροχή"</item>
+ <item msgid="3930807209231347454">"Περιλαμβάνεται"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Χωρίς παροχή"</item>
+ <item msgid="7598231293776486217">"Παρέχεται"</item>
+ <item msgid="3720547957514534185">"Δεν απαιτείται"</item>
+ <item msgid="1264673582354896949">"Σε εξέλιξη"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-es-rUS/strings.xml b/testapps/TestServerApp/app/src/main/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..50c9ff2
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-es-rUS/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Configuración"</string>
+ <string name="server_running" msgid="2780193626090379172">"El servidor se está ejecutando…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Detener el servidor"</string>
+ <string name="server_down" msgid="1030249207496490556">"El servidor se encuentra inactivo"</string>
+ <string name="start_server" msgid="3878573341408591975">"Iniciar el servidor"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Inhabilitado"</item>
+ <item msgid="3193389681837907872">"Habilitado"</item>
+ <item msgid="3124590179479393815">"Incompatible"</item>
+ <item msgid="1606753456265236910">"Aprovisionando"</item>
+ <item msgid="3930807209231347454">"Incluido"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"No aprovisionado"</item>
+ <item msgid="7598231293776486217">"Aprovisionado"</item>
+ <item msgid="3720547957514534185">"No se necesita"</item>
+ <item msgid="1264673582354896949">"En curso"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-es/strings.xml b/testapps/TestServerApp/app/src/main/res/values-es/strings.xml
new file mode 100644
index 0000000..002fca7
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-es/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Ajustes"</string>
+ <string name="server_running" msgid="2780193626090379172">"El servidor se está ejecutando..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Detener servidor"</string>
+ <string name="server_down" msgid="1030249207496490556">"El servidor no está operativo"</string>
+ <string name="start_server" msgid="3878573341408591975">"Iniciar servidor"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Inhabilitado"</item>
+ <item msgid="3193389681837907872">"Habilitado"</item>
+ <item msgid="3124590179479393815">"No compatible"</item>
+ <item msgid="1606753456265236910">"En aprovisionamiento"</item>
+ <item msgid="3930807209231347454">"Incluido"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"No aprovisionado"</item>
+ <item msgid="7598231293776486217">"Aprovisionado"</item>
+ <item msgid="3720547957514534185">"No se requiere"</item>
+ <item msgid="1264673582354896949">"En curso"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-et/strings.xml b/testapps/TestServerApp/app/src/main/res/values-et/strings.xml
new file mode 100644
index 0000000..49c3209
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-et/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Seaded"</string>
+ <string name="server_running" msgid="2780193626090379172">"Server töötab ..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Peata server"</string>
+ <string name="server_down" msgid="1030249207496490556">"Serveris on katkestus"</string>
+ <string name="start_server" msgid="3878573341408591975">"Käivita server"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Keelatud"</item>
+ <item msgid="3193389681837907872">"Lubatud"</item>
+ <item msgid="3124590179479393815">"Ühildumatu"</item>
+ <item msgid="1606753456265236910">"Ettevalmistamine"</item>
+ <item msgid="3930807209231347454">"Kaasas"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Ettevalmistamata"</item>
+ <item msgid="7598231293776486217">"Ettevalmistatud"</item>
+ <item msgid="3720547957514534185">"Pole nõutav"</item>
+ <item msgid="1264673582354896949">"Töötluses"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-eu/strings.xml b/testapps/TestServerApp/app/src/main/res/values-eu/strings.xml
new file mode 100644
index 0000000..70f5423
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-eu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Ezarpenak"</string>
+ <string name="server_running" msgid="2780193626090379172">"Zerbitzaria abian da…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Gelditu zerbitzaria"</string>
+ <string name="server_down" msgid="1030249207496490556">"Zerbitzaria ez dabil"</string>
+ <string name="start_server" msgid="3878573341408591975">"Abiarazi zerbitzaria"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Desgaituta"</item>
+ <item msgid="3193389681837907872">"Gaituta"</item>
+ <item msgid="3124590179479393815">"Bateraezina"</item>
+ <item msgid="1606753456265236910">"Hornitzen"</item>
+ <item msgid="3930807209231347454">"Barne"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Hornitu gabe"</item>
+ <item msgid="7598231293776486217">"Hornituta"</item>
+ <item msgid="3720547957514534185">"Ez da beharrezkoa"</item>
+ <item msgid="1264673582354896949">"Abian"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-fi/strings.xml b/testapps/TestServerApp/app/src/main/res/values-fi/strings.xml
new file mode 100644
index 0000000..9117e0c
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-fi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Asetukset"</string>
+ <string name="server_running" msgid="2780193626090379172">"Palvelin on käytössä..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Keskeytä palvelin"</string>
+ <string name="server_down" msgid="1030249207496490556">"Palvelin on poissa käytöstä"</string>
+ <string name="start_server" msgid="3878573341408591975">"Käynnistä palvelin"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Ei käytössä"</item>
+ <item msgid="3193389681837907872">"Käytössä"</item>
+ <item msgid="3124590179479393815">"Yhteensopimaton"</item>
+ <item msgid="1606753456265236910">"Käsitellään"</item>
+ <item msgid="3930807209231347454">"Sisältyy"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Ei käsitelty"</item>
+ <item msgid="7598231293776486217">"Käsitelty"</item>
+ <item msgid="3720547957514534185">"Valinnainen"</item>
+ <item msgid="1264673582354896949">"Käynnissä"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-fr-rCA/strings.xml b/testapps/TestServerApp/app/src/main/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..97fd06a
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-fr-rCA/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Paramètres"</string>
+ <string name="server_running" msgid="2780193626090379172">"Le serveur est en cours d\'exécution…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Arrêter le serveur"</string>
+ <string name="server_down" msgid="1030249207496490556">"Le serveur est en panne"</string>
+ <string name="start_server" msgid="3878573341408591975">"Démarrer le serveur"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Désactivé"</item>
+ <item msgid="3193389681837907872">"Activé"</item>
+ <item msgid="3124590179479393815">"Incompatible"</item>
+ <item msgid="1606753456265236910">"Approvisionnement en cours…"</item>
+ <item msgid="3930807209231347454">"Inclus"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Non approvisionné"</item>
+ <item msgid="7598231293776486217">"Approvisionné"</item>
+ <item msgid="3720547957514534185">"Facultatif"</item>
+ <item msgid="1264673582354896949">"En cours de traitement"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-fr/strings.xml b/testapps/TestServerApp/app/src/main/res/values-fr/strings.xml
new file mode 100644
index 0000000..be70c79
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-fr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Paramètres"</string>
+ <string name="server_running" msgid="2780193626090379172">"Serveur en cours d\'exécution…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Arrêter le serveur"</string>
+ <string name="server_down" msgid="1030249207496490556">"Serveur en panne"</string>
+ <string name="start_server" msgid="3878573341408591975">"Démarrer le serveur"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Désactivé"</item>
+ <item msgid="3193389681837907872">"Activé"</item>
+ <item msgid="3124590179479393815">"Incompatible"</item>
+ <item msgid="1606753456265236910">"Provisionnement…"</item>
+ <item msgid="3930807209231347454">"Inclus"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Non provisionné"</item>
+ <item msgid="7598231293776486217">"Provisionné"</item>
+ <item msgid="3720547957514534185">"Facultatif"</item>
+ <item msgid="1264673582354896949">"En cours"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-gl/strings.xml b/testapps/TestServerApp/app/src/main/res/values-gl/strings.xml
new file mode 100644
index 0000000..9de1ceb
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-gl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Configuración"</string>
+ <string name="server_running" msgid="2780193626090379172">"O servidor está executándose…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Deter servidor"</string>
+ <string name="server_down" msgid="1030249207496490556">"O servidor non está operativo"</string>
+ <string name="start_server" msgid="3878573341408591975">"Iniciar servidor"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Desactivado"</item>
+ <item msgid="3193389681837907872">"Activado"</item>
+ <item msgid="3124590179479393815">"Incompatible"</item>
+ <item msgid="1606753456265236910">"En aprovisionamento"</item>
+ <item msgid="3930807209231347454">"Incluído"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Non aprovisionado"</item>
+ <item msgid="7598231293776486217">"Aprovisionado"</item>
+ <item msgid="3720547957514534185">"Non obrigatorio"</item>
+ <item msgid="1264673582354896949">"En curso"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-gu/strings.xml b/testapps/TestServerApp/app/src/main/res/values-gu/strings.xml
new file mode 100644
index 0000000..d2fc1d0
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-gu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"સેટિંગ"</string>
+ <string name="server_running" msgid="2780193626090379172">"સર્વર ચાલુ છે..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"સર્વર રોકો"</string>
+ <string name="server_down" msgid="1030249207496490556">"સર્વર ડાઉન છે"</string>
+ <string name="start_server" msgid="3878573341408591975">"સર્વર શરૂ કરો"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"બંધ છે"</item>
+ <item msgid="3193389681837907872">"ચાલુ છે"</item>
+ <item msgid="3124590179479393815">"અસંગત છે"</item>
+ <item msgid="1606753456265236910">"જોગવાઈ કરી રહ્યું છે"</item>
+ <item msgid="3930807209231347454">"શામેલ છે"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"કોઈ જોગવાઈ કરેલી નથી"</item>
+ <item msgid="7598231293776486217">"જોગવાઈ કરેલી છે"</item>
+ <item msgid="3720547957514534185">"આવશ્યક નથી"</item>
+ <item msgid="1264673582354896949">"પ્રક્રિયા ચાલુ છે"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-hi/strings.xml b/testapps/TestServerApp/app/src/main/res/values-hi/strings.xml
new file mode 100644
index 0000000..d5be924
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-hi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"सेटिंग"</string>
+ <string name="server_running" msgid="2780193626090379172">"सर्वर काम कर रहा है..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"सर्वर बंद करें"</string>
+ <string name="server_down" msgid="1030249207496490556">"सर्वर काम नहीं कर रहा है"</string>
+ <string name="start_server" msgid="3878573341408591975">"सर्वर चालू करें"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"बंद है"</item>
+ <item msgid="3193389681837907872">"चालू है"</item>
+ <item msgid="3124590179479393815">"काम नहीं करता"</item>
+ <item msgid="1606753456265236910">"प्रावधान"</item>
+ <item msgid="3930807209231347454">"पहले से मौजूद है"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"प्रावधान नहीं किया गया है"</item>
+ <item msgid="7598231293776486217">"प्रावधान किया गया है"</item>
+ <item msgid="3720547957514534185">"ज़रूरी नहीं है"</item>
+ <item msgid="1264673582354896949">"प्रावधान किया जा रहा है"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-hr/strings.xml b/testapps/TestServerApp/app/src/main/res/values-hr/strings.xml
new file mode 100644
index 0000000..492f2cc
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-hr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Postavke"</string>
+ <string name="server_running" msgid="2780193626090379172">"Poslužitelj je aktivan..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Zaustavljanje poslužitelja"</string>
+ <string name="server_down" msgid="1030249207496490556">"Poslužitelj nije aktivan"</string>
+ <string name="start_server" msgid="3878573341408591975">"Pokretanje poslužitelja"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Onemogućeno"</item>
+ <item msgid="3193389681837907872">"Omogućeno"</item>
+ <item msgid="3124590179479393815">"Nije kompatibilno"</item>
+ <item msgid="1606753456265236910">"Omogućivanje"</item>
+ <item msgid="3930807209231347454">"Uključeno"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Nije omogućeno"</item>
+ <item msgid="7598231293776486217">"Omogućeno"</item>
+ <item msgid="3720547957514534185">"Nije obavezno"</item>
+ <item msgid="1264673582354896949">"U tijeku"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-hu/strings.xml b/testapps/TestServerApp/app/src/main/res/values-hu/strings.xml
new file mode 100644
index 0000000..286fae3
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-hu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Beállítások"</string>
+ <string name="server_running" msgid="2780193626090379172">"A szerver fut..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Szerver leállítása"</string>
+ <string name="server_down" msgid="1030249207496490556">"A szerver leállt"</string>
+ <string name="start_server" msgid="3878573341408591975">"Szerver indítása"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Kikapcsolva"</item>
+ <item msgid="3193389681837907872">"Engedélyezve"</item>
+ <item msgid="3124590179479393815">"Nem kompatibilis"</item>
+ <item msgid="1606753456265236910">"Kiépítés"</item>
+ <item msgid="3930807209231347454">"Tartalmazza"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Nincs kiépítve"</item>
+ <item msgid="7598231293776486217">"Kiépítve"</item>
+ <item msgid="3720547957514534185">"Nem kötelező"</item>
+ <item msgid="1264673582354896949">"Folyamatban"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-hy/strings.xml b/testapps/TestServerApp/app/src/main/res/values-hy/strings.xml
new file mode 100644
index 0000000..03a382b
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-hy/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Կարգավորումներ"</string>
+ <string name="server_running" msgid="2780193626090379172">"Սերվերն աշխատում է..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Կանգնեցնել սերվերը"</string>
+ <string name="server_down" msgid="1030249207496490556">"Սերվերն անջատված է"</string>
+ <string name="start_server" msgid="3878573341408591975">"Գործարկել սերվերը"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Անջատված է"</item>
+ <item msgid="3193389681837907872">"Միացված է"</item>
+ <item msgid="3124590179479393815">"Անհամատեղելիություն"</item>
+ <item msgid="1606753456265236910">"Նախապատրաստում"</item>
+ <item msgid="3930807209231347454">"Ներառված է"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Նախապատրաստված չէ"</item>
+ <item msgid="7598231293776486217">"Նախապատրաստված է"</item>
+ <item msgid="3720547957514534185">"Ոչ պարտադիր"</item>
+ <item msgid="1264673582354896949">"Ընթացքում է"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-in/strings.xml b/testapps/TestServerApp/app/src/main/res/values-in/strings.xml
new file mode 100644
index 0000000..b918582
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-in/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Setelan"</string>
+ <string name="server_running" msgid="2780193626090379172">"Server sedang berjalan..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Hentikan Server"</string>
+ <string name="server_down" msgid="1030249207496490556">"Server tidak berfungsi"</string>
+ <string name="start_server" msgid="3878573341408591975">"Mulai Server"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Nonaktif"</item>
+ <item msgid="3193389681837907872">"Aktif"</item>
+ <item msgid="3124590179479393815">"Tidak kompatibel"</item>
+ <item msgid="1606753456265236910">"Penyediaan"</item>
+ <item msgid="3930807209231347454">"Disertakan"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Tidak Disediakan"</item>
+ <item msgid="7598231293776486217">"Disediakan"</item>
+ <item msgid="3720547957514534185">"Tidak Wajib"</item>
+ <item msgid="1264673582354896949">"Dalam Proses"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-is/strings.xml b/testapps/TestServerApp/app/src/main/res/values-is/strings.xml
new file mode 100644
index 0000000..610755a
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-is/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Stillingar"</string>
+ <string name="server_running" msgid="2780193626090379172">"Þjónn er í gangi..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Stöðva þjón"</string>
+ <string name="server_down" msgid="1030249207496490556">"Þjónn liggur niðri"</string>
+ <string name="start_server" msgid="3878573341408591975">"Ræsa þjón"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Slökkt"</item>
+ <item msgid="3193389681837907872">"Kveikt"</item>
+ <item msgid="3124590179479393815">"Ósamhæft"</item>
+ <item msgid="1606753456265236910">"Úthlutun"</item>
+ <item msgid="3930807209231347454">"Innifalið"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Ekki úthlutað"</item>
+ <item msgid="7598231293776486217">"Úthlutað"</item>
+ <item msgid="3720547957514534185">"Ekki áskilið"</item>
+ <item msgid="1264673582354896949">"Í vinnslu"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-it/strings.xml b/testapps/TestServerApp/app/src/main/res/values-it/strings.xml
new file mode 100644
index 0000000..6f4f3b1
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-it/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Settings"</string>
+ <string name="server_running" msgid="2780193626090379172">"Server is running..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Stop Server"</string>
+ <string name="server_down" msgid="1030249207496490556">"Server is down"</string>
+ <string name="start_server" msgid="3878573341408591975">"Start Server"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Disabled"</item>
+ <item msgid="3193389681837907872">"Enabled"</item>
+ <item msgid="3124590179479393815">"Incompatible"</item>
+ <item msgid="1606753456265236910">"Provisioning"</item>
+ <item msgid="3930807209231347454">"Included"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Not Provisioned"</item>
+ <item msgid="7598231293776486217">"Provisioned"</item>
+ <item msgid="3720547957514534185">"Not Required"</item>
+ <item msgid="1264673582354896949">"In Progress"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-ja/strings.xml b/testapps/TestServerApp/app/src/main/res/values-ja/strings.xml
new file mode 100644
index 0000000..1b962e4
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-ja/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"設定"</string>
+ <string name="server_running" msgid="2780193626090379172">"サーバーが実行中です..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"サーバーを停止"</string>
+ <string name="server_down" msgid="1030249207496490556">"サーバーがダウンしています"</string>
+ <string name="start_server" msgid="3878573341408591975">"サーバーを起動"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"無効"</item>
+ <item msgid="3193389681837907872">"有効"</item>
+ <item msgid="3124590179479393815">"非対応"</item>
+ <item msgid="1606753456265236910">"プロビジョニング"</item>
+ <item msgid="3930807209231347454">"必須"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"未プロビジョニング"</item>
+ <item msgid="7598231293776486217">"プロビジョニング済み"</item>
+ <item msgid="3720547957514534185">"任意"</item>
+ <item msgid="1264673582354896949">"処理中"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-kk/strings.xml b/testapps/TestServerApp/app/src/main/res/values-kk/strings.xml
new file mode 100644
index 0000000..8a93c31
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-kk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Параметрлер"</string>
+ <string name="server_running" msgid="2780193626090379172">"Сервер істеп тұр…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Серверді тоқтату"</string>
+ <string name="server_down" msgid="1030249207496490556">"Сервер істемей тұр"</string>
+ <string name="start_server" msgid="3878573341408591975">"Серверді іске қосу"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Өшірулі"</item>
+ <item msgid="3193389681837907872">"Қосулы"</item>
+ <item msgid="3124590179479393815">"Үйлеспейді"</item>
+ <item msgid="1606753456265236910">"Дайындау"</item>
+ <item msgid="3930807209231347454">"Қамтылды"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Дайындалмады"</item>
+ <item msgid="7598231293776486217">"Дайындалды"</item>
+ <item msgid="3720547957514534185">"Міндетті емес"</item>
+ <item msgid="1264673582354896949">"Орындалып жатыр"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-kn/strings.xml b/testapps/TestServerApp/app/src/main/res/values-kn/strings.xml
new file mode 100644
index 0000000..227d44b
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-kn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <string name="server_running" msgid="2780193626090379172">"ಸರ್ವರ್ ರನ್ ಆಗುತ್ತಿದೆ..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"ಸರ್ವರ್ ನಿಲ್ಲಿಸಿ"</string>
+ <string name="server_down" msgid="1030249207496490556">"ಸರ್ವರ್ ಡೌನ್ ಆಗಿದೆ"</string>
+ <string name="start_server" msgid="3878573341408591975">"ಸರ್ವರ್ ಪ್ರಾರಂಭಿಸಿ"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</item>
+ <item msgid="3193389681837907872">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</item>
+ <item msgid="3124590179479393815">"ಹೊಂದಾಣಿಕೆಯಾಗುವುದಿಲ್ಲ"</item>
+ <item msgid="1606753456265236910">"ಒದಗಿಸಲಾಗುತ್ತಿದೆ"</item>
+ <item msgid="3930807209231347454">"ಒಳಗೊಂಡಿದೆ"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"ಒದಗಿಸಲಾಗಿಲ್ಲ"</item>
+ <item msgid="7598231293776486217">"ಒದಗಿಸಲಾಗಿದೆ"</item>
+ <item msgid="3720547957514534185">"ಅಗತ್ಯವಿಲ್ಲ"</item>
+ <item msgid="1264673582354896949">"ಪ್ರಗತಿಯಲ್ಲಿದೆ"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-ko/strings.xml b/testapps/TestServerApp/app/src/main/res/values-ko/strings.xml
new file mode 100644
index 0000000..ca9b15a
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-ko/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"설정"</string>
+ <string name="server_running" msgid="2780193626090379172">"서버 실행 중…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"서버를 중지하시겠습니까?"</string>
+ <string name="server_down" msgid="1030249207496490556">"서버가 다운됨"</string>
+ <string name="start_server" msgid="3878573341408591975">"서버를 시작하시겠습니까?"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"사용 안함"</item>
+ <item msgid="3193389681837907872">"사용 설정됨"</item>
+ <item msgid="3124590179479393815">"호환되지 않음"</item>
+ <item msgid="1606753456265236910">"프로비저닝"</item>
+ <item msgid="3930807209231347454">"포함됨"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"프로비저닝되지 않음"</item>
+ <item msgid="7598231293776486217">"프로비저닝됨"</item>
+ <item msgid="3720547957514534185">"필요 없음"</item>
+ <item msgid="1264673582354896949">"진행 중"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-ky/strings.xml b/testapps/TestServerApp/app/src/main/res/values-ky/strings.xml
new file mode 100644
index 0000000..3d6c36f
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-ky/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Параметрлер"</string>
+ <string name="server_running" msgid="2780193626090379172">"Сервер иштеп жатат..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Серверди токтотуу"</string>
+ <string name="server_down" msgid="1030249207496490556">"Сервер иштебей калды"</string>
+ <string name="start_server" msgid="3878573341408591975">"Серверди иштетип баштоо"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Өчүрүлдү"</item>
+ <item msgid="3193389681837907872">"Иштетилди"</item>
+ <item msgid="3124590179479393815">"Ылайык келбейт"</item>
+ <item msgid="1606753456265236910">"Камсыз кылууда"</item>
+ <item msgid="3930807209231347454">"Камтылган"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Камсыздалган эмес"</item>
+ <item msgid="7598231293776486217">"Камсыздалган"</item>
+ <item msgid="3720547957514534185">"Талап кылынбайт"</item>
+ <item msgid="1264673582354896949">"Аткарылууда"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-lt/strings.xml b/testapps/TestServerApp/app/src/main/res/values-lt/strings.xml
new file mode 100644
index 0000000..a7e79e9
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-lt/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Nustatymai"</string>
+ <string name="server_running" msgid="2780193626090379172">"Serveris veikia..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Sustabdyti serverį"</string>
+ <string name="server_down" msgid="1030249207496490556">"Serveris neveikia"</string>
+ <string name="start_server" msgid="3878573341408591975">"Paleisti serverį"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Išjungta"</item>
+ <item msgid="3193389681837907872">"Įgalinta"</item>
+ <item msgid="3124590179479393815">"Nesuderinama"</item>
+ <item msgid="1606753456265236910">"Parengiama"</item>
+ <item msgid="3930807209231347454">"Įtraukta"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Neparengta"</item>
+ <item msgid="7598231293776486217">"Parengta"</item>
+ <item msgid="3720547957514534185">"Nebūtina"</item>
+ <item msgid="1264673582354896949">"Vykdoma"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-lv/strings.xml b/testapps/TestServerApp/app/src/main/res/values-lv/strings.xml
new file mode 100644
index 0000000..a7bff51
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-lv/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"ServeraLietotneTestēšanai"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Iestatījumi"</string>
+ <string name="server_running" msgid="2780193626090379172">"Serveris darbojas…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Apturēt servera darbību"</string>
+ <string name="server_down" msgid="1030249207496490556">"Serveris nedarbojas"</string>
+ <string name="start_server" msgid="3878573341408591975">"Palaist serveri"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Atspējots"</item>
+ <item msgid="3193389681837907872">"Iespējots"</item>
+ <item msgid="3124590179479393815">"Nav saderīgs"</item>
+ <item msgid="1606753456265236910">"Notiek nodrošināšana"</item>
+ <item msgid="3930807209231347454">"Iekļauts"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Nav nodrošināts"</item>
+ <item msgid="7598231293776486217">"Nodrošināts"</item>
+ <item msgid="3720547957514534185">"Nav nepieciešams"</item>
+ <item msgid="1264673582354896949">"Notiek apstrāde"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-mk/strings.xml b/testapps/TestServerApp/app/src/main/res/values-mk/strings.xml
new file mode 100644
index 0000000..44a255c
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-mk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Поставки"</string>
+ <string name="server_running" msgid="2780193626090379172">"Серверот се извршува…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Сопри го серверот"</string>
+ <string name="server_down" msgid="1030249207496490556">"Серверот е паднат"</string>
+ <string name="start_server" msgid="3878573341408591975">"Стартувај го серверот"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Оневозможено"</item>
+ <item msgid="3193389681837907872">"Овозможено"</item>
+ <item msgid="3124590179479393815">"Некомпатибилно"</item>
+ <item msgid="1606753456265236910">"Се обезбедува"</item>
+ <item msgid="3930807209231347454">"Опфатено"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Необезбедено"</item>
+ <item msgid="7598231293776486217">"Обезбедено"</item>
+ <item msgid="3720547957514534185">"Незадолжително"</item>
+ <item msgid="1264673582354896949">"Во тек"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-ml/strings.xml b/testapps/TestServerApp/app/src/main/res/values-ml/strings.xml
new file mode 100644
index 0000000..32b305a
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-ml/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"ക്രമീകരണം"</string>
+ <string name="server_running" msgid="2780193626090379172">"സെർവർ പ്രവർത്തിക്കുന്നുണ്ട്..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"സെർവർ നിർത്തുക"</string>
+ <string name="server_down" msgid="1030249207496490556">"സെർവർ ലഭ്യമല്ല"</string>
+ <string name="start_server" msgid="3878573341408591975">"സെർവർ ആരംഭിക്കുക"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"പ്രവർത്തനരഹിതമാക്കി"</item>
+ <item msgid="3193389681837907872">"പ്രവർത്തനക്ഷമമാക്കി"</item>
+ <item msgid="3124590179479393815">"അനുയോജ്യമല്ല"</item>
+ <item msgid="1606753456265236910">"പ്രൊവിഷനിംഗ്"</item>
+ <item msgid="3930807209231347454">"ഉൾപ്പെടുത്തി"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"പ്രൊവിഷൻ ചെയ്തിട്ടില്ല"</item>
+ <item msgid="7598231293776486217">"പ്രൊവിഷൻ ചെയ്തു"</item>
+ <item msgid="3720547957514534185">"ആവശ്യമില്ല"</item>
+ <item msgid="1264673582354896949">"പുരോഗമിക്കുന്നു"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-mn/strings.xml b/testapps/TestServerApp/app/src/main/res/values-mn/strings.xml
new file mode 100644
index 0000000..6c131e2
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-mn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Тохиргоо"</string>
+ <string name="server_running" msgid="2780193626090379172">"Сервер ажиллаж байна..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Серверийг зогсоох"</string>
+ <string name="server_down" msgid="1030249207496490556">"Сервер унтарсан байна"</string>
+ <string name="start_server" msgid="3878573341408591975">"Серверийг эхлүүлэх"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Идэвхгүй болгосон"</item>
+ <item msgid="3193389681837907872">"Идэвхжүүлсэн"</item>
+ <item msgid="3124590179479393815">"Тохирохгүй"</item>
+ <item msgid="1606753456265236910">"Бэлтгэж байна"</item>
+ <item msgid="3930807209231347454">"Багтсан"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Бэлтгээгүй"</item>
+ <item msgid="7598231293776486217">"Бэлтгэсэн"</item>
+ <item msgid="3720547957514534185">"Заавал биш"</item>
+ <item msgid="1264673582354896949">"Үргэлжилж байна"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-mr/strings.xml b/testapps/TestServerApp/app/src/main/res/values-mr/strings.xml
new file mode 100644
index 0000000..74a0f56
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-mr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"सेटिंग्ज"</string>
+ <string name="server_running" msgid="2780193626090379172">"सर्व्हर रन होत आहे..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"सर्व्हर थांबवा"</string>
+ <string name="server_down" msgid="1030249207496490556">"सर्व्हर बंद आहे"</string>
+ <string name="start_server" msgid="3878573341408591975">"सर्व्हर सुरू करा"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"बंद आहे"</item>
+ <item msgid="3193389681837907872">"सुरू आहे"</item>
+ <item msgid="3124590179479393815">"कंपॅटिबल नाही"</item>
+ <item msgid="1606753456265236910">"तरतूद"</item>
+ <item msgid="3930807209231347454">"समावेश आहे"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"तरतूद केलेली नाही"</item>
+ <item msgid="7598231293776486217">"तरतूद केली आहे"</item>
+ <item msgid="3720547957514534185">"आवश्यक नाही"</item>
+ <item msgid="1264673582354896949">"प्रगतीपथावर आहे"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-my/strings.xml b/testapps/TestServerApp/app/src/main/res/values-my/strings.xml
new file mode 100644
index 0000000..9a0dcdf
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-my/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"ဆက်တင်များ"</string>
+ <string name="server_running" msgid="2780193626090379172">"ဆာဗာ လုပ်ဆောင်နေသည်…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"ဆာဗာ ရပ်ရန်"</string>
+ <string name="server_down" msgid="1030249207496490556">"ဆာဗာကျနေသည်"</string>
+ <string name="start_server" msgid="3878573341408591975">"ဆာဗာ စတင်ရန်"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"ပိတ်ထားသည်"</item>
+ <item msgid="3193389681837907872">"ဖွင့်ထားသည်"</item>
+ <item msgid="3124590179479393815">"တွဲဖက်မသုံးနိုင်ပါ"</item>
+ <item msgid="1606753456265236910">"ပံ့ပိုးပေးခြင်း"</item>
+ <item msgid="3930807209231347454">"ပါဝင်သည်"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"ပံ့ပိုးမထားပါ"</item>
+ <item msgid="7598231293776486217">"ပံ့ပိုးပေးထားသည်"</item>
+ <item msgid="3720547957514534185">"မလိုအပ်ပါ"</item>
+ <item msgid="1264673582354896949">"ဆောင်ရွက်နေဆဲ"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-nb/strings.xml b/testapps/TestServerApp/app/src/main/res/values-nb/strings.xml
new file mode 100644
index 0000000..d28a197
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-nb/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Innstillinger"</string>
+ <string name="server_running" msgid="2780193626090379172">"Tjeneren kjører …"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Stopp tjeneren"</string>
+ <string name="server_down" msgid="1030249207496490556">"Tjeneren er nede"</string>
+ <string name="start_server" msgid="3878573341408591975">"Start tjeneren"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Slått av"</item>
+ <item msgid="3193389681837907872">"Slått på"</item>
+ <item msgid="3124590179479393815">"Inkompatibel"</item>
+ <item msgid="1606753456265236910">"Klargjøring"</item>
+ <item msgid="3930807209231347454">"Inkludert"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Ikke klargjort"</item>
+ <item msgid="7598231293776486217">"Klargjort"</item>
+ <item msgid="3720547957514534185">"Ikke obligatorisk"</item>
+ <item msgid="1264673582354896949">"Under behandling"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-ne/strings.xml b/testapps/TestServerApp/app/src/main/res/values-ne/strings.xml
new file mode 100644
index 0000000..fe1f119
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-ne/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"सेटिङ"</string>
+ <string name="server_running" msgid="2780193626090379172">"सर्भर चल्दै छ..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"सर्भर बन्द गर्नुहोस्"</string>
+ <string name="server_down" msgid="1030249207496490556">"सर्भर डाउन छ"</string>
+ <string name="start_server" msgid="3878573341408591975">"सर्भर प्रयोग गर्न थाल्नुहोस्"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"अफ गरिएको छ"</item>
+ <item msgid="3193389681837907872">"अन गरिएको छ"</item>
+ <item msgid="3124590179479393815">"नमिल्दो"</item>
+ <item msgid="1606753456265236910">"प्रावधान मिलाइँदै छ"</item>
+ <item msgid="3930807209231347454">"समावेश गरिएको छ"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"प्रावधान मिलाइएको छैन"</item>
+ <item msgid="7598231293776486217">"प्रावधान मिलाइएको छ"</item>
+ <item msgid="3720547957514534185">"आवश्यक छैन"</item>
+ <item msgid="1264673582354896949">"प्रक्रियामा छ"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-nl/strings.xml b/testapps/TestServerApp/app/src/main/res/values-nl/strings.xml
new file mode 100644
index 0000000..847dfca
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-nl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Instellingen"</string>
+ <string name="server_running" msgid="2780193626090379172">"Server actief..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Server stoppen"</string>
+ <string name="server_down" msgid="1030249207496490556">"Server offline"</string>
+ <string name="start_server" msgid="3878573341408591975">"Server starten"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Uit"</item>
+ <item msgid="3193389681837907872">"Aan"</item>
+ <item msgid="3124590179479393815">"Niet geschikt"</item>
+ <item msgid="1606753456265236910">"Registratie"</item>
+ <item msgid="3930807209231347454">"Opgenomen"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Niet geregistreerd"</item>
+ <item msgid="7598231293776486217">"Geregistreerd"</item>
+ <item msgid="3720547957514534185">"Niet vereist"</item>
+ <item msgid="1264673582354896949">"In behandeling"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-pa/strings.xml b/testapps/TestServerApp/app/src/main/res/values-pa/strings.xml
new file mode 100644
index 0000000..f4509fc
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-pa/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"ਸੈਟਿੰਗਾਂ"</string>
+ <string name="server_running" msgid="2780193626090379172">"ਸਰਵਰ ਚੱਲ ਰਿਹਾ ਹੈ..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"ਸਰਵਰ ਬੰਦ ਕਰੋ"</string>
+ <string name="server_down" msgid="1030249207496490556">"ਸਰਵਰ ਨਹੀਂ ਚੱਲ ਰਿਹਾ"</string>
+ <string name="start_server" msgid="3878573341408591975">"ਸਰਵਰ ਚਾਲੂ ਕਰੋ"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"ਬੰਦ"</item>
+ <item msgid="3193389681837907872">"ਚਾਲੂ"</item>
+ <item msgid="3124590179479393815">"ਗੈਰ-ਅਨੁਰੂਪ"</item>
+ <item msgid="1606753456265236910">"ਪ੍ਰਬੰਧਿਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</item>
+ <item msgid="3930807209231347454">"ਸ਼ਾਮਲ"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"ਪ੍ਰਬੰਧਿਤ ਨਹੀਂ ਹੈ"</item>
+ <item msgid="7598231293776486217">"ਪ੍ਰਬੰਧਿਤ ਹੈ"</item>
+ <item msgid="3720547957514534185">"ਲੋੜੀਂਦਾ ਨਹੀਂ"</item>
+ <item msgid="1264673582354896949">"ਜਾਰੀ ਹੈ"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-pl/strings.xml b/testapps/TestServerApp/app/src/main/res/values-pl/strings.xml
new file mode 100644
index 0000000..4a13003
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-pl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Ustawienia"</string>
+ <string name="server_running" msgid="2780193626090379172">"Serwer działa…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"ZatrzymaJ serwer"</string>
+ <string name="server_down" msgid="1030249207496490556">"Serwer nie działa"</string>
+ <string name="start_server" msgid="3878573341408591975">"Uruchom serwer"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Wyłączono"</item>
+ <item msgid="3193389681837907872">"Włączono"</item>
+ <item msgid="3124590179479393815">"Brak zgodności"</item>
+ <item msgid="1606753456265236910">"Obsługa administracyjna"</item>
+ <item msgid="3930807209231347454">"Dostępne"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Nieobsługiwane"</item>
+ <item msgid="7598231293776486217">"Obsługiwane"</item>
+ <item msgid="3720547957514534185">"Niewymagane"</item>
+ <item msgid="1264673582354896949">"W toku"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-pt-rPT/strings.xml b/testapps/TestServerApp/app/src/main/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..e61c84d
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-pt-rPT/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Definições"</string>
+ <string name="server_running" msgid="2780193626090379172">"O servidor está em execução…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Parar servidor"</string>
+ <string name="server_down" msgid="1030249207496490556">"O servidor está indisponível"</string>
+ <string name="start_server" msgid="3878573341408591975">"Iniciar servidor"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Desativado"</item>
+ <item msgid="3193389681837907872">"Ativado"</item>
+ <item msgid="3124590179479393815">"Incompatível"</item>
+ <item msgid="1606753456265236910">"A aprovisionar"</item>
+ <item msgid="3930807209231347454">"Incluído"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Não aprovisionado"</item>
+ <item msgid="7598231293776486217">"Aprovisionado"</item>
+ <item msgid="3720547957514534185">"Não obrigatório"</item>
+ <item msgid="1264673582354896949">"Em curso"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-pt/strings.xml b/testapps/TestServerApp/app/src/main/res/values-pt/strings.xml
new file mode 100644
index 0000000..1d2310d
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-pt/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Configurações"</string>
+ <string name="server_running" msgid="2780193626090379172">"O servidor está em execução..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Interromper servidor"</string>
+ <string name="server_down" msgid="1030249207496490556">"O servidor está fora do ar."</string>
+ <string name="start_server" msgid="3878573341408591975">"Iniciar servidor"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Desativado"</item>
+ <item msgid="3193389681837907872">"Ativado"</item>
+ <item msgid="3124590179479393815">"Incompatível"</item>
+ <item msgid="1606753456265236910">"Provisionando"</item>
+ <item msgid="3930807209231347454">"Incluso"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Não provisionado"</item>
+ <item msgid="7598231293776486217">"Provisionado"</item>
+ <item msgid="3720547957514534185">"Não obrigatório"</item>
+ <item msgid="1264673582354896949">"Em andamento"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-ro/strings.xml b/testapps/TestServerApp/app/src/main/res/values-ro/strings.xml
new file mode 100644
index 0000000..a84085a
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-ro/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Setări"</string>
+ <string name="server_running" msgid="2780193626090379172">"Serverul rulează..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Oprește serverul"</string>
+ <string name="server_down" msgid="1030249207496490556">"Serverul nu funcționează"</string>
+ <string name="start_server" msgid="3878573341408591975">"Pornește serverul"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Dezactivat"</item>
+ <item msgid="3193389681837907872">"Activat"</item>
+ <item msgid="3124590179479393815">"Incompatibil"</item>
+ <item msgid="1606753456265236910">"Configurarea accesului pentru utilizatori"</item>
+ <item msgid="3930807209231347454">"Inclus"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Nu a fost configurat accesul"</item>
+ <item msgid="7598231293776486217">"A fost configurat accesul"</item>
+ <item msgid="3720547957514534185">"Nu este obligatoriu"</item>
+ <item msgid="1264673582354896949">"În desfășurare"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-ru/strings.xml b/testapps/TestServerApp/app/src/main/res/values-ru/strings.xml
new file mode 100644
index 0000000..1242f96
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-ru/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Настройки"</string>
+ <string name="server_running" msgid="2780193626090379172">"Сервер работает…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Остановить сервер"</string>
+ <string name="server_down" msgid="1030249207496490556">"Сервер не работает"</string>
+ <string name="start_server" msgid="3878573341408591975">"Запустить сервер"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Неактивно"</item>
+ <item msgid="3193389681837907872">"Активно"</item>
+ <item msgid="3124590179479393815">"Несовместимо"</item>
+ <item msgid="1606753456265236910">"Инициализация"</item>
+ <item msgid="3930807209231347454">"Включено"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Не подготовлено"</item>
+ <item msgid="7598231293776486217">"Подготовлено"</item>
+ <item msgid="3720547957514534185">"Необязательно"</item>
+ <item msgid="1264673582354896949">"На рассмотрении"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-si/strings.xml b/testapps/TestServerApp/app/src/main/res/values-si/strings.xml
new file mode 100644
index 0000000..47dba95
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-si/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"සැකසීම්"</string>
+ <string name="server_running" msgid="2780193626090379172">"සේවාදායකය ධාවනය වේ..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"සේවාදායකය නවත්වන්න"</string>
+ <string name="server_down" msgid="1030249207496490556">"සේවාදායකය බිඳ වැටී ඇත"</string>
+ <string name="start_server" msgid="3878573341408591975">"සේවාදායකය අරඹන්න"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"අබලයි"</item>
+ <item msgid="3193389681837907872">"සබලයි"</item>
+ <item msgid="3124590179479393815">"නොගැළපෙන"</item>
+ <item msgid="1606753456265236910">"ප්රතිපාදනය කරමින්"</item>
+ <item msgid="3930807209231347454">"ඇතුළත් වේ"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"ප්රතිපාදනය කර නැත"</item>
+ <item msgid="7598231293776486217">"ප්රතිපාදන ලත්"</item>
+ <item msgid="3720547957514534185">"අවශ්ය නැත"</item>
+ <item msgid="1264673582354896949">"ප්රගතියේ පවතී"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-sk/strings.xml b/testapps/TestServerApp/app/src/main/res/values-sk/strings.xml
new file mode 100644
index 0000000..ca4bd55
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-sk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Nastavenia"</string>
+ <string name="server_running" msgid="2780193626090379172">"Server je spustený…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Zastaviť server"</string>
+ <string name="server_down" msgid="1030249207496490556">"Server je nedostupný"</string>
+ <string name="start_server" msgid="3878573341408591975">"Spustiť server"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Vypnuté"</item>
+ <item msgid="3193389681837907872">"Zapnuté"</item>
+ <item msgid="3124590179479393815">"Nekompatibilné"</item>
+ <item msgid="1606753456265236910">"Poskytovanie"</item>
+ <item msgid="3930807209231347454">"Zahrnuté"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Neposkytuje sa"</item>
+ <item msgid="7598231293776486217">"Poskytuje sa"</item>
+ <item msgid="3720547957514534185">"Nepovinné"</item>
+ <item msgid="1264673582354896949">"Spracúva sa"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-sl/strings.xml b/testapps/TestServerApp/app/src/main/res/values-sl/strings.xml
new file mode 100644
index 0000000..570e6e2
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-sl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Nastavitve"</string>
+ <string name="server_running" msgid="2780193626090379172">"Strežnik se izvaja ..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Ustavi strežnik"</string>
+ <string name="server_down" msgid="1030249207496490556">"Strežnik ne deluje"</string>
+ <string name="start_server" msgid="3878573341408591975">"Zaženi strežnik"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Onemogočeno"</item>
+ <item msgid="3193389681837907872">"Omogočeno"</item>
+ <item msgid="3124590179479393815">"Nezdružljivo"</item>
+ <item msgid="1606753456265236910">"Omogočanje uporabe"</item>
+ <item msgid="3930807209231347454">"Vključeno"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Uporaba ni omogočena"</item>
+ <item msgid="7598231293776486217">"Omogočeno"</item>
+ <item msgid="3720547957514534185">"Ni zahtevano"</item>
+ <item msgid="1264673582354896949">"Poteka"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-sq/strings.xml b/testapps/TestServerApp/app/src/main/res/values-sq/strings.xml
new file mode 100644
index 0000000..fbcd4da
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-sq/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Cilësimet"</string>
+ <string name="server_running" msgid="2780193626090379172">"Serveri është në ekzekutim..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Ndalo serverin"</string>
+ <string name="server_down" msgid="1030249207496490556">"Serveri nuk po funksionon"</string>
+ <string name="start_server" msgid="3878573341408591975">"Nis serverin"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Çaktivizuar"</item>
+ <item msgid="3193389681837907872">"Aktivizuar"</item>
+ <item msgid="3124590179479393815">"I papërputhshëm"</item>
+ <item msgid="1606753456265236910">"Po përgatitet"</item>
+ <item msgid="3930807209231347454">"Përfshirë"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Nuk është përgatitur"</item>
+ <item msgid="7598231293776486217">"Përgatitur"</item>
+ <item msgid="3720547957514534185">"Nuk kërkohet"</item>
+ <item msgid="1264673582354896949">"Në vazhdim"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-sr/strings.xml b/testapps/TestServerApp/app/src/main/res/values-sr/strings.xml
new file mode 100644
index 0000000..e8fc322
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-sr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Подешавања"</string>
+ <string name="server_running" msgid="2780193626090379172">"Сервер је покренут…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Заустави сервер"</string>
+ <string name="server_down" msgid="1030249207496490556">"Сервер је пао"</string>
+ <string name="start_server" msgid="3878573341408591975">"Покрени сервер"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Онемогућено"</item>
+ <item msgid="3193389681837907872">"Омогућено"</item>
+ <item msgid="3124590179479393815">"Некомпатибилно"</item>
+ <item msgid="1606753456265236910">"Додељује се"</item>
+ <item msgid="3930807209231347454">"Уврштено"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Није додељено"</item>
+ <item msgid="7598231293776486217">"Додељено"</item>
+ <item msgid="3720547957514534185">"Није обавезно"</item>
+ <item msgid="1264673582354896949">"У току"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-sv/strings.xml b/testapps/TestServerApp/app/src/main/res/values-sv/strings.xml
new file mode 100644
index 0000000..8470695
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-sv/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Inställningar"</string>
+ <string name="server_running" msgid="2780193626090379172">"Servern körs …"</string>
+ <string name="stop_server" msgid="6192029827529013598">"Stoppa servern"</string>
+ <string name="server_down" msgid="1030249207496490556">"Servern ligger nere"</string>
+ <string name="start_server" msgid="3878573341408591975">"Starta servern"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Inaktiverad"</item>
+ <item msgid="3193389681837907872">"Aktiverad"</item>
+ <item msgid="3124590179479393815">"Ej kompatibel"</item>
+ <item msgid="1606753456265236910">"Certifikaten installeras"</item>
+ <item msgid="3930807209231347454">"Inkluderat"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Ej administrerad"</item>
+ <item msgid="7598231293776486217">"Administrerad"</item>
+ <item msgid="3720547957514534185">"Ej obligatorisk"</item>
+ <item msgid="1264673582354896949">"Pågår"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-sw/strings.xml b/testapps/TestServerApp/app/src/main/res/values-sw/strings.xml
new file mode 100644
index 0000000..c1155b8
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-sw/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Mipangilio"</string>
+ <string name="server_running" msgid="2780193626090379172">"Seva inatekeleza..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Sitisha Seva"</string>
+ <string name="server_down" msgid="1030249207496490556">"Seva iko chini"</string>
+ <string name="start_server" msgid="3878573341408591975">"Washa Seva"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Imezimwa"</item>
+ <item msgid="3193389681837907872">"Imewashwa"</item>
+ <item msgid="3124590179479393815">"Haioani"</item>
+ <item msgid="1606753456265236910">"Inaandaa"</item>
+ <item msgid="3930807209231347454">"Imejumuishwa"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Haijatolewa"</item>
+ <item msgid="7598231293776486217">"Imetolewa"</item>
+ <item msgid="3720547957514534185">"Haihitajiki"</item>
+ <item msgid="1264673582354896949">"Inaendelea"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-ta/strings.xml b/testapps/TestServerApp/app/src/main/res/values-ta/strings.xml
new file mode 100644
index 0000000..adad427
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-ta/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"அமைப்புகள்"</string>
+ <string name="server_running" msgid="2780193626090379172">"சேவையகம் இயக்கத்தில் உள்ளது..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"சேவையகத்தை நிறுத்து"</string>
+ <string name="server_down" msgid="1030249207496490556">"சேவையகம் இயங்கவில்லை"</string>
+ <string name="start_server" msgid="3878573341408591975">"சேவையகத்தைத் தொடங்கு"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"முடக்கப்பட்டது"</item>
+ <item msgid="3193389681837907872">"இயக்கப்பட்டது"</item>
+ <item msgid="3124590179479393815">"இணக்கமற்றது"</item>
+ <item msgid="1606753456265236910">"அமைக்கிறது"</item>
+ <item msgid="3930807209231347454">"சேர்க்கப்பட்டது"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"அமைக்கப்படவில்லை"</item>
+ <item msgid="7598231293776486217">"அமைக்கப்பட்டது"</item>
+ <item msgid="3720547957514534185">"அவசியமில்லை"</item>
+ <item msgid="1264673582354896949">"செயலிலுள்ளது"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-te/strings.xml b/testapps/TestServerApp/app/src/main/res/values-te/strings.xml
new file mode 100644
index 0000000..39cc2fe
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-te/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"టెస్ట్సర్వర్యాప్"</string>
+ <string name="action_settings" msgid="1335152369747372374">"సెట్టింగ్లు"</string>
+ <string name="server_running" msgid="2780193626090379172">"సర్వర్ రన్ అవుతోంది..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"సర్వర్ను ఆపివేయండి"</string>
+ <string name="server_down" msgid="1030249207496490556">"సర్వర్ డౌన్ అయింది"</string>
+ <string name="start_server" msgid="3878573341408591975">"సర్వర్ను ప్రారంభించండి"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"డిజేబుల్ చేయబడింది"</item>
+ <item msgid="3193389681837907872">"ఎనేబుల్ చేయబడింది"</item>
+ <item msgid="3124590179479393815">"అనుకూలంగా లేదు"</item>
+ <item msgid="1606753456265236910">"కేటాయిస్తోంది"</item>
+ <item msgid="3930807209231347454">"చేర్చబడింది"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"కేటాయించలేదు"</item>
+ <item msgid="7598231293776486217">"కేటాయించబడింది"</item>
+ <item msgid="3720547957514534185">"అవసరం లేదు"</item>
+ <item msgid="1264673582354896949">"ప్రోగ్రెస్లో ఉంది"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-th/strings.xml b/testapps/TestServerApp/app/src/main/res/values-th/strings.xml
new file mode 100644
index 0000000..78232ca
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-th/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"แอปเซิร์ฟเวอร์ทดสอบ"</string>
+ <string name="action_settings" msgid="1335152369747372374">"การตั้งค่า"</string>
+ <string name="server_running" msgid="2780193626090379172">"เซิร์ฟเวอร์กำลังทำงาน..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"หยุดเซิร์ฟเวอร์"</string>
+ <string name="server_down" msgid="1030249207496490556">"เซิร์ฟเวอร์ขัดข้อง"</string>
+ <string name="start_server" msgid="3878573341408591975">"เริ่มต้นเซิร์ฟเวอร์"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"ปิดใช้อยู่"</item>
+ <item msgid="3193389681837907872">"เปิดใช้อยู่"</item>
+ <item msgid="3124590179479393815">"ใช้งานร่วมกันไม่ได้"</item>
+ <item msgid="1606753456265236910">"การจัดสรร"</item>
+ <item msgid="3930807209231347454">"รวมอยู่ด้วย"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"ยังไม่ได้จัดสรร"</item>
+ <item msgid="7598231293776486217">"จัดสรรแล้ว"</item>
+ <item msgid="3720547957514534185">"ไม่บังคับ"</item>
+ <item msgid="1264673582354896949">"อยู่ในระหว่างดำเนินการ"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-ur/strings.xml b/testapps/TestServerApp/app/src/main/res/values-ur/strings.xml
new file mode 100644
index 0000000..5df75a7
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-ur/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"ترتیبات"</string>
+ <string name="server_running" msgid="2780193626090379172">"سرور چل رہا ہے..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"سرور روکیں"</string>
+ <string name="server_down" msgid="1030249207496490556">"سرور ڈاؤن ہے"</string>
+ <string name="start_server" msgid="3878573341408591975">"سرور شروع کریں"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"غیر فعال ہے"</item>
+ <item msgid="3193389681837907872">"فعال ہے"</item>
+ <item msgid="3124590179479393815">"غیر مطابقت پذیر"</item>
+ <item msgid="1606753456265236910">"فراہمی"</item>
+ <item msgid="3930807209231347454">"شامل ہے"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"فراہم نہیں کیا گا"</item>
+ <item msgid="7598231293776486217">"فراہم کیا گیا"</item>
+ <item msgid="3720547957514534185">"غیر مطلوب"</item>
+ <item msgid="1264673582354896949">"پیشرفت میں ہے"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-vi/strings.xml b/testapps/TestServerApp/app/src/main/res/values-vi/strings.xml
new file mode 100644
index 0000000..78bd3d8
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-vi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Cài đặt"</string>
+ <string name="server_running" msgid="2780193626090379172">"Máy chủ đang chạy..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Dừng máy chủ"</string>
+ <string name="server_down" msgid="1030249207496490556">"Máy chủ không hoạt động"</string>
+ <string name="start_server" msgid="3878573341408591975">"Khởi động máy chủ"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Đã tắt"</item>
+ <item msgid="3193389681837907872">"Đã bật"</item>
+ <item msgid="3124590179479393815">"Không tương thích"</item>
+ <item msgid="1606753456265236910">"Đang cung cấp"</item>
+ <item msgid="3930807209231347454">"Đã bao gồm"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Chưa được cung cấp"</item>
+ <item msgid="7598231293776486217">"Đã cung cấp"</item>
+ <item msgid="3720547957514534185">"Không bắt buộc"</item>
+ <item msgid="1264673582354896949">"Đang xử lý"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-zh-rCN/strings.xml b/testapps/TestServerApp/app/src/main/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..6e26819
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-zh-rCN/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"设置"</string>
+ <string name="server_running" msgid="2780193626090379172">"服务器正在运行…"</string>
+ <string name="stop_server" msgid="6192029827529013598">"停止服务器"</string>
+ <string name="server_down" msgid="1030249207496490556">"服务器出现故障"</string>
+ <string name="start_server" msgid="3878573341408591975">"启动服务器"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"已停用"</item>
+ <item msgid="3193389681837907872">"已启用"</item>
+ <item msgid="3124590179479393815">"不兼容"</item>
+ <item msgid="1606753456265236910">"正在配置"</item>
+ <item msgid="3930807209231347454">"已包含"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"未配置"</item>
+ <item msgid="7598231293776486217">"已配置"</item>
+ <item msgid="3720547957514534185">"不需要"</item>
+ <item msgid="1264673582354896949">"进行中"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestServerApp/app/src/main/res/values-zu/strings.xml b/testapps/TestServerApp/app/src/main/res/values-zu/strings.xml
new file mode 100644
index 0000000..fbb6262
--- /dev/null
+++ b/testapps/TestServerApp/app/src/main/res/values-zu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="2894617184221823208">"I-TestServerApp"</string>
+ <string name="action_settings" msgid="1335152369747372374">"Amasethingi"</string>
+ <string name="server_running" msgid="2780193626090379172">"Iseva iyaqhubeka..."</string>
+ <string name="stop_server" msgid="6192029827529013598">"Misa Iseva"</string>
+ <string name="server_down" msgid="1030249207496490556">"Iseva iphansi"</string>
+ <string name="start_server" msgid="3878573341408591975">"Qalisa Iseva"</string>
+ <string-array name="entitlement_status">
+ <item msgid="5560300387618996934">"Kukhutshaziwe"</item>
+ <item msgid="3193389681837907872">"Kunikwe amandla"</item>
+ <item msgid="3124590179479393815">"Ayisebenzisani"</item>
+ <item msgid="1606753456265236910">"Iyahlinzeka"</item>
+ <item msgid="3930807209231347454">"Kuhlanganisiwe"</item>
+ </string-array>
+ <string-array name="provision_status">
+ <item msgid="3486273747926710021">"Akulungiselelwanga"</item>
+ <item msgid="7598231293776486217">"Kulungiselelwe"</item>
+ <item msgid="3720547957514534185">"Akudingekile"</item>
+ <item msgid="1264673582354896949">"Kuyaqhubeka"</item>
+ </string-array>
+</resources>
diff --git a/testapps/TestSliceApp/.idea/gradle.xml b/testapps/TestSliceApp/.idea/gradle.xml
index 526b4c2..a2d7c21 100644
--- a/testapps/TestSliceApp/.idea/gradle.xml
+++ b/testapps/TestSliceApp/.idea/gradle.xml
@@ -13,7 +13,6 @@
<option value="$PROJECT_DIR$/app" />
</set>
</option>
- <option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
diff --git a/testapps/TestSliceApp/.idea/misc.xml b/testapps/TestSliceApp/.idea/misc.xml
index a329266..7c85865 100644
--- a/testapps/TestSliceApp/.idea/misc.xml
+++ b/testapps/TestSliceApp/.idea/misc.xml
@@ -1,68 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
- <component name="DesignSurface">
- <option name="filePathToZoomLevelMap">
- <map>
- <entry key="app/src/main/res/drawable/ic_launcher_background.xml" value="0.38177083333333334" />
- <entry key="app/src/main/res/layout/_copy.xml" value="0.365625" />
- <entry key="app/src/main/res/layout/activity_main.xml" value="0.4891304347826087" />
- <entry key="app/src/main/res/layout/copy.xml" value="0.37135416666666665" />
- <entry key="app/src/main/res/layout/fragment_c_b_s.xml" value="0.473731884057971" />
- <entry key="app/src/main/res/layout/fragment_c_b_s_copy.xml" value="0.365625" />
- <entry key="app/src/main/res/layout/fragment_main.xml" value="0.46693840579710144" />
- <entry key="app/src/main/res/layout/fragment_prioritize_bandwidth.xml" value="0.473731884057971" />
- <entry key="app/src/main/res/layout/fragment_prioritize_bandwidth2.xml" value="0.365625" />
- <entry key="app/src/main/res/layout/fragment_prioritize_latency.xml" value="0.473731884057971" />
- <entry key="app/src/main/res/layout/fragment_prioritize_latency2.xml" value="0.365625" />
- </map>
- </option>
- </component>
+ <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="androidx.annotation.Nullable" />
<option name="myDefaultNotNull" value="androidx.annotation.NonNull" />
<option name="myNullables">
<value>
- <list size="17">
- <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
- <item index="1" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
- <item index="2" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
- <item index="3" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
- <item index="4" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
- <item index="5" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
- <item index="6" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
- <item index="7" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
- <item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
- <item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
- <item index="10" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
- <item index="11" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
- <item index="12" class="java.lang.String" itemvalue="io.reactivex.annotations.Nullable" />
- <item index="13" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.Nullable" />
- <item index="14" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
- <item index="15" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
- <item index="16" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
+ <list size="15">
+ <item index="0" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
+ <item index="1" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
+ <item index="2" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
+ <item index="3" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
+ <item index="4" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
+ <item index="5" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
+ <item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
+ <item index="7" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
+ <item index="8" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
+ <item index="9" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
+ <item index="10" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
+ <item index="11" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
+ <item index="12" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
+ <item index="13" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
+ <item index="14" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
- <list size="17">
- <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
- <item index="1" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
- <item index="2" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
+ <list size="14">
+ <item index="0" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
+ <item index="1" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
+ <item index="2" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
- <item index="4" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
- <item index="5" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
- <item index="6" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
- <item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
- <item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
- <item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
+ <item index="4" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
+ <item index="5" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
+ <item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
+ <item index="7" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
+ <item index="8" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
+ <item index="9" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="10" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" />
- <item index="11" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" />
- <item index="12" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" />
- <item index="13" class="java.lang.String" itemvalue="lombok.NonNull" />
- <item index="14" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
- <item index="15" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
- <item index="16" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
+ <item index="11" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
+ <item index="12" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
+ <item index="13" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
</list>
</value>
</option>
diff --git a/testapps/TestSliceApp/app/src/main/AndroidManifest.xml b/testapps/TestSliceApp/app/src/main/AndroidManifest.xml
index d28bbb0..cea9e5f 100644
--- a/testapps/TestSliceApp/app/src/main/AndroidManifest.xml
+++ b/testapps/TestSliceApp/app/src/main/AndroidManifest.xml
@@ -3,6 +3,9 @@
package="com.google.android.sample.testsliceapp">
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.READ_BASIC_PHONE_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
@@ -10,7 +13,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat"
- android:versionCode="34">
+ android:versionCode="34"
+ android:usesCleartextTraffic="true">
<activity
android:name=".MainActivity"
android:exported="true">
@@ -28,5 +32,7 @@
<meta-data android:name="android.service.carrier.LONG_LIVED_BINDING"
android:value="true" />
</service>
+ <property android:name="android.net.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES"
+ android:resource="@xml/self_certified_network_capabilities_both" />
</application>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/CBS.java b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/CBS.java
index a555ce6..c85f830 100644
--- a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/CBS.java
+++ b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/CBS.java
@@ -20,7 +20,10 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.TelephonyNetworkSpecifier;
import android.os.Bundle;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -62,6 +65,7 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mConnectivityManager = getContext().getSystemService(ConnectivityManager.class);
}
@Override
@@ -80,23 +84,33 @@
mRelease.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
- mConnectivityManager.unregisterNetworkCallback(
+ try {
+ mConnectivityManager.unregisterNetworkCallback(
mProfileCheckNetworkCallback);
+ } catch (Exception e) {
+ Log.d("SliceTest", "Exception: " + e);
+ }
}
});
mRequest = view.findViewById(R.id.requestcbs);
mRequest.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
- NetworkCallback mProfileCheckNetworkCallback = new NetworkCallback() {
+ mProfileCheckNetworkCallback = new NetworkCallback() {
@Override
public void onAvailable(final Network network) {
mNetwork = network;
+ Log.d("CBS", "onAvailable + " + network);
}
};
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
+ builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ builder.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(subId).build());
mConnectivityManager.requestNetwork(builder.build(), mProfileCheckNetworkCallback);
+ Log.d("CBS", "onClick + " + builder.build());
}
});
mPing = view.findViewById(R.id.pingcbs);
@@ -106,8 +120,9 @@
if (mNetwork != null) {
//mNetwork.
try {
- new RequestTask().ping(mNetwork);
+ new RequestTask().execute(mNetwork);
} catch (Exception e) {
+ Log.d("SliceTest", "Exception: " + e);
}
}
}
diff --git a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeBandwidth.java b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeBandwidth.java
index d997178..6812ddc 100644
--- a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeBandwidth.java
+++ b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeBandwidth.java
@@ -21,6 +21,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -73,6 +74,7 @@
new NetworkCallback() {
@Override
public void onAvailable(final Network network) {
+ Log.d("SliceTest", "onAvailable: " + network);
mNetwork = network;
}
};
@@ -80,23 +82,30 @@
mRelease.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
- mConnectivityManager.unregisterNetworkCallback(mProfileCheckNetworkCallback);
+ try {
+ mConnectivityManager.unregisterNetworkCallback(
+ mProfileCheckNetworkCallback);
+ } catch (Exception e) {
+ Log.d("SliceTest", "Exception: " + e);
+ }
}
});
mRequest = view.findViewById(R.id.requestbw);
mRequest.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
- NetworkCallback mProfileCheckNetworkCallback =
+ mProfileCheckNetworkCallback =
new NetworkCallback() {
@Override
public void onAvailable(final Network network) {
+ Log.d("PrioritizeBandwidth", "onAvailable + " + network);
mNetwork = network;
}
};
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH);
mConnectivityManager.requestNetwork(builder.build(), mProfileCheckNetworkCallback);
+ Log.d("PrioritizeBandwidth", "onClick + " + builder.build());
}
});
mPing = view.findViewById(R.id.pingbw);
@@ -104,10 +113,10 @@
@Override
public void onClick(View view) {
if (mNetwork != null) {
- //mNetwork.
try {
- new RequestTask().ping(mNetwork);
+ new RequestTask().execute(mNetwork);
} catch (Exception e) {
+ Log.d("SliceTest", "Exception: " + e);
}
}
}
diff --git a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java
index b45362c..a74e7f0 100644
--- a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java
+++ b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java
@@ -15,31 +15,64 @@
*/
package com.google.android.sample.testsliceapp;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED;
+
+import android.annotation.TargetApi;
+import android.content.Context;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Bundle;
+import android.telephony.TelephonyManager;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.TextView;
import androidx.fragment.app.Fragment;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
/**
* A simple {@link Fragment} subclass. Use the {@link PrioritizeLatency#newInstance} factory method
* to create an instance of this fragment.
*/
public class PrioritizeLatency extends Fragment {
- Button mRelease, mRequest, mPing;
- Network mNetwork;
+ Button mPurchase, mNetworkRequestRelease, mPing;
+ TextView mResultTextView;
+ Network mNetwork = null;
ConnectivityManager mConnectivityManager;
NetworkCallback mProfileCheckNetworkCallback;
+ TelephonyManager mTelephonyManager;
+ Context mContext;
+ private final ExecutorService mFixedThreadPool = Executors.newFixedThreadPool(3);
+
+ private static final String LOG_TAG = "PrioritizeLatency";
+ private static final int TIMEOUT_FOR_PURCHASE = 5 * 60; // 5 minutes
+
public PrioritizeLatency() {
- // Required empty public constructor
+ // Required empty public constructor
}
/**
@@ -61,6 +94,9 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mContext = getContext();
+ mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
}
@Override
@@ -68,48 +104,205 @@
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_prioritize_latency, container, false);
- mProfileCheckNetworkCallback =
- new NetworkCallback() {
- @Override
- public void onAvailable(final Network network) {
- mNetwork = network;
- }
- };
- mRelease = view.findViewById(R.id.releaselatency);
- mRelease.setOnClickListener(new OnClickListener() {
+ mResultTextView = view.findViewById(R.id.resultTextView);
+
+ mPurchase = view.findViewById(R.id.purchaseButton);
+ mPurchase.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
- mConnectivityManager.unregisterNetworkCallback(mProfileCheckNetworkCallback);
+ Log.d(LOG_TAG, "Clicking purchase button");
+ onPurchaseButtonClick();
}
});
- mRequest = view.findViewById(R.id.requestlatency);
- mRequest.setOnClickListener(new OnClickListener() {
+
+ mNetworkRequestRelease = view.findViewById(R.id.requestReleaseButton);
+ mNetworkRequestRelease.setEnabled(false);
+ mNetworkRequestRelease.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
- NetworkCallback mProfileCheckNetworkCallback = new NetworkCallback() {
- @Override
- public void onAvailable(final Network network) {
- mNetwork = network;
- }
- };
- NetworkRequest.Builder builder = new NetworkRequest.Builder();
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY);
- mConnectivityManager.requestNetwork(builder.build(), mProfileCheckNetworkCallback);
+ Log.d(LOG_TAG, "Clicking Request/Release Network button");
+ onNetworkRequestReleaseClick();
}
});
+
mPing = view.findViewById(R.id.pinglatency);
+ mPing.setEnabled(false);
mPing.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
+ Log.d(LOG_TAG, "Clicking Ping button");
if (mNetwork != null) {
- //mNetwork.
- try {
- new RequestTask().ping(mNetwork);
- } catch (Exception e) {
- }
+ mFixedThreadPool.execute(() -> {
+ try {
+ RequestTask requestTask = new RequestTask();
+ requestTask.ping(mNetwork);
+ updateResultTextView("Result: Ping is done successfully!");
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Exception at ping: " + e);
+ updateResultTextView("Result: Got exception with ping!!!");
+ }
+ });
}
}
});
return view;
}
+
+ private void onNetworkRequestReleaseClick() {
+ if (mNetwork == null) {
+ mProfileCheckNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onAvailable(final Network network) {
+ Log.d(LOG_TAG, "onAvailable + " + network);
+ mNetwork = network;
+ updateUIOnNetworkAvailable();
+ }
+ };
+ NetworkRequest.Builder builder = new NetworkRequest.Builder();
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY);
+ mConnectivityManager.requestNetwork(builder.build(),
+ mProfileCheckNetworkCallback);
+ Log.d(LOG_TAG, "Network Request/Release onClick + " + builder.build());
+ mResultTextView.setText(R.string.network_requested);
+ } else {
+ try {
+ mConnectivityManager.unregisterNetworkCallback(
+ mProfileCheckNetworkCallback);
+ mNetwork = null;
+ mNetworkRequestRelease.setText(R.string.request_network);
+ mResultTextView.setText(R.string.network_released);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Exception when releasing network: " + e);
+ mResultTextView.setText(R.string.network_release_failed);
+ }
+ }
+ }
+
+ @TargetApi(34)
+ private void onPurchaseButtonClick() {
+ try {
+ if (mTelephonyManager.isPremiumCapabilityAvailableForPurchase(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY)) {
+ LinkedBlockingQueue<Integer> purchaseRequest = new LinkedBlockingQueue<>(1);
+
+ // Try to purchase the capability
+ mTelephonyManager.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY,
+ mFixedThreadPool, purchaseRequest::offer);
+ mResultTextView.setText(R.string.purchase_in_progress);
+
+ mFixedThreadPool.execute(() -> {
+ try {
+ Integer result = purchaseRequest.poll(
+ TIMEOUT_FOR_PURCHASE, TimeUnit.SECONDS);
+ if (result == null) {
+ updateResultTextView(R.string.purchase_empty_result);
+ Log.d(LOG_TAG, "Got null result at purchasePremiumCapability");
+ return;
+ }
+
+ String purchaseResultText = "Result: "
+ + purchasePremiumResultToText(result.intValue());
+ updateResultTextView(purchaseResultText);
+ Log.d(LOG_TAG, purchaseResultText);
+
+ if (isPremiumCapacityAvailableForUse(result.intValue())) {
+ updateNetworkRequestReleaseButton(true);
+ }
+ } catch (InterruptedException e) {
+ Log.e(LOG_TAG, "InterruptedException at onPurchaseButtonClick: " + e);
+ updateResultTextView(R.string.purchase_exception);
+ }
+ });
+ } else {
+ mResultTextView.setText(R.string.premium_not_available);
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Exception when purchasing network premium: " + e);
+ mResultTextView.setText(R.string.purchase_exception);
+ }
+ }
+
+ private void updateNetworkRequestReleaseButton(boolean enabled) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mNetworkRequestRelease.setEnabled(enabled);
+ }
+ });
+ }
+
+ private void updateResultTextView(int status) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mResultTextView.setText(status);
+ }
+ });
+ }
+
+ private void updateResultTextView(String status) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mResultTextView.setText(status);
+ }
+ });
+ }
+
+ private void updateUIOnNetworkAvailable() {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mPing.setEnabled(true);
+ mNetworkRequestRelease.setText(R.string.release_network);
+ mResultTextView.setText(R.string.network_available);
+ }
+ });
+ }
+
+ private String purchasePremiumResultToText(int result) {
+ switch (result) {
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS:
+ return "Success";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED:
+ return "Throttled";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED:
+ return "Already purchased";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS:
+ return "Already in progress";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND:
+ return "Not foreground";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED:
+ return "User canceled";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED:
+ return "Carrier disabled";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR:
+ return "Carrier error";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT:
+ return "Timeout";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED:
+ return "Feature not supported";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE:
+ return "Network not available";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED:
+ return "Entitlement check failed";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION:
+ return "Not default data subscription";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP:
+ return "Pending network setup";
+ default:
+ String errorStr = "Unknown purchasing result " + result;
+ Log.e(LOG_TAG, errorStr);
+ return errorStr;
+ }
+ }
+
+ private boolean isPremiumCapacityAvailableForUse(int purchaseResult) {
+ if (purchaseResult == PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS
+ || purchaseResult == PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/RequestTask.java b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/RequestTask.java
index b12939e..569c066 100644
--- a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/RequestTask.java
+++ b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/RequestTask.java
@@ -16,24 +16,35 @@
package com.google.android.sample.testsliceapp;
import android.net.Network;
+import android.os.AsyncTask;
+import android.util.Log;
-import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.util.Scanner;
-class RequestTask{
+class RequestTask extends AsyncTask<Network, Integer, Integer> {
+ protected Integer doInBackground(Network... network) {
+ ping(network[0]);
+ return 0;
+ }
String ping(Network network) {
URL url = null;
try {
url = new URL("http://www.google.com");
} catch (Exception e) {
+ Log.d("SliceTest", "exception: " + e);
}
if (url != null) {
try {
- return httpGet(network, url);
+ Log.d("SliceTest", "ping " + url);
+ String result = httpGet(network, url);
+ Log.d("SliceTest", "result " + result);
+ return result;
} catch (Exception e) {
+ Log.d("SliceTest", "exception: " + e);
}
}
return "";
@@ -47,7 +58,9 @@
HttpURLConnection connection = (HttpURLConnection) network.openConnection(httpUrl);
try {
InputStream inputStream = connection.getInputStream();
- return new BufferedInputStream(inputStream).toString();
+ Log.d("httpGet", "httpUrl + " + httpUrl);
+ Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
+ return scanner.hasNext() ? scanner.next() : "";
} finally {
connection.disconnect();
}
diff --git a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/TestCarrierService.java b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/TestCarrierService.java
index b1d019e..daa1d17 100644
--- a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/TestCarrierService.java
+++ b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/TestCarrierService.java
@@ -21,6 +21,7 @@
import android.service.carrier.CarrierService;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
+import android.util.Log;
/**
* Carrier Service that sets the carrier config upon being bound by the system. Requires UICC
@@ -32,11 +33,13 @@
CarrierConfigManager cfgMgr =
(CarrierConfigManager) getSystemService(Context.CARRIER_CONFIG_SERVICE);
cfgMgr.notifyConfigChangedForSubId(SubscriptionManager.getDefaultSubscriptionId());
+ Log.d("TestCarrierService", "onCreate + ");
}
@Override
public PersistableBundle onLoadConfig(CarrierIdentifier carrierIdentifier) {
PersistableBundle config = new PersistableBundle();
+ Log.d("TestCarrierService", "onLoadConfig + ");
return config;
}
}
diff --git a/testapps/TestSliceApp/app/src/main/res/layout/activity_main.xml b/testapps/TestSliceApp/app/src/main/res/layout/activity_main.xml
index a723e6f..8a7d991 100644
--- a/testapps/TestSliceApp/app/src/main/res/layout/activity_main.xml
+++ b/testapps/TestSliceApp/app/src/main/res/layout/activity_main.xml
@@ -6,7 +6,6 @@
android:layout_height="match_parent"
tools:context=".MainActivity" >
<androidx.fragment.app.FragmentContainerView
- xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/frameLayoutMainFrag"
diff --git a/testapps/TestSliceApp/app/src/main/res/layout/fragment_c_b_s.xml b/testapps/TestSliceApp/app/src/main/res/layout/fragment_c_b_s.xml
index ac2ef9d..eff68ac 100644
--- a/testapps/TestSliceApp/app/src/main/res/layout/fragment_c_b_s.xml
+++ b/testapps/TestSliceApp/app/src/main/res/layout/fragment_c_b_s.xml
@@ -8,9 +8,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/frameLayoutCBS">
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
+<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/frameLayout3"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -19,30 +17,33 @@
android:id="@+id/textView3"
android:layout_width="342dp"
android:layout_height="49dp"
- android:text="CBS"
+ android:text="@string/cbs_title"
tools:layout_editor_absoluteX="19dp"
tools:layout_editor_absoluteY="7dp" />
<Button
android:id="@+id/requestcbs"
android:layout_width="186dp"
android:layout_height="57dp"
- android:text="Request Network"
- tools:layout_editor_absoluteX="120dp"
- tools:layout_editor_absoluteY="154dp" />
+ android:layout_marginTop="164dp"
+ android:text="@string/request_network"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:layout_editor_absoluteX="112dp" />
<Button
android:id="@+id/releasecbs"
android:layout_width="187dp"
android:layout_height="61dp"
- android:text="Release Network"
- tools:layout_editor_absoluteX="119dp"
- tools:layout_editor_absoluteY="273dp" />
+ android:layout_marginTop="124dp"
+ android:text="@string/release_network"
+ app:layout_constraintTop_toBottomOf="@+id/requestcbs"
+ tools:layout_editor_absoluteX="119dp" />
<Button
android:id="@+id/pingcbs"
android:layout_width="186dp"
android:layout_height="55dp"
- android:text="Ping"
- tools:layout_editor_absoluteX="120dp"
- tools:layout_editor_absoluteY="379dp" />
+ android:text="@string/ping"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/releasecbs"
+ tools:layout_editor_absoluteX="120dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
</RelativeLayout>
\ No newline at end of file
diff --git a/testapps/TestSliceApp/app/src/main/res/layout/fragment_main.xml b/testapps/TestSliceApp/app/src/main/res/layout/fragment_main.xml
index 11e95a8..5c12075 100644
--- a/testapps/TestSliceApp/app/src/main/res/layout/fragment_main.xml
+++ b/testapps/TestSliceApp/app/src/main/res/layout/fragment_main.xml
@@ -5,18 +5,17 @@
android:layout_height="match_parent"
tools:context=".MainFragment">
- <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/frameLayoutMain"
tools:context=".MainActivity">
<Button
android:id="@+id/cbs"
- android:layout_width="222dp"
- android:layout_height="51dp"
- android:text="CBS"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/cbs_title"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.493"
@@ -25,9 +24,9 @@
app:layout_constraintVertical_bias="0.751" />
<Button
android:id="@+id/latency"
- android:layout_width="222dp"
- android:layout_height="46dp"
- android:text="PRIORITIZE LATENCY"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/latency_title"
app:layout_constraintBottom_toTopOf="@+id/cbs"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
@@ -38,7 +37,7 @@
android:id="@+id/bw"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="PRIORITIZE BANDWIDTH"
+ android:text="@string/bw_title"
app:layout_constraintBottom_toTopOf="@+id/latency"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
diff --git a/testapps/TestSliceApp/app/src/main/res/layout/fragment_prioritize_bandwidth.xml b/testapps/TestSliceApp/app/src/main/res/layout/fragment_prioritize_bandwidth.xml
index 8933e7a..412ec39 100644
--- a/testapps/TestSliceApp/app/src/main/res/layout/fragment_prioritize_bandwidth.xml
+++ b/testapps/TestSliceApp/app/src/main/res/layout/fragment_prioritize_bandwidth.xml
@@ -8,9 +8,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/frameLayoutBW">
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
+<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/frameLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -19,7 +17,7 @@
android:id="@+id/textView2"
android:layout_width="371dp"
android:layout_height="52dp"
- android:text="Prioritize Bandwidth"
+ android:text="@string/bw_title"
tools:layout_editor_absoluteX="20dp"
tools:layout_editor_absoluteY="4dp" />
<Button
@@ -27,7 +25,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="64dp"
- android:text="Release Network"
+ android:text="@string/release_network"
app:layout_constraintBottom_toTopOf="@+id/requestbw"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -38,7 +36,7 @@
android:layout_width="182dp"
android:layout_height="42dp"
android:layout_marginBottom="228dp"
- android:text="Ping"
+ android:text="@string/ping"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.493"
@@ -48,7 +46,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="308dp"
- android:text="RequestNetwork"
+ android:text="@string/request_network"
app:layout_constraintBottom_toTopOf="@+id/pingbw"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
diff --git a/testapps/TestSliceApp/app/src/main/res/layout/fragment_prioritize_latency.xml b/testapps/TestSliceApp/app/src/main/res/layout/fragment_prioritize_latency.xml
index 9527d69..37a519f 100644
--- a/testapps/TestSliceApp/app/src/main/res/layout/fragment_prioritize_latency.xml
+++ b/testapps/TestSliceApp/app/src/main/res/layout/fragment_prioritize_latency.xml
@@ -8,51 +8,60 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/frameLayoutLatency">
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/frameLayout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".PrioritizeLatency" >
- <Button
- android:id="@+id/requestlatency"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="RequestNetwork"
- app:layout_constraintBottom_toTopOf="@+id/button6"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.461"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.717" />
- <Button
- android:id="@+id/releaselatency"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="76dp"
- android:text="Release Network"
- app:layout_constraintBottom_toTopOf="@+id/button7"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.478"
- app:layout_constraintStart_toStartOf="parent" />
- <Button
- android:id="@+id/pinglatency"
- android:layout_width="182dp"
- android:layout_height="42dp"
- android:layout_marginBottom="308dp"
- android:text="Ping"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.471"
- app:layout_constraintStart_toStartOf="parent" />
- <TextView
- android:id="@+id/textView"
- android:layout_width="371dp"
- android:layout_height="52dp"
- android:text="Prioritize Latency"
- tools:layout_editor_absoluteX="21dp"
- tools:layout_editor_absoluteY="1dp" />
-</androidx.constraintlayout.widget.ConstraintLayout>
- </FrameLayout>
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/frameLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".PrioritizeLatency">
+ <Button
+ android:id="@+id/purchaseButton"
+ android:layout_width="233dp"
+ android:layout_height="50dp"
+ android:layout_marginTop="176dp"
+ android:text="@string/purchase"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.495"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.717" />
+ <Button
+ android:id="@+id/requestReleaseButton"
+ android:layout_width="183dp"
+ android:layout_height="50dp"
+ android:layout_marginTop="84dp"
+ android:text="@string/request_network"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/purchaseButton"
+ app:layout_constraintVertical_bias="0.717" />
+ <Button
+ android:id="@+id/pinglatency"
+ android:layout_width="182dp"
+ android:layout_height="42dp"
+ android:layout_marginBottom="92dp"
+ android:text="@string/ping"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.493"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/requestReleaseButton" />
+ <TextView
+ android:id="@+id/resultTextView"
+ android:layout_width="283dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="84dp"
+ android:text="@string/result_prefix"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/pinglatency"
+ app:layout_constraintVertical_bias="0.717" />
+ <TextView
+ android:id="@+id/textView"
+ android:layout_width="371dp"
+ android:layout_height="52dp"
+ android:text="@string/latency_title"
+ tools:layout_editor_absoluteX="16dp"
+ tools:layout_editor_absoluteY="16dp" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </FrameLayout>
</RelativeLayout>
\ No newline at end of file
diff --git a/testapps/TestSliceApp/app/src/main/res/values-af/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-af/strings.xml
index d172f0f..5e2a27f 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-af/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-af/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hallo leë fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Versoek netwerk"</string>
+ <string name="release_network" msgid="174252378593535238">"Stel netwerk vry"</string>
+ <string name="ping" msgid="7890607576220714932">"Pieng"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Resultaat:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritiseer traagheid"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritiseer bandwydte"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Koop netwerkpremium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Resultaat: Die netwerk wat versoek is, is nou beskikbaar!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Resultaat: Die netwerk is versoek!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Resultaat: Die netwerk is vrygestel!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Resultaat: Kon nie die netwerk vrystel nie!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Resultaat: Uitsondering wanneer netwerkpremium gekoop word!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Resultaat: Het leë resultaat gekry toe netwerkpremium gekoop is!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Resultaat: Die netwerkpremium kan nie gekoop word nie!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Resultaat: Die netwerkpremium word tans gekoop …"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-am/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-am/strings.xml
index 229ff5d..ff5e8fa 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-am/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-am/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"አውታረ መረብ ይጠይቁ"</string>
+ <string name="release_network" msgid="174252378593535238">"አውታረ መረብ ይልቀቁ"</string>
+ <string name="ping" msgid="7890607576220714932">"ፒንግ"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"ውጤት፦"</string>
+ <string name="latency_title" msgid="963052613947017009">"ለስርዓተ ምላሽ ጊዜ ቅድሚያ ይስጡ"</string>
+ <string name="bw_title" msgid="3902162973688221344">"ለመተላለፊያ ይዘት ቅድሚያ ይስጡ"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"የአውታረ መረብ Premiumን ይግዙ"</string>
+ <string name="network_available" msgid="4780293262690730734">"ውጤት፦ የተጠየቀው አውታረ መረብ አሁን ይገኛል!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"ውጤት፦ አውታረ መረቡ ተጠይቋል!"</string>
+ <string name="network_released" msgid="2992280481133877025">"ውጤት፦ አውታረ መረቡ ተለቅቋል!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"ውጤት፦ አውታረ መረቡን መልቀቅ አልተሳካም!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"ውጤት፦የአውታረ መረብ premium በሚገዛበት ጊዜ ያለ ለየት ያለ!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"ውጤት፦የአውታረ መረብ premium በሚገዛበት ጊዜ ባዶ ውጤት ተገኝቷል!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"ውጤት፦የአውታረ መረቡ premium ለግዢ አይገኝም!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"ውጤት፦ የአውታረ መረብ premium ግዢ በሂደት ላይ ነው ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ar/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ar/strings.xml
index 0cadb81..25fa479 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ar/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ar/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"جزء فارغ للترحيب"</string>
+ <string name="request_network" msgid="8945235490804849914">"طلب الشبكة"</string>
+ <string name="release_network" msgid="174252378593535238">"إطلاق الإصدار"</string>
+ <string name="ping" msgid="7890607576220714932">"فحص الاتصال"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"النتيجة:"</string>
+ <string name="latency_title" msgid="963052613947017009">"منح الأولوية لوقت الاستجابة"</string>
+ <string name="bw_title" msgid="3902162973688221344">"منح الأولوية لمعدّل نقل البيانات"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"شراء الاشتراك المميّز في الشبكة"</string>
+ <string name="network_available" msgid="4780293262690730734">"النتيجة: الشبكة المطلوبة متاحة الآن."</string>
+ <string name="network_requested" msgid="5646123922691865991">"النتيجة: تم طلب الشبكة."</string>
+ <string name="network_released" msgid="2992280481133877025">"النتيجة: تم إطلاق الشبكة."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"النتيجة: تعذَّر إطلاق الشبكة."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"النتيجة: حدث استثناء عند شراء اشتراك مميّز في الشبكة."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"النتيجة: لقد حصلْت على نتيجة فارغة عند شراء اشتراك مميّز في الشبكة."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"النتيجة: لا تتوفّر إمكانية شراء الاشتراك المميّز في الشبكة."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"النتيجة: عملية شراء الاشتراك المميّز في الشبكة جارية ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-as/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-as/strings.xml
index 229ff5d..350d8dd 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-as/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-as/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"নেটৱৰ্কৰ বাবে অনুৰোধ কৰক"</string>
+ <string name="release_network" msgid="174252378593535238">"নেটৱৰ্ক মুকলি কৰক"</string>
+ <string name="ping" msgid="7890607576220714932">"পিং"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"ফলাফল:"</string>
+ <string name="latency_title" msgid="963052613947017009">"বিলম্বতাক অগ্ৰাধিকাৰ দিয়ক"</string>
+ <string name="bw_title" msgid="3902162973688221344">"বেণ্ডৱিথক অগ্ৰাধিকাৰ দিয়ক"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"নেটৱৰ্ক প্ৰিমিয়াম ক্ৰয় কৰক"</string>
+ <string name="network_available" msgid="4780293262690730734">"ফলাফল: অনুৰোধ কৰা নেটৱৰ্কটো বৰ্তমান উপলব্ধ নহয়!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"ফলাফল: নেটৱৰ্কটোৰ বাবে অনুৰোধ কৰা হৈছে!"</string>
+ <string name="network_released" msgid="2992280481133877025">"ফলাফল: নেটৱৰ্কটো মুকলি কৰি দিয়া হৈছে!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"ফলাফল: নেটৱৰ্কটো মুকলি কৰি দিয়াত বিফল হৈছে!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"ফলাফল: নেটৱৰ্ক প্ৰিমিয়াম ক্ৰয় কৰাৰ সময়ত ব্যতিক্ৰম!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"ফলাফল: নেটৱৰ্ক প্ৰিমিয়াম ক্ৰয় কৰাৰ সময়ত খালী ফলাফল পোৱা গৈছে!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"ফলাফল: ক্ৰয় কৰিবলৈ নেটৱৰ্ক প্ৰিমিয়াম উপলব্ধ নহয়!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"ফলাফল: নেটৱৰ্ক প্ৰিমিয়াম ক্ৰয় কৰাৰ প্ৰক্ৰিয়া চলি আছে ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-az/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-az/strings.xml
index 3702d56..8d09fb9 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-az/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-az/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Boş salamlama fraqmenti"</string>
+ <string name="request_network" msgid="8945235490804849914">"Şəbəkə sorğusu göndərin"</string>
+ <string name="release_network" msgid="174252378593535238">"Şəbəkə ilə əlaqəni kəsin"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Nəticə:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Gecikməni prioritetləşdirin"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Ötürmə sürətini prioritetləşdirin"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Premium şəbəkə satın alın"</string>
+ <string name="network_available" msgid="4780293262690730734">"Nəticə: Tələb olunan şəbəkə indi əlçatandır!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Nəticə: Şəbəkə sorğusu göndərildi!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Nəticə: Şəbəkə ilə əlaqə kəsildi!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Nəticə: Şəbəkə ilə əlaqəni kəsmək alınmadı!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Nəticə: Premium şəbəkə satın alan zaman istisna baş verdi!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Nəticə: Premium şəbəkə satın alan zaman boş nəticə əldə edildi!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Nəticə: Premium şəbəkə satın almaq mümkün deyil!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Nəticə: Premium şəbəkə satın almaq prosesi davam edir ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-b+sr+Latn/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-b+sr+Latn/strings.xml
index 229ff5d..2ca99d3 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-b+sr+Latn/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-b+sr+Latn/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Zatražite mrežu"</string>
+ <string name="release_network" msgid="174252378593535238">"Objavite mrežu"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Rezultat:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Dajte prioritet kašnjenju"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Dajte prioritet propusnom opsegu"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Kupite Premium mrežu"</string>
+ <string name="network_available" msgid="4780293262690730734">"Rezultat: Zahtevana mreža je trenutno dostupna!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Rezultat: Mreža je zatražena!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Rezultat: Mreža je objavljena!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Rezultat: Objavljivanje mreže nije uspelo!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Rezultat: Izuzetak pri kupovini Premium mreže!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Rezultat: Rezultat je prazan kada kupujete Premium mrežu!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Rezultat: Premium mreža nije dostupna za kupovinu!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Rezultat: Kupovina Premium mreže je u toku..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-be/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-be/strings.xml
index 385f84e..4b8a613 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-be/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-be/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Вітаем,"</string>
+ <string name="request_network" msgid="8945235490804849914">"Запытаць сетку"</string>
+ <string name="release_network" msgid="174252378593535238">"Вызваліць сетку"</string>
+ <string name="ping" msgid="7890607576220714932">"Пінг"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Вынік:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Аддаванне прыярытэту затрымцы"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Аддаванне прыярытэту паласе прапускання"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Купіць прэміяльную падпіску на выкарыстанне сеткі"</string>
+ <string name="network_available" msgid="4780293262690730734">"Вынік: запытаная сетка даступная!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Вынік: сетка запытана!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Вынік: сетка вызвалена!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Вынік: не ўдалося вызваліць сетку!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Вынік: пры спробе купіць прэміяльную падпіску на выкарыстанне сеткі ўзнікла выключэнне!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Вынік: пры спробе купіць прэміяльную падпіску на выкарыстанне сеткі атрыманы пусты вынік!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Вынік: прэміяльная падпіска на выкарыстанне сеткі недаступная для куплі!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Вынік: выконваецца купля прэміяльнай падпіскі на выкарыстанне сеткі..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-bg/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-bg/strings.xml
index 943a70d..b2951f9 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-bg/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-bg/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Здравей, празен фрагмент"</string>
+ <string name="request_network" msgid="8945235490804849914">"Заявка за мрежа"</string>
+ <string name="release_network" msgid="174252378593535238">"Освобождаване на мрежата"</string>
+ <string name="ping" msgid="7890607576220714932">"Команда ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Резултат:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Даване на приоритет на забавянето"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Даване на приоритет на пропускателната способност"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Купете Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Резултат: Заявената мрежа е достъпна сега!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Резултат: Мрежата е заявена!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Резултат: Мрежата е освободена!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Резултат: Мрежата не бе освободена!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Резултат: Изключение при покупка на Network Premium!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Резултат: При покупката на Network Premium получихте празен резултат."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Резултат: Network Premium не е налице за покупка!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Резултат: Network Premium се купува..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-bn/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-bn/strings.xml
index 229ff5d..5280d16 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-bn/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-bn/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Network সংক্রান্ত অনুরোধ করুন"</string>
+ <string name="release_network" msgid="174252378593535238">"Network রিলিজ করুন"</string>
+ <string name="ping" msgid="7890607576220714932">"পিং"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"ফলাফল:"</string>
+ <string name="latency_title" msgid="963052613947017009">"লেটেন্সিকে অগ্রাধিকার দিন"</string>
+ <string name="bw_title" msgid="3902162973688221344">"ব্যান্ডউইথকে অগ্রাধিকার দিন"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Network Premium কিনুন"</string>
+ <string name="network_available" msgid="4780293262690730734">"ফলাফল: অনুরোধ করা নেটওয়ার্ক এখন উপলভ্য আছে!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"ফলাফল: নেটওয়ার্কের জন্য অনুরোধ জানানো হয়েছে!"</string>
+ <string name="network_released" msgid="2992280481133877025">"ফলাফলt: নেটওয়ার্ক রিলিজ করা হয়েছে!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"ফলাফল: নেটওয়ার্ক রিলিজ করা যায়নি!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"ফলাফল: Network Premium কেনার সময় ব্যতিক্রম!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"ফলাফল: Network Premium কেনার সময় কোনও ফলাফল পাওয়া যায়নি!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"ফলাফল: Network Premium কেনার জন্য উপলভ্য নেই!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"ফলাফল: Network Premium কেনার প্রক্রিয়া চলছে ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-bs/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-bs/strings.xml
index 229ff5d..e2ea203 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-bs/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-bs/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Zatraži mrežu"</string>
+ <string name="release_network" msgid="174252378593535238">"Otključaj mrežu"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Rezultat:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Dodijeli prioritet latentnosti"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Dodijeli prioritet propusnosti"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Kupite premium verziju mreže"</string>
+ <string name="network_available" msgid="4780293262690730734">"Rezultat: zatražena mreža je sada dostupna."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Rezultat: mreža je zatražena."</string>
+ <string name="network_released" msgid="2992280481133877025">"Rezultat: mreža je otključana."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Rezultat: otključavanje mreže nije uspjelo."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Rezultat: izuzetak prilikom kupovine premium verzije mreže."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Rezultat: dobijen je prazan rezultat prilikom kupovine premium verzije mreže."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Rezultat: premium verzija mreže nije dostupna za kupovinu."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Rezultat: kupovina premium verzije mreže je u toku…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ca/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ca/strings.xml
index 9799d39..05529e3 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ca/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ca/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Fragment de benvinguda en blanc"</string>
+ <string name="request_network" msgid="8945235490804849914">"Sol·licita la xarxa"</string>
+ <string name="release_network" msgid="174252378593535238">"Allibera la xarxa"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Resultat:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritza la latència"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritza l\'amplada de banda"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Compra la xarxa prèmium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Resultat: la xarxa sol·licitada ja està disponible."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Resultat: s\'ha sol·licitat la xarxa."</string>
+ <string name="network_released" msgid="2992280481133877025">"Resultat: s\'ha alliberat la xarxa."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Resultat: no s\'ha pogut alliberar la xarxa."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Resultat: s\'ha produït una excepció en comprar la xarxa prèmium."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Resultat: s\'ha obtingut un resultat buit en comprar la xarxa prèmium."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Resultat: la xarxa prèmium no es pot comprar."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Resultat: la compra de la xarxa prèmium està en curs..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-cs/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-cs/strings.xml
index 793ab6f..d25f77d 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-cs/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-cs/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Prázdný fragment pro pozdrav"</string>
+ <string name="request_network" msgid="8945235490804849914">"Odeslat požadavek na síť"</string>
+ <string name="release_network" msgid="174252378593535238">"Uvolnit síť"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Výsledek:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Upřednostnit latenci"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Upřednostnit rychlost připojení"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Pořiďte si bonus k síti"</string>
+ <string name="network_available" msgid="4780293262690730734">"Výsledek: Požadovaná síť je teď dostupná."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Výsledek: Byl odeslán požadavek na síť."</string>
+ <string name="network_released" msgid="2992280481133877025">"Výsledek: Síť byla uvolněna."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Výsledek: Síť se nepodařilo uvolnit."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Výsledek: Výjimka při nákupu bonusu k síti."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Výsledek: Při nákupu bonusu k síti se vrátil prázdný výsledek."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Výsledek: Bonus k síti není k prodeji."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Výsledek: Probíhá nákup bonusu k síti…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-da/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-da/strings.xml
index 229ff5d..6e074c2 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-da/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-da/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Anmod om tv-kanal"</string>
+ <string name="release_network" msgid="174252378593535238">"Udgiv tv-kanal"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Resultat:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioriter forsinkelse"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioriter båndbredde"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Køb Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Resultat: Den tv-kanal, der blev anmodet om, er tilgængelig nu!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Resultat: Der blev anmodet om tv-kanalen!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Resultat: Tv-kanalen er blevet udgivet!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Resultat: Tv-kanalen kunne ikke udgives!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Resultat: Undtagen, når du køber Network Premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Resultat: Der blev vist et tomt resultat ved køb af Network Premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Resultat: Network Premium kan ikke købes!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Resultat: Købet af Network Premium er i gang…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-de/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-de/strings.xml
index 229ff5d..4ff4b5f 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-de/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-de/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Netzwerk anfragen"</string>
+ <string name="release_network" msgid="174252378593535238">"Netzwerk freigeben"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Ergebnis:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Latenz priorisieren"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Bandbreite priorisieren"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Premium-Netzwerk erwerben"</string>
+ <string name="network_available" msgid="4780293262690730734">"Ergebnis: Das gewünschte Netzwerk ist jetzt verfügbar."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Ergebnis: Das Netzwerk wurde angefordert."</string>
+ <string name="network_released" msgid="2992280481133877025">"Ergebnis: Das Netzwerk wurde freigegeben."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Ergebnis: Netzwerk konnte nicht freigegeben werden."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Ergebnis: Ausnahme beim Kauf des Premium-Netzwerks."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Ergebnis: Beim Kauf des Premium-Netzwerks wurde ein leeres Ergebnis zurückgegeben."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Ergebnis: Premium-Netzwerk kann nicht erworben werden."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Ergebnis: Der Kauf des Premium-Netzwerks läuft..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-el/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-el/strings.xml
index 229ff5d..0d39d8b 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-el/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-el/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Υποβολή αιτήματος για δίκτυο"</string>
+ <string name="release_network" msgid="174252378593535238">"Κυκλοφορία δικτύου"</string>
+ <string name="ping" msgid="7890607576220714932">"Εφαρμογή Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Αποτέλεσμα:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Προτεραιότητα λανθάνοντος χρόνου"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Προτεραιότητα εύρους ζώνης"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Αγορά επιπλέον χρέωσης δικτύου"</string>
+ <string name="network_available" msgid="4780293262690730734">"Αποτέλεσμα: Το δίκτυο που ζητήθηκε είναι πλέον διαθέσιμο!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Αποτέλεσμα: Υποβλήθηκε αίτημα για το δίκτυο!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Αποτέλεσμα: Το δίκτυο κυκλοφόρησε!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Αποτέλεσμα: Αποτυχία κυκλοφορίας του δικτύου!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Αποτέλεσμα: Εξαίρεση κατά την αγορά δικτύου με επιπλέον χρέωση!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Αποτέλεσμα: Εμφανίστηκε κενό αποτέλεσμα κατά την αγορά δικτύου με επιπλέον χρέωση!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Αποτέλεσμα: Η επιπλέον χρέωση του δικτύου δεν είναι διαθέσιμη για αγορά!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Αποτέλεσμα: Η αγορά δικτύου με επιπλέον χρέωση βρίσκεται σε εξέλιξη …"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-en-rAU/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-en-rAU/strings.xml
index 229ff5d..fbb98d6 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-en-rAU/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-en-rAU/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritise latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritise bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase network premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-en-rCA/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-en-rCA/strings.xml
index 229ff5d..a5c81f6 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-en-rCA/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-en-rCA/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request Network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release Network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritize Latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritize Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-en-rGB/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-en-rGB/strings.xml
index 229ff5d..fbb98d6 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-en-rGB/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-en-rGB/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritise latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritise bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase network premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-en-rIN/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-en-rIN/strings.xml
index 229ff5d..fbb98d6 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-en-rIN/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-en-rIN/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritise latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritise bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase network premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-en-rXC/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-en-rXC/strings.xml
index dafc7d2..a24b28a 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-en-rXC/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-en-rXC/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request Network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release Network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritize Latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritize Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-es-rUS/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-es-rUS/strings.xml
index 229ff5d..39b1338 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-es-rUS/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-es-rUS/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Solicitar red"</string>
+ <string name="release_network" msgid="174252378593535238">"Publicar red"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Resultado:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioriza la latencia"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioriza el ancho de banda"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Comprar red premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Resultado: Ya está disponible la red solicitada."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Resultado: Se solicitó la red."</string>
+ <string name="network_released" msgid="2992280481133877025">"Resultado: Se liberó la red."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Resultado: No se pudo liberar la red."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Resultado: Excepción cuando se compra la red premium."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Resultado: Se obtuvo un resultado vacío durante la compra de la red premium."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Resultado: La red premium no está disponible para comprarse."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Resultado: La compra de la red premium está en curso…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-es/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-es/strings.xml
index 355b912..8f46501 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-es/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-es/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hola, (segmento en blanco):"</string>
+ <string name="request_network" msgid="8945235490804849914">"Solicitar red"</string>
+ <string name="release_network" msgid="174252378593535238">"Lanzar red"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Resultado:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Priorizar latencia"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Priorizar ancho de banda"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Comprar red premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Resultado: la red solicitada ya está disponible."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Resultado: se ha solicitado la red."</string>
+ <string name="network_released" msgid="2992280481133877025">"Resultado: la red se ha lanzado."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Resultado: no se ha podido lanzar la red."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Resultado: excepción al comprar la red premium."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Resultado: se ha obtenido un resultado vacío al comprar la red premium."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Resultado: no se puede comprar la red premium."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Resultado: la compra de la red premium está en curso..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-et/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-et/strings.xml
index 229ff5d..f5ff262 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-et/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-et/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request Network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release Network"</string>
+ <string name="ping" msgid="7890607576220714932">"Kõll"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Tulemus:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritize Latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritize Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-eu/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-eu/strings.xml
index 229ff5d..ce566a1 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-eu/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-eu/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Eskatu sarea"</string>
+ <string name="release_network" msgid="174252378593535238">"Askatu sarea"</string>
+ <string name="ping" msgid="7890607576220714932">"Egin ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Emaitza:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Eman lehentasuna latentziari"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Eman lehentasuna banda-zabalerari"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Erosi sarearen premium bertsioa"</string>
+ <string name="network_available" msgid="4780293262690730734">"Emaitza: eskatutako sarea erabilgarri dago orain!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Emaitza: eskatu da sarea!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Emaitza: askatu da sarea!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Emaitza: ezin izan da askatu sarea!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Emaitza: salbuespena sarearen premium bertsioa erostean!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Emaitza: hutsik dauden emaitzak lortu dira sarearen premium bertsioa erostean!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Emaitza: ezin da erosi sarearen premium bertsioa!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Emaitza: abian da sarearen premium bertsioa erosteko prozesua…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-fa/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-fa/strings.xml
index 6f35108..5a51eaa 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-fa/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-fa/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"سلام بخش خالی"</string>
+ <string name="request_network" msgid="8945235490804849914">"درخواست شبکه"</string>
+ <string name="release_network" msgid="174252378593535238">"انتشار شبکه"</string>
+ <string name="ping" msgid="7890607576220714932">"پینگ"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"نتیجه:"</string>
+ <string name="latency_title" msgid="963052613947017009">"اولویتبندی تأخیر"</string>
+ <string name="bw_title" msgid="3902162973688221344">"اولویتبندی پهنای باند"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"خرید حساب ممتاز شبکه"</string>
+ <string name="network_available" msgid="4780293262690730734">"نتیجه: شبکه درخواستشده اکنون دردسترس است!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"نتیجه: شبکه درخواست شد!"</string>
+ <string name="network_released" msgid="2992280481133877025">"نتیجه: شبکه منتشر شد!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"نتیجه: شبکه منتشر نشد!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"نتیجه: هنگام خرید حساب ممتاز شبکهْ استثنائی پیش میآید!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"نتیجه: هنگام تلاش برای خرید حساب ممتاز شبکه نتیجه خالی برگردانده میشود!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"نتیجه: حساب ممتاز شبکه برای خرید دردسترس نیست!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"نتیجه: خرید حساب ممتاز شبکه درحال انجام است…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-fi/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-fi/strings.xml
index 229ff5d..df8ce6b 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-fi/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-fi/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Pyydä verkkoa"</string>
+ <string name="release_network" msgid="174252378593535238">"Vapauta verkko"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Tulos:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Priorisoi viive"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Priorisoi kaistanleveys"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Osta Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Tulos: Pyydetty verkko on nyt käytettävissä!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Tulos: Verkkoa on pyydetty!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Tulos: Verkko on vapautettu!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Tulos: Verkon vapautus epäonnistui!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Tulos: Poikkeus Network Premiumia ostettaessa!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Tulos: Tyhjä tulos Network Premiumia ostettaessa!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Tulos: Network Premium ei ole ostettavissa!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Tulos: Network Premiumin osto on käynnissä..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-fr-rCA/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-fr-rCA/strings.xml
index 229ff5d..70a891b 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-fr-rCA/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-fr-rCA/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Réseau de demande"</string>
+ <string name="release_network" msgid="174252378593535238">"Réseau de diffusion"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Résultat :"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioriser la latence"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioriser la bande passante"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Acheter le réseau Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Résultat : Le réseau demandé est maintenant disponible!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Résultat : Le réseau a été demandé!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Résultat : Le réseau est publié!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Résultat : Échec de la publication du réseau!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Résultat : Exception lors de l\'achat du réseau premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Résultat : Aucun résultat lors de l\'achat du réseau Premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Résultat : Le réseau Premium n\'est pas offert à l\'achat!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Résultat : L\'achat du réseau Premium est en cours…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-fr/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-fr/strings.xml
index e9b3b45..96d996b 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-fr/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-fr/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Bonjour fragment vierge"</string>
+ <string name="request_network" msgid="8945235490804849914">"Demander le réseau"</string>
+ <string name="release_network" msgid="174252378593535238">"Libérer le réseau"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Résultat :"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioriser la latence"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioriser la bande passante"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Souscrire au réseau premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Résultat : le réseau demandé est disponible."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Résultat : le réseau a été demandé."</string>
+ <string name="network_released" msgid="2992280481133877025">"Résultat : le réseau a été libéré."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Résultat : échec de la libération du réseau."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Résultat : une exception s\'est produite lors de la souscription au réseau premium."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Résultat : un résultat vide s\'est affiché lors de la souscription au réseau premium."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Résultat : le réseau premium n\'est pas disponible à l\'achat."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Résultat : la souscription au réseau premium est en cours…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-gl/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-gl/strings.xml
index 229ff5d..c505b21 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-gl/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-gl/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Solicitar rede"</string>
+ <string name="release_network" msgid="174252378593535238">"Liberar rede"</string>
+ <string name="ping" msgid="7890607576220714932">"Facer ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Resultado:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Darlle prioridade á latencia"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Darlle prioridade á largura de banda"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Comprar rede premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Resultado: A rede solicitada está dispoñible neste momento"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Resultado: Solicitouse a rede"</string>
+ <string name="network_released" msgid="2992280481133877025">"Resultado: Liberouse a rede"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Resultado: Produciuse un erro ao liberar a rede"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Resultado: Excepción ao comprar a rede premium"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Resultado: Recibiuse un resultado baleiro ao comprar a rede premium"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Resultado: A rede premium non está á venda"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Resultado: A compra da rede premium está en curso…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-gu/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-gu/strings.xml
index 229ff5d..675ac46 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-gu/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-gu/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"નેટવર્કની વિનંતી કરો"</string>
+ <string name="release_network" msgid="174252378593535238">"નેટવર્ક રિલીઝ કરો"</string>
+ <string name="ping" msgid="7890607576220714932">"પિંગ"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"પરિણામ:"</string>
+ <string name="latency_title" msgid="963052613947017009">"વિલંબતાને પ્રાધાન્યતા આપો"</string>
+ <string name="bw_title" msgid="3902162973688221344">"બૅન્ડવિડ્થને પ્રાધાન્યતા આપો"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Network Premium ખરીદો"</string>
+ <string name="network_available" msgid="4780293262690730734">"પરિણામ: વિનંતી કરવામાં આવેલું નેટવર્ક હવે ઉપલબ્ધ છે!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"પરિણામ: નેટવર્કની વિનંતી કરવામાં આવી છે!"</string>
+ <string name="network_released" msgid="2992280481133877025">"પરિણામ: નેટવર્ક રિલીઝ કરવામાં આવ્યું છે!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"પરિણામ: નેટવર્ક રિલીઝ કરવામાં નિષ્ફળ રહ્યાં!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"પરિણામ: network premium ખરીદતી વખતે અપવાદ!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"પરિણામ: network premium ખરીદતી વખતે ખાલી પરિણામ મળ્યું!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"પરિણામ: ખરીદી કરવા માટે network premium ઉપલબ્ધ નથી!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"પરિણામ: network premiumની ખરીદીની પ્રક્રિયા ચાલુ છે ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-hi/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-hi/strings.xml
index 229ff5d..0f13f68 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-hi/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-hi/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"नेटवर्क का अनुरोध करें"</string>
+ <string name="release_network" msgid="174252378593535238">"नेटवर्क रिलीज़ करें"</string>
+ <string name="ping" msgid="7890607576220714932">"पिंग करें"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"नतीजा:"</string>
+ <string name="latency_title" msgid="963052613947017009">"इंतज़ार के समय को प्राथमिकता दें"</string>
+ <string name="bw_title" msgid="3902162973688221344">"बैंडविड्थ को प्राथमिकता दें"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"नेटवर्क प्रीमियम खरीदें"</string>
+ <string name="network_available" msgid="4780293262690730734">"नतीजा: अनुरोध किया गया नेटवर्क अब उपलब्ध है!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"नतीजा: नेटवर्क के लिए अनुरोध किया गया है!"</string>
+ <string name="network_released" msgid="2992280481133877025">"नतीजा: नेटवर्क रिलीज़ हो गया है!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"नतीजा: नेटवर्क रिलीज़ नहीं किया जा सका!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"नतीजा: नेटवर्क प्रीमियम खरीदते समय अपवाद!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"नतीजा: नेटवर्क प्रीमियम खरीदते समय खाली नतीजा मिला!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"नतीजा: नेटवर्क प्रीमियम खरीदारी के लिए उपलब्ध नहीं है!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"नतीजा: नेटवर्क प्रीमियम खरीदारी चल रही है ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-hr/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-hr/strings.xml
index 229ff5d..dd4f3f5 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-hr/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-hr/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Zahtjev za mrežu"</string>
+ <string name="release_network" msgid="174252378593535238">"Otkazivanje mreže"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Rezultat:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prednost ima latencija"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prednost ima propusnost"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Kupnja premium sadržaja mreže"</string>
+ <string name="network_available" msgid="4780293262690730734">"Rezultat: zatražena mreža sada je dostupna!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Rezultat: mreža je zatražena!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Rezultat: mreža je otkazana!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Rezultat: otkazivanje mreže nije uspjelo!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Rezultat: iznimka pri kupnji premium sadržaja mreže!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Rezultat: dobiven je prazni rezultat pri kupnji premium sadržaja mreže!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Rezultat: premium sadržaj mreže nije dostupan za kupnju!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Rezultat: u tijeku je kupnja premium sadržaja mreže..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-hu/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-hu/strings.xml
index 0e73dba..524b271 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-hu/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-hu/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Üres üdvözlő rész"</string>
+ <string name="request_network" msgid="8945235490804849914">"Hálózat kérése"</string>
+ <string name="release_network" msgid="174252378593535238">"Hálózat felszabadítása"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Eredmény:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Várakozási idő előnyben"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Sávszélesség előnyben"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"A hálózati prémium megvásárlása"</string>
+ <string name="network_available" msgid="4780293262690730734">"Eredmény: A kért hálózat már rendelkezésre áll."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Eredmény: A hálózat igénylése megtörtént."</string>
+ <string name="network_released" msgid="2992280481133877025">"Eredmény: A hálózat felszabadult."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Eredmény: Nem sikerült felszabadítani a hálózatot."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Eredmény: Kivétel a hálózati prémium megvásárlásakor."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Eredmény: Üres eredmény érkezett vissza a hálózati prémium megvásárlásakor."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Eredmény: A hálózati prémium nem vásárolható meg."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Eredmény: A hálózati prémium megvásárlása folyamatban van..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-hy/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-hy/strings.xml
index 31a60c6..77ef65f 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-hy/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-hy/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Դատարկ հատված"</string>
+ <string name="request_network" msgid="8945235490804849914">"Ցանցն ազատելու հայտ ուղարկել"</string>
+ <string name="release_network" msgid="174252378593535238">"Ազատել ցանցը"</string>
+ <string name="ping" msgid="7890607576220714932">"Փինգ"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Արդյունքը՝"</string>
+ <string name="latency_title" msgid="963052613947017009">"Առաջնահերթություն տալ հապաղմանը"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Առաջնահերթություն տալ թողունակությանը"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Գնել ցանցի օգտագործման պրեմիում բաժանորդագրություն"</string>
+ <string name="network_available" msgid="4780293262690730734">"Արդյունքը՝ պահանջվող ցանցն այժմ հասանելի է։"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Արդյունքը՝ ցանցն ազատելու հայտն ուղարկվեց։"</string>
+ <string name="network_released" msgid="2992280481133877025">"Արդյունքը՝ ցանցն ազատվեց։"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Արդյունքը՝ չհաջողվեց ազատել ցանցը։"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Արդյունքը՝ ցանցի օգտագործման պրեմիում բաժանորդագրություն գնելու փորձի ժամանակ բացառություն է առաջացել։"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Արդյունքը՝ ցանցի օգտագործման պրեմիում բաժանորդագրություն գնելու փորձի ժամանակ ստացվել է դատարկ արդյունք։"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Արդյունքը՝ ցանցի օգտագործման պրեմիում բաժանորդագրությունը հասանելի չէ գնման համար։"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Արդյունքը՝ կատարվում է ցանցի օգտագործման պրեմիում բաժանորդագրության գնում ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-in/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-in/strings.xml
index 229ff5d..9e1aa19 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-in/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-in/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Meminta Jaringan"</string>
+ <string name="release_network" msgid="174252378593535238">"Melepaskan Jaringan"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Hasil:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Memprioritaskan Latensi"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Memprioritaskan Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Membeli Jaringan Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Hasil: Jaringan yang diminta kini tersedia."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Hasil: Jaringan telah diminta."</string>
+ <string name="network_released" msgid="2992280481133877025">"Hasil: Jaringan telah dilepas."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Hasil: Gagal melepaskan jaringan."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Hasil: Pengecualian saat membeli jaringan premium."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Hasil: Mendapatkan hasil kosong saat membeli jaringan premium."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Hasil: Jaringan premium tidak dapat dibeli."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Hasil: Pembelian jaringan premium sedang berlangsung."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-is/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-is/strings.xml
index 229ff5d..61772de 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-is/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-is/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Biðja um netkerfi"</string>
+ <string name="release_network" msgid="174252378593535238">"Gefa út netkerfi"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping-prófun"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Niðurstaða:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Forgangsraða biðtíma"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Forgangsraða bandvídd"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Kaupa úrvalsnetkerfi"</string>
+ <string name="network_available" msgid="4780293262690730734">"Niðurstaða: Netkerfið sem beðið var um er nú í boði!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Niðurstaða: Beiðni um netkerfið hefur verið send!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Niðurstaða: Útgáfa netkerfisins tókst!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Niðurstaða: Ekki tókst að gefa netkerfið út!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Niðurstaða: Undantekning við kaup á úrvalsnetkerfi!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Niðurstaða: Fékk tóma niðurstöðu við kaup á úrvalsnetkerfi!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Niðurstaða: Það er ekki hægt að kaupa úrvalsnetkerfið!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Niðurstaða: Kaup á úrvalsnetkerfi eru í vinnslu..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-it/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-it/strings.xml
index 229ff5d..0c7b5f0 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-it/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-it/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request Network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release Network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritize Latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritize Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Risultato: la rete è stata richiesta."</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-iw/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-iw/strings.xml
index 4287a40..243e2c0 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-iw/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-iw/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"שלום קטע ריק"</string>
+ <string name="request_network" msgid="8945235490804849914">"שליחת בקשה לרשת"</string>
+ <string name="release_network" msgid="174252378593535238">"שחרור הרשת"</string>
+ <string name="ping" msgid="7890607576220714932">"פינג"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"תוצאה:"</string>
+ <string name="latency_title" msgid="963052613947017009">"עדיפות לזמן האחזור"</string>
+ <string name="bw_title" msgid="3902162973688221344">"עדיפות לרוחב הפס"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"רכישת רשת פרימיום"</string>
+ <string name="network_available" msgid="4780293262690730734">"תוצאה: הרשת המבוקשת זמינה"</string>
+ <string name="network_requested" msgid="5646123922691865991">"תוצאה: הבקשה לרשת נשלחה"</string>
+ <string name="network_released" msgid="2992280481133877025">"תוצאה: הרשת שוחררה"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"תוצאה: הרשת לא שוחררה"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"תוצאה: חריגה ברכישת פרמיה לרשת"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"תוצאה: התקבלה תוצאה ריקה בניסיון לרכוש פרמיה לרשת"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"תוצאה: אי אפשר לרכוש את הפרמיה לרשת"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"תוצאה: רכישת פרמיה לרשת מתבצעת…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ja/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ja/strings.xml
index 229ff5d..a265867 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ja/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ja/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"ネットワークをリクエストする"</string>
+ <string name="release_network" msgid="174252378593535238">"ネットワークを解放する"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"結果:"</string>
+ <string name="latency_title" msgid="963052613947017009">"レイテンシを優先する"</string>
+ <string name="bw_title" msgid="3902162973688221344">"帯域幅を優先する"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"ネットワーク プレミアムを購入"</string>
+ <string name="network_available" msgid="4780293262690730734">"結果: リクエストされたネットワークが利用できるようになりました。"</string>
+ <string name="network_requested" msgid="5646123922691865991">"結果: ネットワークがリクエストされました。"</string>
+ <string name="network_released" msgid="2992280481133877025">"結果: ネットワークが解放されました。"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"結果: ネットワークを解放できませんでした。"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"結果: ネットワーク プレミアム購入時に例外が発生しました。"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"結果: ネットワーク プレミアムの購入時に空の結果が返されました。"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"結果: ネットワーク プレミアムを購入できません。"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"結果: ネットワーク プレミアムの購入処理中です。"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ka/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ka/strings.xml
index 8d91ed0..1543a21 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ka/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ka/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"მოგესალმებით, ეს ცარიელი ფრაგმენტია"</string>
+ <string name="request_network" msgid="8945235490804849914">"მოთხოვნილი ქსელი"</string>
+ <string name="release_network" msgid="174252378593535238">"გამოშვების ქსელი"</string>
+ <string name="ping" msgid="7890607576220714932">"ზუზუნი"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"შედეგი:"</string>
+ <string name="latency_title" msgid="963052613947017009">"უპირატესობა მიანიჭეთ რეაგირების დროს"</string>
+ <string name="bw_title" msgid="3902162973688221344">"პრიორიტეტული გამტარუნარიანობა"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"შეიძინეთ ქსელის პრემიუმი"</string>
+ <string name="network_available" msgid="4780293262690730734">"შედეგი: მოთხოვნილი ქსელი უკვე ხელმისაწვდომია!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"შედეგი: ქსელი მოთხოვნილია!"</string>
+ <string name="network_released" msgid="2992280481133877025">"შედეგი: ქსელი გათავისუფლდა!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"შედეგი: ქსელის გათავისუფლება ვერ მოხერხდა"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"შედეგი: გამონაკლისი ქსელის პრემიუმის შეძენისას!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"შედეგი: მიიღეთ ცარიელი შედეგი ქსელის პრემიუმის შეძენისას!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"შედეგი: ქსელის პრემია არ არის ხელმისაწვდომი შესაძენად!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"შედეგი:მიმდინარეობს ქსელის პრემიუმ შესყიდვა ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-kk/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-kk/strings.xml
index 229ff5d..1dac729 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-kk/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-kk/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Желіні сұрау"</string>
+ <string name="release_network" msgid="174252378593535238">"Желіні шығару"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Нәтиже:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Кідіріске басымдық беру"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Өткізу мүмкіндігіне басымдық беру"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Премиум желіні сатып алу"</string>
+ <string name="network_available" msgid="4780293262690730734">"Нәтиже: сұралған желіні қазір пайдалануға болады!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Нәтиже: желі сұралды!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Нәтиже: желі шығарылды!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Нәтиже: желі шығарылмады!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Нәтиже: премиум желіні сатып алу кезінде ерекше жағдай шықты!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Нәтиже: премиум желіні сатып алу кезінде бос нәтиже шықты!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Нәтиже: премиум желіні сатып алу мүмкін емес!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Нәтиже: премиум желіні сатып алу процесі жүріп жатыр…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-km/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-km/strings.xml
index 229ff5d..1c4002a 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-km/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-km/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"ស្នើសុំបណ្ដាញ"</string>
+ <string name="release_network" msgid="174252378593535238">"ដកបណ្ដាញចេញ"</string>
+ <string name="ping" msgid="7890607576220714932">"ភីង"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"លទ្ធផល៖"</string>
+ <string name="latency_title" msgid="963052613947017009">"ផ្ដល់អាទិភាពការពន្យារ"</string>
+ <string name="bw_title" msgid="3902162973688221344">"ផ្ដល់អាទិភាពកម្រិតបញ្ជូន"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"ទិញបណ្ដាញលំដាប់ខ្ពស់"</string>
+ <string name="network_available" msgid="4780293262690730734">"លទ្ធផល៖ ឥឡូវនេះអាចប្រើបណ្ដាញដែលបានស្នើសុំបានហើយ!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"លទ្ធផល៖ បានស្នើសុំបណ្ដាញ!"</string>
+ <string name="network_released" msgid="2992280481133877025">"លទ្ធផល៖ បានដកបណ្ដាញចេញ!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"លទ្ធផល៖ ការលើកលែង នៅពេលទិញបណ្ដាញលំដាប់ខ្ពស់!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"លទ្ធផល៖ ការទិញបណ្ដាញលំដាប់ខ្ពស់កំពុងដំណើរការ ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-kn/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-kn/strings.xml
index 13a202b..1c06a91 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-kn/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-kn/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"ಹಲೋ, ಖಾಲಿ ಫ್ರಾಗ್ಮೆಂಟ್"</string>
+ <string name="request_network" msgid="8945235490804849914">"ನೆಟ್ವರ್ಕ್ಗಾಗಿ ವಿನಂತಿಸಿ"</string>
+ <string name="release_network" msgid="174252378593535238">"ನೆಟ್ವರ್ಕ್ ಅನ್ನು ಬಿಡುಗಡೆ ಮಾಡಿ"</string>
+ <string name="ping" msgid="7890607576220714932">"ಪಿಂಗ್"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"ಫಲಿತಾಂಶ:"</string>
+ <string name="latency_title" msgid="963052613947017009">"ವಿಳಂಬವನ್ನು ಆದ್ಯತೆಗೊಳಿಸಿ"</string>
+ <string name="bw_title" msgid="3902162973688221344">"ಬ್ಯಾಂಡ್ವಿಡ್ತ್ ಆದ್ಯತೆಗೊಳಿಸಿ"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"ನೆಟ್ವರ್ಕ್ ಪ್ರೀಮಿಯಂ ಖರೀದಿಸಿ"</string>
+ <string name="network_available" msgid="4780293262690730734">"ಫಲಿತಾಂಶ: ವಿನಂತಿಸಿದ ನೆಟ್ವರ್ಕ್ ಈಗ ಲಭ್ಯವಿದೆ!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"ಫಲಿತಾಂಶ: ನೆಟ್ವರ್ಕ್ಗಾಗಿ ವಿನಂತಿಸಲಾಗಿದೆ!"</string>
+ <string name="network_released" msgid="2992280481133877025">"ಫಲಿತಾಂಶ: ನೆಟ್ವರ್ಕ್ ಅನ್ನು ಬಿಡುಗಡೆ ಮಾಡಲಾಗಿದೆ!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"ಫಲಿತಾಂಶ: ನೆಟ್ವರ್ಕ್ ಅನ್ನು ಬಿಡುಗಡೆ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"ಫಲಿತಾಂಶ: ನೆಟ್ವರ್ಕ್ ಪ್ರೀಮಿಯಂ ಅನ್ನು ಖರೀದಿಸುವಾಗ ದೊರೆಯುವ ವಿನಾಯಿತಿ!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"ಫಲಿತಾಂಶ: ನೆಟ್ವರ್ಕ್ ಪ್ರೀಮಿಯಂ ಅನ್ನು ಖರೀದಿಸುವಾಗ ಫಲಿತಾಂಶವು ದೊರೆತಿಲ್ಲ!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"ಫಲಿತಾಂಶ: ನೆಟ್ವರ್ಕ್ ಪ್ರೀಮಿಯಂ ಖರೀದಿಗೆ ಲಭ್ಯವಿಲ್ಲ!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"ಫಲಿತಾಂಶ: ನೆಟ್ವರ್ಕ್ ಪ್ರೀಮಿಯಂ ಖರೀದಿ ಪ್ರಕ್ರಿಯೆ ಪ್ರಗತಿಯಲ್ಲಿದೆ ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ko/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ko/strings.xml
index 229ff5d..78199c8 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ko/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ko/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"네트워크 요청"</string>
+ <string name="release_network" msgid="174252378593535238">"네트워크 해제"</string>
+ <string name="ping" msgid="7890607576220714932">"핑"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"결과:"</string>
+ <string name="latency_title" msgid="963052613947017009">"지연 시간 우선순위 지정"</string>
+ <string name="bw_title" msgid="3902162973688221344">"대역폭의 우선순위 지정"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Network Premium 구매"</string>
+ <string name="network_available" msgid="4780293262690730734">"결과: 요청된 네트워크를 지금 사용할 수 있습니다."</string>
+ <string name="network_requested" msgid="5646123922691865991">"결과: 네트워크가 요청되었습니다."</string>
+ <string name="network_released" msgid="2992280481133877025">"결과: 네트워크가 해제되었습니다."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"결과: 네트워크 해제에 실패했습니다."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"결과: 네트워크 프리미엄 구매 시 예외 발생"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"결과: 네트워크 프리미엄을 구매할 때 결과가 비어 있습니다."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"결과: 네트워크 프리미엄을 구매할 수 없습니다."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"결과: 네트워크 프리미엄 구매가 진행 중입니다."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ky/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ky/strings.xml
index 229ff5d..8e9fd3d 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ky/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ky/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Тармак сурамы"</string>
+ <string name="release_network" msgid="174252378593535238">"Тармакты артка кайтаруу"</string>
+ <string name="ping" msgid="7890607576220714932">"Хрусталь"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Натыйжа:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Күтүү убакытына артыкчылык берүү"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Өткөрүү жөндөмдүүлүгүнө артыкчылык берүү"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Премиум тармагын сатып алуу"</string>
+ <string name="network_available" msgid="4780293262690730734">"Натыйжа: Суралган тармак азыр жеткиликтүү."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Натыйжа: Тармак суралды."</string>
+ <string name="network_released" msgid="2992280481133877025">"Натыйжа: Тармак артка кайтарылды."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Натыйжа: Тармак артка кайтарылган жок."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Натыйжа: Премиум тармагын сатып алууда өзгөчө учур чыкты."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Натыйжа: Премиум тармагын сатып алууда жыйынтык көрсөтүлгөн жок."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Натыйжа: Премиум тармагы сатылбайт."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Натыйжа: Премиум тармагы сатылып алынууда."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-lo/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-lo/strings.xml
index 3d47ad6..03812e7 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-lo/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-lo/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"ສະບາຍດີຈຸດແຕກທີ່ຫວ່າງເປົ່າ"</string>
+ <string name="request_network" msgid="8945235490804849914">"ຮ້ອງຂໍເຄືອຂ່າຍ"</string>
+ <string name="release_network" msgid="174252378593535238">"ປ່ອຍເຄືອຂ່າຍ"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"ຈັດລຳດັບຄວາມສຳຄັນເວລາຕອບສະໜອງ"</string>
+ <string name="bw_title" msgid="3902162973688221344">"ຈັດລຳດັບຄວາມສຳຄັນແບນວິດ"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"ຊື້ເຄືອຂ່າຍ Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"ຜົນຮັບ: ຕອນນີ້ເຄືອຂ່າຍທີ່ຮ້ອງຂໍໃຊ້ໄດ້ແລ້ວ!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-lt/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-lt/strings.xml
index 509fd2d..4d6c73e 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-lt/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-lt/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Sveiki, tuščias fragmentas"</string>
+ <string name="request_network" msgid="8945235490804849914">"Pateikti tinklo užklausą"</string>
+ <string name="release_network" msgid="174252378593535238">"Išleisti tinklą"</string>
+ <string name="ping" msgid="7890607576220714932">"Ryšio patikra"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Rezultatas:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Suteikti prioritetą delsai"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Suteikti prioritetą pralaidumui"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Įsigyti tinklą „Premium“"</string>
+ <string name="network_available" msgid="4780293262690730734">"Rezultatas: tinklas, dėl kurio pateikta užklausa, dabar pasiekiamas!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Rezultatas: pateikta tinklo užklausa!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Rezultatas: tinklas išleistas!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Rezultatas: nepavyko išleisti tinklo!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Rezultatas: išimtis įsigyjant tinklą „Premium“!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Rezultatas: gautas tuščias rezultatas įsigyjant tinklą „Premium“!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Rezultatas: tinklo „Premium“ negalima įsigyti!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Rezultatas: vykdomas tinklo „Premium“ įsigijimas..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-lv/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-lv/strings.xml
index 229ff5d..3f24cf5 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-lv/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-lv/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Pieprasīt tīklu"</string>
+ <string name="release_network" msgid="174252378593535238">"Atbrīvot tīklu"</string>
+ <string name="ping" msgid="7890607576220714932">"Ehotestēt"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Rezultāts:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritāte latentumam"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritāte joslas platumam"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Iegādāties tīkla maksas abonementu"</string>
+ <string name="network_available" msgid="4780293262690730734">"Rezultāts: pieprasītais tīkls tagad ir pieejams."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Rezultāts: tīkls ir pieprasīts."</string>
+ <string name="network_released" msgid="2992280481133877025">"Rezultāts: tīkls ir atbrīvots."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Rezultāts: neizdevās atbrīvot tīklu."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Rezultāts: iegādājoties tīkla maksas abonementu, radās izņēmums."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Rezultāts: iegādājoties tīkla maksas abonementu, tika iegūts tukšs rezultāts."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Rezultāts: tīkla maksas abonementu nevar iegādāties."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Rezultāts: notiek tīkla maksas abonementa iegāde…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-mk/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-mk/strings.xml
index 229ff5d..41c891f 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-mk/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-mk/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Побарајте мрежа"</string>
+ <string name="release_network" msgid="174252378593535238">"Исклучете мрежа"</string>
+ <string name="ping" msgid="7890607576220714932">"Пинг"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Резултат:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritize Latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritize Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Купете премиум-мрежа"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Резултат: мрежата е побарана!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Резултат: мрежата е исклучена!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Резултат: исклучок при купување премиум-мрежа!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Резултат: се доби празен резултат при купување премиум-мрежа!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Резултат: премиум-мрежата не е достапна за купување!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Резултат: купувањето на премиум-мрежата е во тек …"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ml/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ml/strings.xml
index 47ca9c8..bc8e8c5 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ml/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ml/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"ഹലോ ബ്ലാങ്ക് ഫ്രാഗ്മെന്റ്"</string>
+ <string name="request_network" msgid="8945235490804849914">"ഒരു നെറ്റ്വർക്ക് അഭ്യർത്ഥിക്കുക"</string>
+ <string name="release_network" msgid="174252378593535238">"നെറ്റ്വർക്ക് റിലീസ് ചെയ്യുക"</string>
+ <string name="ping" msgid="7890607576220714932">"പിംഗ്"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"ഫലം:"</string>
+ <string name="latency_title" msgid="963052613947017009">"പ്രതികരണ സമയത്തിന് മുൻഗണന നൽകുക"</string>
+ <string name="bw_title" msgid="3902162973688221344">"ബാൻഡ്വിഡ്ത്തിന് മുൻഗണന നൽകുക"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"നെറ്റ്വർക്ക് പ്രീമിയം വാങ്ങുക"</string>
+ <string name="network_available" msgid="4780293262690730734">"ഫലം: അഭ്യർത്ഥിച്ച നെറ്റ്വർക്ക് ഇപ്പോൾ ലഭ്യമാണ്!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"ഫലം: നെറ്റ്വർക്കിന് അഭ്യർത്ഥിച്ചു!"</string>
+ <string name="network_released" msgid="2992280481133877025">"ഫലം: നെറ്റ്വർക്ക് റിലീസ് ചെയ്തിരിക്കുന്നു!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"ഫലം: നെറ്റ്വർക്ക് റിലീസ് ചെയ്യാനായില്ല!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"ഫലം: നെറ്റ്വർക്ക് പ്രീമിയം വാങ്ങുമ്പോൾ ഒഴിവാക്കുന്നവ!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"ഫലം: നെറ്റ്വർക്ക് പ്രീമിയം വാങ്ങുമ്പോൾ ഫലം ലഭിക്കുന്നില്ല!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"ഫലം: നെറ്റ്വർക്ക് പ്രീമിയം വാങ്ങുന്നതിന് ലഭ്യമല്ല!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"ഫലം: നെറ്റ്വർക്ക് പ്രീമിയം വാങ്ങൽ പുരോഗമിക്കുന്നു ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-mn/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-mn/strings.xml
index 1aa5138..117cd3a 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-mn/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-mn/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Сайн байна уу хоосон хэсэг"</string>
+ <string name="request_network" msgid="8945235490804849914">"Сүлжээний хүсэлт тавих"</string>
+ <string name="release_network" msgid="174252378593535238">"Сүлжээ шинээр гаргах"</string>
+ <string name="ping" msgid="7890607576220714932">"Пинг"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Үр дүн:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Хоцролтыг чухалчлах"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Зурвасын өргөнийг чухалчлах"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Сүлжээний премиумийг худалдан авах"</string>
+ <string name="network_available" msgid="4780293262690730734">"Үр дүн: Хүсэлт тавьсан сүлжээ одоо боломжтой боллоо!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Үр дүн: Сүлжээний хүсэлт тавьсан!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Үр дүн: Сүлжээг шинээр гаргалаа!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Үр дүн: Сүлжээг шинээр гаргаж чадсангүй!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Үр дүн: Сүлжээний премиумийг худалдан авах үед гажилт гарлаа!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Үр дүн: Сүлжээний премиумийг худалдан авах үед хоосон илэрц авсан!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Үр дүн: Сүлжээний премиумийг худалдан авах боломжгүй!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Үр дүн: Сүлжээний премиумийг худалдан авч байна ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-mr/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-mr/strings.xml
index 7fbe792..35347b6 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-mr/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-mr/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"हॅलो ब्लँक फ्रॅग्मेंट"</string>
+ <string name="request_network" msgid="8945235490804849914">"नेटवर्कची विनंती करा"</string>
+ <string name="release_network" msgid="174252378593535238">"नेटवर्क रिलीझ करा"</string>
+ <string name="ping" msgid="7890607576220714932">"पिंग"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"परिणाम:"</string>
+ <string name="latency_title" msgid="963052613947017009">"लेटन्सी ला प्राधान्य द्या"</string>
+ <string name="bw_title" msgid="3902162973688221344">"बँडविड्थ ला प्राधान्य द्या"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"नेटवर्क प्रीमियमची खरेदी"</string>
+ <string name="network_available" msgid="4780293262690730734">"परिणाम: विनंती केलेले नेटवर्क आता उपलब्ध आहे!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"परिणाम: नटवर्कची विनंती केली आहे!"</string>
+ <string name="network_released" msgid="2992280481133877025">"परिणाम: नेटवर्क रिलीझ केले आहे!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"परिणाम: नेटवर्क रिलीझ करता आले नाही!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"परिणाम: नेटवर्क प्रीमियम खरेदी करताना एक्सेप्शन!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"परिणाम: नेटवर्क प्रीमियम खरेदी करताना कोणताही परिणाम आढळला नाही!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"परिणाम: नेटवर्क प्रीमियम खरेदी करण्यासाठी उपलब्ध नाही!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"परिणाम: नेटवर्क प्रीमियम खरेदी करणे प्रगतीपथावर आहे..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ms/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ms/strings.xml
index b928cd6..cfb7de6 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ms/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ms/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Helo cebisan kosong"</string>
+ <string name="request_network" msgid="8945235490804849914">"Minta Rangkaian"</string>
+ <string name="release_network" msgid="174252378593535238">"Lepaskan Rangkaian"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Hasil:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Utamakan Kependaman"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Utamakan Lebar Jalur"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Beli Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Hasil: Rangkaian yang diminta kini tersedia!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Hasil: Rangkaian telah diminta!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Hasil: Rangkaian telah dilepaskan!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Hasil: Gagal mengeluarkan rangkaian!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Hasil: Pengecualian apabila membeli network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Hasil: Tiada hasil apabila membeli network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Hasil: Network premium tidak tersedia untuk pembelian!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Hasil: Pembelian network premium sedang diproses ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-my/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-my/strings.xml
index 229ff5d..a5c81f6 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-my/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-my/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request Network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release Network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritize Latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritize Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-nb/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-nb/strings.xml
index 3d601ab..abb1af4 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-nb/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-nb/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hei, tomt fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Forespør nettverk"</string>
+ <string name="release_network" msgid="174252378593535238">"Frigi nettverk"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Resultat:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioriter tidsforsinkelser"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioriter båndbredde"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Kjøp premiumnettverk"</string>
+ <string name="network_available" msgid="4780293262690730734">"Resultat: Det forespurte nettverket er tilgjengelig nå."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Resultat: Nettverket er forespurt."</string>
+ <string name="network_released" msgid="2992280481133877025">"Resultat: Nettverket er frigitt."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Resultat: Kunne ikke frigi nettverket."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Resultat: Unntak ved kjøp av premiumnettverk."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Resultat: Fikk tomt resultat ved kjøp av premiumnettverk."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Resultat: Premiumnettverket er ikke tilgjengelig for kjøp."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Resultat: Kjøp av premiumnettverk pågår …"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ne/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ne/strings.xml
index 229ff5d..cb2d561 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ne/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ne/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"नेटवर्क अनुरोध गर्नुहोस्"</string>
+ <string name="release_network" msgid="174252378593535238">"नेटवर्क रिलिज गर्नुहोस्"</string>
+ <string name="ping" msgid="7890607576220714932">"पिङ"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"परिणाम:"</string>
+ <string name="latency_title" msgid="963052613947017009">"ल्याटेन्सीलाई प्राथमिकता दिनुहोस्"</string>
+ <string name="bw_title" msgid="3902162973688221344">"ब्यान्डविथलाई प्राथमिकता दिनुहोस्"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Network Premium खरिद गर्नुहोस्"</string>
+ <string name="network_available" msgid="4780293262690730734">"परिणाम: अनुरोध गरिएको नेटवर्क अब उपलब्ध भएको छ!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"परिणाम: नेटवर्क अनुरोध गरिएको छ!"</string>
+ <string name="network_released" msgid="2992280481133877025">"परिणाम: नेटवर्क रिलिज गरिएको छ!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"परिणाम: नेटवर्क रिलिज गर्न सकिएन!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"परिणाम: Network Premium खरिद गर्दा अपवाद भयो!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"परिणाम: Network Premium खरिद गर्दा कुनै पनि परिणाम प्राप्त भएन!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"परिणाम: Network Premium खरिद गर्न मिल्दैन!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"परिणाम: Network Premium खरिद गरिँदै छ …"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-nl/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-nl/strings.xml
index 5d10fc6..9d98130 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-nl/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-nl/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hallo leeg fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Netwerk aanvragen"</string>
+ <string name="release_network" msgid="174252378593535238">"Netwerk vrijgeven"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Resultaat:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Vertraging prioriteit geven"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Bandbreedte prioriteit geven"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Netwerkpremium kopen"</string>
+ <string name="network_available" msgid="4780293262690730734">"Resultaat: Het gevraagde netwerk is nu beschikbaar."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Resultaat: Het netwerk is aangevraagd."</string>
+ <string name="network_released" msgid="2992280481133877025">"Resultaat: Het netwerk is vrijgegeven."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Resultaat: Het netwerk kan niet worden vrijgegeven."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Resultaat: Uitzondering bij kopen van netwerkpremium."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Resultaat: Er is een leeg resultaat geretourneerd bij het kopen van netwerkpremium."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Resultaat: Het netwerkpremium kan niet worden gekocht."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Resultaat: Netwerkpremium wordt gekocht..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-or/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-or/strings.xml
index 229ff5d..287aa01 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-or/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-or/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Networkର ଅନୁରୋଧ କରନ୍ତୁ"</string>
+ <string name="release_network" msgid="174252378593535238">"Network ରିଲିଜ କରନ୍ତୁ"</string>
+ <string name="ping" msgid="7890607576220714932">"ପିଂ"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"ଫଳାଫଳ:"</string>
+ <string name="latency_title" msgid="963052613947017009">"ଲେଟେନ୍ସିକୁ ପ୍ରାଥମିକତା ଦିଅନ୍ତୁ"</string>
+ <string name="bw_title" msgid="3902162973688221344">"ବେଣ୍ଡୱିଡଥକୁ ପ୍ରାଥମିକତା ଦିଅନ୍ତୁ"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Network Premium କିଣନ୍ତୁ"</string>
+ <string name="network_available" msgid="4780293262690730734">"ଫଳାଫଳ: ଅନୁରୋଧ କରାଯାଇଥିବା ନେଟୱାର୍କଟି ବର୍ତ୍ତମାନ ଉପଲବ୍ଧ ଅଛି!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"ଫଳାଫଳ: ନେଟୱାର୍କର ଅନୁରୋଧ କରାଯାଇଛି!"</string>
+ <string name="network_released" msgid="2992280481133877025">"ଫଳାଫଳ: ନେଟୱାର୍କକୁ ରିଲିଜ କରାଯାଇଛି!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"ଫଳାଫଳ: ନେଟୱାର୍କକୁ ରିଲିଜ କରିବାରେ ବିଫଳ ହୋଇଛି!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"ଫଳାଫଳ: Network Premium କିଣିବା ବେଳେ ବ୍ୟତିକ୍ରମ!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"ଫଳାଫଳ: Network Premium କିଣିବା ବେଳେ ଖାଲି ଫଳାଫଳ ମିଳିଲା!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"ଫଳାଫଳ: Network Premium କ୍ରୟ ପାଇଁ ଉପଲବ୍ଧ ନାହିଁ!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"ଫଳାଫଳ: Network Premium କିଣିବାର ପ୍ରକ୍ରିୟା ଚାଲିଛି ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-pa/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-pa/strings.xml
index 229ff5d..e210500 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-pa/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-pa/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Network ਦੀ ਬੇਨਤੀ ਕਰੋ"</string>
+ <string name="release_network" msgid="174252378593535238">"Network ਰਿਲੀਜ਼ ਕਰੋ"</string>
+ <string name="ping" msgid="7890607576220714932">"ਪਿੰਗ"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"ਨਤੀਜਾ:"</string>
+ <string name="latency_title" msgid="963052613947017009">"ਵਿਲੰਬਤਾ ਨੂੰ ਤਰਜੀਹ ਦਿਓ"</string>
+ <string name="bw_title" msgid="3902162973688221344">"ਬੈਂਡਵਿਡਥ ਨੂੰ ਤਰਜੀਹ ਦਿਓ"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Network Premium ਖਰੀਦੋ"</string>
+ <string name="network_available" msgid="4780293262690730734">"ਨਤੀਜਾ: ਬੇਨਤੀ ਕੀਤਾ ਨੈੱਟਵਰਕ ਹੁਣ ਉਪਲਬਧ ਹੈ!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"ਨਤੀਜਾ: ਨੈੱਟਵਰਕ ਦੇ ਲਈ ਬੇਨਤੀ ਕੀਤੀ ਗਈ ਹੈ!"</string>
+ <string name="network_released" msgid="2992280481133877025">"ਨਤੀਜਾ: ਨੈੱਟਵਰਕ ਨੂੰ ਰਿਲੀਜ਼ ਕੀਤਾ ਗਿਆ ਹੈ!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"ਨਤੀਜਾ: ਨੈੱਟਵਰਕ ਨੂੰ ਰਿਲੀਜ਼ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"ਨਤੀਜਾ: ਨੈੱਟਵਰਕ ਪ੍ਰੀਮੀਅਮ ਖਰੀਦਣ ਵੇਲੇ ਅਪਵਾਦ!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"ਨਤੀਜਾ: ਨੈੱਟਵਰਕ ਪ੍ਰੀਮੀਅਮ ਖਰੀਦਣ ਵੇਲੇ ਖਾਲੀ ਨਤੀਜਾ ਮਿਲਿਆ!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"ਨਤੀਜਾ: ਨੈੱਟਵਰਕ ਪ੍ਰੀਮੀਅਮ ਖਰੀਦ ਲਈ ਉਪਲਬਧ ਨਹੀਂ ਹੈ!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"ਨਤੀਜਾ: ਨੈੱਟਵਰਕ ਪ੍ਰੀਮੀਅਮ ਦੀ ਖਰੀਦ ਜਾਰੀ ਹੈ ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-pl/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-pl/strings.xml
index 0bf147b..21b1a78 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-pl/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-pl/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Tutaj jest pusty fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Poproś o dostęp do sieci"</string>
+ <string name="release_network" msgid="174252378593535238">"Zwolnij sieć"</string>
+ <string name="ping" msgid="7890607576220714932">"Dzyń"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Efekt:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Nadaj priorytet czasowi oczekiwania"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Nadaj priorytet przepustowości"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Kup sieć Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Efekt: żądana sieć jest już dostępna"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Efekt: zażądano dostępu do sieci"</string>
+ <string name="network_released" msgid="2992280481133877025">"Efekt: sieć została zwolniona"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Efekt: nie udało się zwolnić sieci"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Wynik: przy zakupie sieci premium pojawia się wyjątek"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Wynik: brak wyników w przypadku zakupu sieci premium"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Efekt: nie można kupić sieci premium"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Wynik: trwa kupowanie sieci premium…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-pt-rPT/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-pt-rPT/strings.xml
index b94a687..31c2720 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-pt-rPT/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-pt-rPT/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Olá, fragmento em branco"</string>
+ <string name="request_network" msgid="8945235490804849914">"Pedir rede"</string>
+ <string name="release_network" msgid="174252378593535238">"Lançar rede"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Resultado:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Priorizar latência"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Priorizar largura de banda"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Comprar serviço premium de rede"</string>
+ <string name="network_available" msgid="4780293262690730734">"Resultado: a rede pedida já está disponível!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Resultado: a rede foi pedida!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Resultado: a rede foi lançada!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Resultado: falha ao lançar a rede!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Resultado: exceção ao comprar o serviço premium de rede!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Resultado: obteve um resultado vazio ao comprar o serviço premium de rede!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Resultado: o serviço premium de rede não está disponível para compra!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Resultado: a compra do serviço premium de rede está em curso…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-pt/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-pt/strings.xml
index 229ff5d..b0b4b51 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-pt/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-pt/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Rede da solicitação"</string>
+ <string name="release_network" msgid="174252378593535238">"Rede de lançamento"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Resultado:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Priorizar a latência"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Priorizar a largura de banda"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Comprar Rede Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Resultado: a rede solicitada já está disponível."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Resultado: a rede foi solicitada."</string>
+ <string name="network_released" msgid="2992280481133877025">"Resultado: a rede foi liberada."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Resultado: falha ao liberar a rede."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Resultado: exceção ao comprar uma rede Premium."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Resultado: há um resultado vazio ao comprar a rede Premium."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Resultado: a rede Premium não está disponível para compra."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Resultado: a compra da rede Premium está em andamento..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ro/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ro/strings.xml
index 229ff5d..a5c81f6 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ro/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ro/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request Network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release Network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritize Latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritize Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ru/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ru/strings.xml
index 229ff5d..7763c62 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ru/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ru/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Запросить сеть"</string>
+ <string name="release_network" msgid="174252378593535238">"Освободить сеть"</string>
+ <string name="ping" msgid="7890607576220714932">"Запрос ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Результат."</string>
+ <string name="latency_title" msgid="963052613947017009">"Минимизировать задержку"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Минимизировать нагрузку на сеть"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Купить премиальную подписку на использование сети"</string>
+ <string name="network_available" msgid="4780293262690730734">"Результат: запрошенная сеть сейчас доступна."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Результат: сеть запрошена."</string>
+ <string name="network_released" msgid="2992280481133877025">"Результат: сеть освобождена."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Результат: не удалось освободить сеть."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Результат: при попытке купить премиальную подписку на использование сети возникло исключение."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Результат: получен пустой результат при попытке купить премиальную подписку на использование сети."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Результат: премиальная подписка на использование сети недоступна для покупки."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Результат: выполняется покупка премиальной подписки на использование сети."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-si/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-si/strings.xml
index 7c88afe..ed91dd7 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-si/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-si/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"ආයුබෝවන් හිස් කොටස"</string>
+ <string name="request_network" msgid="8945235490804849914">"ජාලය ඉල්ලන්න"</string>
+ <string name="release_network" msgid="174252378593535238">"ජාලය මුදා හරින්න"</string>
+ <string name="ping" msgid="7890607576220714932">"පිං"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"ප්රතිඵලය:"</string>
+ <string name="latency_title" msgid="963052613947017009">"පමාවට ප්රමුඛත්වය දෙන්න"</string>
+ <string name="bw_title" msgid="3902162973688221344">"කලාප පළලට ප්රමුඛත්වය දෙන්න"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"ජාල ප්රිමියම් මිල දී ගන්න"</string>
+ <string name="network_available" msgid="4780293262690730734">"ප්රතිඵලය: ඉල්ලූ ජාලය දැන් තිබේ!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"ප්රතිඵලය: ජාලය ඉල්ලා ඇත!"</string>
+ <string name="network_released" msgid="2992280481133877025">"ප්රතිඵලය: ජාලය මුදා හැර ඇත!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"ප්රතිඵලය: ජාලය මුදා හැරීමට අසමත් විය!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"ප්රතිඵලය: ජාල ප්රිමියම් මිල දී ගැනීමේ දී ව්යතිරේකයක්!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"ප්රතිඵලය: ජාල ප්රිමියම් මිල දී ගැනීමේ දී හිස් ප්රතිඵලයක් ලැබිණි!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"ප්රතිඵලය: ජාල ප්රිමියම් මිල දී ගැනීමට නොමැත!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"ප්රතිඵලය: ජාල ප්රිමියම් මිල දී ගැනීම සිදු වෙමින් පවතී ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-sk/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-sk/strings.xml
index 229ff5d..89628b4 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-sk/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-sk/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Vyžiadať sieť"</string>
+ <string name="release_network" msgid="174252378593535238">"Vydať sieť"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Výsledok:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Uprednostniť latenciu"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Uprednostniť rýchlosť pripojenia"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Kúpiť prémiovú sieť"</string>
+ <string name="network_available" msgid="4780293262690730734">"Výsledok: požadovaná sieť je už k dispozícii"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Výsledok: sieť bola vyžiadaná"</string>
+ <string name="network_released" msgid="2992280481133877025">"Výsledok: sieť bola vydaná"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Výsledok: sieť sa nepodarilo vydať"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Výsledok: výnimka pri nákupe prémiovej siete"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Výsledok: prázdny výsledok pri nákupe prémiovej siete"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Výsledok: prémiová sieť nie je k dispozícii na zakúpenie"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Výsledok: prebieha nákup prémiovej siete…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-sl/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-sl/strings.xml
index b3fd35a..0b94379 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-sl/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-sl/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Prazen del za pozdrav"</string>
+ <string name="request_network" msgid="8945235490804849914">"Zahtevanje omrežja"</string>
+ <string name="release_network" msgid="174252378593535238">"Sprostitev omrežja"</string>
+ <string name="ping" msgid="7890607576220714932">"Zven"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Rezultat:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Podelitev prednosti zakasnitvi"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Podelitev prednosti pasovni širini"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Nakup omrežne naročnine"</string>
+ <string name="network_available" msgid="4780293262690730734">"Rezultat: Zahtevano omrežje je zdaj na voljo."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Rezultat: Omrežje je bilo zahtevano."</string>
+ <string name="network_released" msgid="2992280481133877025">"Rezultat: Omrežje je sproščeno."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Rezultat: Sprostitev omrežja ni uspela."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Rezultat: Izjema pri nakupu omrežne naročnine."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Rezultat: Pri nakupu omrežne naročnine je prišlo do praznega rezultata."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Rezultat: Omrežna naročnina ni na voljo za nakup."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Rezultat: Nakup omrežne naročnine je v teku ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-sq/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-sq/strings.xml
index 229ff5d..ccbe35a 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-sq/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-sq/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Kërko rrjetin"</string>
+ <string name="release_network" msgid="174252378593535238">"Publiko rrjetin"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Rezultati:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Jepi përparësi vonesës"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Jepi përparësi gjerësisë së bandës"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Bli rrjetin premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Rezultati: Rrjeti i kërkuar ofrohet tani!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Rezultati: Rrjeti është kërkuar!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Rezultati: Rrjeti është publikuar!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Rezultati: Publikimi i rrjetit dështoi!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Rezultati: Përjashtim kur blihet rrjeti premium!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Rezultati: U mor një rezultat bosh kur u ble rrjeti premium!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Rezultati: Rrjeti premium nuk ofrohet për blerje!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Rezultati: Blerja e rrjetit premium është në vazhdim..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-sr/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-sr/strings.xml
index 229ff5d..60ba624 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-sr/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-sr/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Затражите мрежу"</string>
+ <string name="release_network" msgid="174252378593535238">"Објавите мрежу"</string>
+ <string name="ping" msgid="7890607576220714932">"Пинг"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Резултат:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Дајте приоритет кашњењу"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Дајте приоритет пропусном опсегу"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Купите Premium мрежу"</string>
+ <string name="network_available" msgid="4780293262690730734">"Резултат: Захтевана мрежа је тренутно доступна!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Резултат: Мрежа је затражена!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Резултат: Мрежа је објављена!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Резултат: Објављивање мреже није успело!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Резултат: Изузетак при куповини Premium мреже!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Резултат: Резултат је празан када купујете Premium мрежу!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Резултат: Premium мрежа није доступна за куповину!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Резултат: Куповина Premium мреже је у току..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-sv/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-sv/strings.xml
index 229ff5d..a5c81f6 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-sv/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-sv/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request Network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release Network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritize Latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritize Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-sw/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-sw/strings.xml
index 5cc6a6a..c01aa3a 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-sw/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-sw/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Kipande cha salamu kisichokuwa na kitu"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request Network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release Network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritize Latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritize Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ta/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ta/strings.xml
index 229ff5d..2a05673 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ta/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ta/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"நெட்வொர்க்கைக் கோரு"</string>
+ <string name="release_network" msgid="174252378593535238">"நெட்வொர்க்கை வெளியிடு"</string>
+ <string name="ping" msgid="7890607576220714932">"பிங்"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"முடிவு:"</string>
+ <string name="latency_title" msgid="963052613947017009">"தாமதத்திற்கு முன்னுரிமை வழங்கு"</string>
+ <string name="bw_title" msgid="3902162973688221344">"இணைய வேகத்திற்கு முன்னுரிமை வழங்கு"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"நெட்வொர்க் பிரீமியத்தை வாங்கு"</string>
+ <string name="network_available" msgid="4780293262690730734">"முடிவு: கோரப்பட்ட நெட்வொர்க் இப்போது கிடைக்கிறது!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"முடிவு: நெட்வொர்க் கோரப்பட்டுள்ளது!"</string>
+ <string name="network_released" msgid="2992280481133877025">"முடிவு: நெட்வொர்க் வெளியிடப்பட்டுள்ளது!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"முடிவு: நெட்வொர்க்கை வெளியிட முடியவில்லை!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"முடிவு: நெட்வொர்க் பிரீமியம் வாங்கும்போது விதிவிலக்கு!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"முடிவு: நெட்வொர்க் பிரீமியம் வாங்கும்போது முடிவு எதுவும் கிடைக்கவில்லை!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"முடிவு: வாங்குவதற்கு நெட்வொர்க் பிரீமியம் கிடைக்கவில்லை!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"முடிவு: நெட்வொர்க் பிரீமியம் வாங்கப்படுகிறது..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-te/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-te/strings.xml
index ad9cef8..43d9a00 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-te/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-te/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"హలో ఖాళీ విడిభాగము"</string>
+ <string name="request_network" msgid="8945235490804849914">"రిక్వెస్ట్ నెట్వర్క్"</string>
+ <string name="release_network" msgid="174252378593535238">"నెట్వర్క్ను రిలీజ్ చేయండి"</string>
+ <string name="ping" msgid="7890607576220714932">"పింగ్"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"ఫలితం:"</string>
+ <string name="latency_title" msgid="963052613947017009">"ప్రతిస్పందన సమయానికి ప్రాధాన్యత ఇవ్వండి"</string>
+ <string name="bw_title" msgid="3902162973688221344">"బ్యాండ్విడ్త్కు ప్రాధాన్యత ఇవ్వండి"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"ప్రీమియం నెట్వర్క్ను కొనుగోలు చేయండి"</string>
+ <string name="network_available" msgid="4780293262690730734">"ఫలితం: రిక్వెస్ట్ చేసిన నెట్వర్క్ ఇప్పుడు అందుబాటులో ఉంది!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"ఫలితం: నెట్వర్క్ రిక్వెస్ట్ చేయబడింది!"</string>
+ <string name="network_released" msgid="2992280481133877025">"ఫలితం: నెట్వర్క్ రిలీజ్ చేయబడింది!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"ఫలితం: నెట్వర్క్ను రిలీజ్ చేయడంలో విఫలమైంది!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"ఫలితం: ప్రీమియం నెట్వర్క్ను కొనుగోలు చేసేటప్పుడు అరుదైన ఘటన సంభవించింది!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"ఫలితం: ప్రీమియం నెట్వర్క్ను కొనుగోలు చేసినప్పుడు ఖాళీ ఫలితం వచ్చింది!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"ఫలితం: కొనుగోలు కోసం ప్రీమియం నెట్వర్క్ అందుబాటులో లేదు!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"ఫలితం: ప్రీమియం నెట్వర్క్ కొనుగోలు ప్రోగ్రెస్లో ఉంది ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-th/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-th/strings.xml
index 229ff5d..f0dbee8 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-th/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-th/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"ขอใช้งานเครือข่าย"</string>
+ <string name="release_network" msgid="174252378593535238">"เผยแพร่เครือข่าย"</string>
+ <string name="ping" msgid="7890607576220714932">"ใช้คำสั่ง ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"ผลลัพธ์:"</string>
+ <string name="latency_title" msgid="963052613947017009">"ตั้งเป็นเวลาในการตอบสนองสำคัญ"</string>
+ <string name="bw_title" msgid="3902162973688221344">"ตั้งเป็นแบนด์วิดท์สำคัญ"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"ซื้อเครือข่ายระดับพรีเมียม"</string>
+ <string name="network_available" msgid="4780293262690730734">"ผลลัพธ์: เครือข่ายที่ต้องการพร้อมใช้งานแล้ว"</string>
+ <string name="network_requested" msgid="5646123922691865991">"ผลลัพธ์: ขอใช้งานเครือข่ายแล้ว"</string>
+ <string name="network_released" msgid="2992280481133877025">"ผลลัพธ์: เผยแพร่เครือข่ายแล้ว"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"ผลลัพธ์: เผยแพร่เครือข่ายไม่สําเร็จ"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"ผลลัพธ์: ข้อยกเว้นในการซื้อเครือข่ายระดับพรีเมียม"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"ผลลัพธ์: ได้ผลลัพธ์ว่างเปล่าเมื่อซื้อเครือข่ายระดับพรีเมียม"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"ผลลัพธ์: เครือข่ายระดับพรีเมียมยังไม่พร้อมให้จำหน่าย"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"ผลลัพธ์: กําลังดําเนินการซื้อเครือข่ายระดับพรีเมียม ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-tl/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-tl/strings.xml
index 229ff5d..50897f6 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-tl/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-tl/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Hilingin ang Network"</string>
+ <string name="release_network" msgid="174252378593535238">"I-release ang Network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Resulta:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Isapriyoridad ang Latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Isapriyoridad ang Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Bilhin ang Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Resulta: Available na ang hiniling na network!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Resulta: Hiniling na ang network!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Resulta: Na-release na ang network!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Resulta: Hindi na-release ang network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Resulta: Nagkakaroon ng exception noong binili ang network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Resulta: Walang nakuhang resulta noong binili ang network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Resulta: Hindi available ang network premium para bilhin!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Resulta: Kasalukuyang binibili ang network premium..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-tr/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-tr/strings.xml
index 1e18a88..d8c2751 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-tr/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-tr/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Merhaba boş bölüm"</string>
+ <string name="request_network" msgid="8945235490804849914">"Request Network"</string>
+ <string name="release_network" msgid="174252378593535238">"Release Network"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Result:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Prioritize Latency"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Prioritize Bandwidth"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Purchase Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Result: The requested network is available now!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Result: The network has been requested!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Result: The network has been released!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Result: Failed to release the network!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Result: Exception when purchasing network premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Result: Got empty result when purchasing network premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Result: The network premium purchase is in progress ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-uk/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-uk/strings.xml
index 0219615..a5f6921 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-uk/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-uk/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Пустий фрагмент із привітанням"</string>
+ <string name="request_network" msgid="8945235490804849914">"Надіслати запит на мережу"</string>
+ <string name="release_network" msgid="174252378593535238">"Випустити мережу"</string>
+ <string name="ping" msgid="7890607576220714932">"Перевірити"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Результат:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Надати пріоритет затримці"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Надати пріоритет пропускній спроможності"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Придбати преміум-доступ до мережі"</string>
+ <string name="network_available" msgid="4780293262690730734">"Результат: потрібна мережа тепер доступна."</string>
+ <string name="network_requested" msgid="5646123922691865991">"Результат: надіслано запит на мережу."</string>
+ <string name="network_released" msgid="2992280481133877025">"Результат: мережу звільнено."</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Результат: не вдалося звільнити мережу."</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Результат: виняток під час покупки преміум-доступу до мережі."</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Результат: під час покупки преміум-доступу до мережі отримано пустий результат."</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Результат: преміум-доступ до мережі недоступний для купівлі."</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Результат: триває процес купівлі преміум-доступу до мережі."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-ur/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-ur/strings.xml
index 229ff5d..1068cf1 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-ur/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-ur/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"نیٹ ورک کی درخواست کریں"</string>
+ <string name="release_network" msgid="174252378593535238">"نیٹ ورک ریلیز کریں"</string>
+ <string name="ping" msgid="7890607576220714932">"پنگ"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"نتیجہ:"</string>
+ <string name="latency_title" msgid="963052613947017009">"تاخیر کو ترجیح دیں"</string>
+ <string name="bw_title" msgid="3902162973688221344">"بینڈوتھ کو ترجیح دیں"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"نیٹ ورک پریمیم خریدیں"</string>
+ <string name="network_available" msgid="4780293262690730734">"نتیجہ: درخواست کردہ نیٹ ورک اب دستیاب ہے!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"نتیجہ: نیٹ ورک کی درخواست کی گئی ہے!"</string>
+ <string name="network_released" msgid="2992280481133877025">"نتیجہ: نیٹ ورک ریلیز کر دیا گیا ہے!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"نتیجہ: نیٹ ورک ریلیز کرنے میں ناکام!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"نتیجہ: نیٹ ورک پریمیم کی خریداری کرتے وقت استثنا!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"نتیجہ: نیٹ ورک پریمیم کی خریداری کرتے وقت خالی نتیجہ ملا!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"نتیجہ: نیٹ ورک پریمیم خریداری کے لیے دستیاب نہیں ہے!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"نتیجہ: نیٹ ورک پریمیم خریداری جاری ہے..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-uz/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-uz/strings.xml
index 229ff5d..a0d4547 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-uz/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-uz/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Tarmoqni talab qilish"</string>
+ <string name="release_network" msgid="174252378593535238">"Tarmoqni chiqarish"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Natija:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Kechikish vaqti ustuvorligi"</string>
+ <string name="bw_title" msgid="3902162973688221344">"O‘tkazuvchanlik qobiliyati ustuvorligi"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Tarmoq premiumni xarid qilish"</string>
+ <string name="network_available" msgid="4780293262690730734">"Natija: Talab qilingan tarmoq hozir mavjud!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Natija: Tarmoq talab qilindi!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Natija: Tarmoq chiqarildi!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Natija: Tarmoqni chiqarib bo‘lmadi!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Natija: Tarmoq premiumni xarid qilishda istisno!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Natija: Tarmoq premiumni xarid qilayotganda bo‘sh natijaga erishildi!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Natija: Tarmoq premiumni xarid qilib bo‘lmaydi!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Natija: Tarmoq premium xaridi davom etmoqda ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-vi/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-vi/strings.xml
index 229ff5d..75bb2a3 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-vi/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-vi/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Yêu cầu mạng"</string>
+ <string name="release_network" msgid="174252378593535238">"Phát hành mạng"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Kết quả:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Ưu tiên độ trễ"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Ưu tiên băng thông"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Mua dịch vụ đặc biệt cho mạng"</string>
+ <string name="network_available" msgid="4780293262690730734">"Kết quả: Mạng bạn yêu cầu hiện đã sử dụng được!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Kết quả: Mạng đã được yêu cầu!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Kết quả: Mạng đã được phát hành!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Kết quả: Không phát hành được mạng!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Kết quả: Ngoại lệ khi mua dịch vụ đặc biệt cho mạng!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Kết quả: Đã nhận kết quả không có nội dung khi mua dịch vụ đặc biệt cho mạng!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Kết quả: Không thể mua dịch vụ đặc biệt cho mạng!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Kết quả: Đang xử lý yêu cầu mua dịch vụ đặc biệt cho mạng ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-zh-rCN/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-zh-rCN/strings.xml
index 229ff5d..0f22142 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-zh-rCN/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-zh-rCN/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"请求网络"</string>
+ <string name="release_network" msgid="174252378593535238">"释放网络"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"结果:"</string>
+ <string name="latency_title" msgid="963052613947017009">"划分延迟时间优先级"</string>
+ <string name="bw_title" msgid="3902162973688221344">"划分带宽优先级"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"购买 Network Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"结果:请求的网络现已可用!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"结果:已请求网络!"</string>
+ <string name="network_released" msgid="2992280481133877025">"结果:已释放网络!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"结果:未能释放网络!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"结果:购买 Network Premium 时出现异常!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"结果:购买 Network Premium 时获得空的结果!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"结果:无法购买 Network Premium!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"结果:正在购买 Network Premium…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-zh-rHK/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-zh-rHK/strings.xml
index 93c0492..18e7286 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-zh-rHK/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-zh-rHK/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"歡迎詞空白片段"</string>
+ <string name="request_network" msgid="8945235490804849914">"要求網絡"</string>
+ <string name="release_network" msgid="174252378593535238">"釋出網絡"</string>
+ <string name="ping" msgid="7890607576220714932">"Ping"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"結果:"</string>
+ <string name="latency_title" msgid="963052613947017009">"以延遲時間為優先"</string>
+ <string name="bw_title" msgid="3902162973688221344">"以頻寬為優先"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"購買進階網絡"</string>
+ <string name="network_available" msgid="4780293262690730734">"結果:要求的網絡現可使用!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"結果:已要求網絡!"</string>
+ <string name="network_released" msgid="2992280481133877025">"結果:已釋出網絡!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"結果:無法釋出網絡!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"結果:購買進階網絡時出現例外情況!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"結果:購買進階網絡時獲得空白結果!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Result: The network premium is not available for purchase!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"結果:進階網絡購買過程正在進行…"</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-zh-rTW/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-zh-rTW/strings.xml
index 229ff5d..3307122 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-zh-rTW/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-zh-rTW/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"要求網路"</string>
+ <string name="release_network" msgid="174252378593535238">"發布網路"</string>
+ <string name="ping" msgid="7890607576220714932">"連線偵測 (ping)"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"結果:"</string>
+ <string name="latency_title" msgid="963052613947017009">"優先處理延遲"</string>
+ <string name="bw_title" msgid="3902162973688221344">"優先處理頻寬"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"購買網路付費項目"</string>
+ <string name="network_available" msgid="4780293262690730734">"結果:要求的網路現已可供使用!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"結果:已要求網路!"</string>
+ <string name="network_released" msgid="2992280481133877025">"結果:已發布網路!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"結果:無法發布網路!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"結果:購買網路付費項目時發生例外狀況!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"結果:購買網路付費項目時收到空白結果!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"結果:無法購買網路付費項目!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"結果:正在購買網路付費項目..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values-zu/strings.xml b/testapps/TestSliceApp/app/src/main/res/values-zu/strings.xml
index 229ff5d..3f329f3 100644
--- a/testapps/TestSliceApp/app/src/main/res/values-zu/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values-zu/strings.xml
@@ -3,4 +3,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1265450418387661962">"TestSliceApp"</string>
<string name="hello_blank_fragment" msgid="1245093642770491175">"Hello blank fragment"</string>
+ <string name="request_network" msgid="8945235490804849914">"Cela Inethiwekhi"</string>
+ <string name="release_network" msgid="174252378593535238">"Khulula Inethiwekhi"</string>
+ <string name="ping" msgid="7890607576220714932">"Qhweba"</string>
+ <string name="result_prefix" msgid="3522796186427501399">"Umphumela:"</string>
+ <string name="latency_title" msgid="963052613947017009">"Beka kuqala Ukubambezeleka"</string>
+ <string name="bw_title" msgid="3902162973688221344">"Beka kuqala Umkhawulokudonsa"</string>
+ <string name="cbs_title" msgid="5234410535569935600">"I-CBS"</string>
+ <string name="purchase" msgid="7843181995697372128">"Thenga Inethiwekhi ye-Premium"</string>
+ <string name="network_available" msgid="4780293262690730734">"Umphumela: Inethiwekhi eceliwe isiyatholakala manje!"</string>
+ <string name="network_requested" msgid="5646123922691865991">"Umphumela: Inethiwekhi iceliwe!"</string>
+ <string name="network_released" msgid="2992280481133877025">"Umphumela: Inethiwekhi isikhishiwe!"</string>
+ <string name="network_release_failed" msgid="256471231420029151">"Umphumela: Yehlulekile ukukhipha inethiwekhi!!!"</string>
+ <string name="purchase_exception" msgid="8876841120055716671">"Umphumela: Okuhlukile lapho uthenga nethiwekhi ye-premium!!!"</string>
+ <string name="purchase_empty_result" msgid="7497824191649973928">"Umphumela: Uthole umphumela ongenalutho lapho uthenga wenethiwekhi ye-premium!!!"</string>
+ <string name="premium_not_available" msgid="7346368693802644748">"Umphumela: Inethiwekhi ye-premium ayitholakaleli ukuthengwa!!!"</string>
+ <string name="purchase_in_progress" msgid="5450288183685032424">"Umphumela: Ukuthengwa kwenethiwekhi ye-premium kuyaqhubeka ..."</string>
</resources>
diff --git a/testapps/TestSliceApp/app/src/main/res/values/strings.xml b/testapps/TestSliceApp/app/src/main/res/values/strings.xml
index c2bb089..4990cec 100644
--- a/testapps/TestSliceApp/app/src/main/res/values/strings.xml
+++ b/testapps/TestSliceApp/app/src/main/res/values/strings.xml
@@ -1,5 +1,20 @@
-<resources>
+<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">TestSliceApp</string>
- <!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
+ <string name="request_network">Request Network</string>
+ <string name="release_network">Release Network</string>
+ <string name="ping">Ping</string>
+ <string name="result_prefix">Result: </string>
+ <string name="latency_title">Prioritize Latency</string>
+ <string name="bw_title">Prioritize Bandwidth</string>
+ <string name="cbs_title">CBS</string>
+ <string name="purchase">Purchase Network Premium</string>
+ <string name="network_available">Result: The requested network is available now!</string>
+ <string name="network_requested">Result: The network has been requested!</string>
+ <string name="network_released">Result: The network has been released!</string>
+ <string name="network_release_failed">Result: Failed to release the network!!!</string>
+ <string name="purchase_exception">Result: Exception when purchasing network premium!!!</string>
+ <string name="purchase_empty_result">Result: Got empty result when purchasing network premium!!!</string>
+ <string name="premium_not_available">Result: The network premium is not available for purchase!!!</string>
+ <string name="purchase_in_progress">Result: The network premium purchase is in progress ...</string>
</resources>
\ No newline at end of file
diff --git a/testapps/TestSliceApp/app/src/main/res/xml/self_certified_network_capabilities_both.xml b/testapps/TestSliceApp/app/src/main/res/xml/self_certified_network_capabilities_both.xml
new file mode 100644
index 0000000..cd4d370
--- /dev/null
+++ b/testapps/TestSliceApp/app/src/main/res/xml/self_certified_network_capabilities_both.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<network-capabilities-declaration xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-network-capability android:name="NET_CAPABILITY_PRIORITIZE_LATENCY"/>
+ <uses-network-capability android:name="NET_CAPABILITY_PRIORITIZE_BANDWIDTH"/>
+</network-capabilities-declaration>
\ No newline at end of file
diff --git a/tests/src/com/android/TelephonyTestBase.java b/tests/src/com/android/TelephonyTestBase.java
index ffda81b..d72d85e 100644
--- a/tests/src/com/android/TelephonyTestBase.java
+++ b/tests/src/com/android/TelephonyTestBase.java
@@ -24,10 +24,16 @@
import com.android.internal.telephony.PhoneConfigurationManager;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -40,6 +46,10 @@
protected TestContext mContext;
+ private final HashMap<InstanceKey, Object> mOldInstances = new HashMap<>();
+ private final LinkedList<InstanceKey> mInstanceKeys = new LinkedList<>();
+
+ @Before
public void setUp() throws Exception {
mContext = spy(new TestContext());
// Set up the looper if it does not exist on the test thread.
@@ -56,9 +66,11 @@
}
}
+ @After
public void tearDown() throws Exception {
// Ensure there are no static references to handlers after test completes.
PhoneConfigurationManager.unregisterAllMultiSimConfigChangeRegistrants();
+ restoreInstances();
}
protected final boolean waitForExecutorAction(Executor executor, long timeoutMillis) {
@@ -108,6 +120,61 @@
}
}
+ protected synchronized void replaceInstance(final Class c, final String instanceName,
+ final Object obj, final Object newValue)
+ throws Exception {
+ Field field = c.getDeclaredField(instanceName);
+ field.setAccessible(true);
+
+ InstanceKey key = new InstanceKey(c, instanceName, obj);
+ if (!mOldInstances.containsKey(key)) {
+ mOldInstances.put(key, field.get(obj));
+ mInstanceKeys.add(key);
+ }
+ field.set(obj, newValue);
+ }
+
+ private synchronized void restoreInstances() throws Exception {
+ Iterator<InstanceKey> it = mInstanceKeys.descendingIterator();
+
+ while (it.hasNext()) {
+ InstanceKey key = it.next();
+ Field field = key.mClass.getDeclaredField(key.mInstName);
+ field.setAccessible(true);
+ field.set(key.mObj, mOldInstances.get(key));
+ }
+
+ mInstanceKeys.clear();
+ mOldInstances.clear();
+ }
+
+ private static class InstanceKey {
+ public final Class mClass;
+ public final String mInstName;
+ public final Object mObj;
+ InstanceKey(final Class c, final String instName, final Object obj) {
+ mClass = c;
+ mInstName = instName;
+ mObj = obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return (mClass.getName().hashCode() * 31 + mInstName.hashCode()) * 31;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+
+ InstanceKey other = (InstanceKey) obj;
+ return (other.mClass == mClass && other.mInstName.equals(mInstName)
+ && other.mObj == mObj);
+ }
+ }
+
protected TestContext getTestContext() {
return mContext;
}
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
index 7c3a842..111df53 100644
--- a/tests/src/com/android/TestContext.java
+++ b/tests/src/com/android/TestContext.java
@@ -17,6 +17,7 @@
package com.android;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import android.content.AttributionSource;
@@ -61,7 +62,11 @@
@Mock ImsManager mMockImsManager;
@Mock UserManager mMockUserManager;
- private SparseArray<PersistableBundle> mCarrierConfigs = new SparseArray<>();
+ private final SparseArray<PersistableBundle> mCarrierConfigs = new SparseArray<>();
+
+ private Intent mIntent;
+
+ private BroadcastReceiver mReceiver;
private final HashSet<String> mPermissionTable = new HashSet<>();
@@ -69,13 +74,12 @@
MockitoAnnotations.initMocks(this);
doAnswer((Answer<PersistableBundle>) invocation -> {
int subId = (int) invocation.getArguments()[0];
- if (subId < 0) {
- return new PersistableBundle();
- }
- PersistableBundle b = mCarrierConfigs.get(subId);
-
- return (b != null ? b : new PersistableBundle());
+ return getTestConfigs(subId);
}).when(mMockCarrierConfigManager).getConfigForSubId(anyInt());
+ doAnswer((Answer<PersistableBundle>) invocation -> {
+ int subId = (int) invocation.getArguments()[0];
+ return getTestConfigs(subId);
+ }).when(mMockCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
}
@Override
@@ -105,28 +109,42 @@
}
@Override
+ public void sendBroadcast(Intent intent) {
+ mIntent = intent;
+ }
+
+ @Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ mReceiver = receiver;
return null;
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+ mReceiver = receiver;
return null;
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
+ mReceiver = receiver;
return null;
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler, int flags) {
+ mReceiver = receiver;
return null;
}
@Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ mReceiver = null;
+ }
+
+ @Override
public ContentResolver getContentResolver() {
return null;
}
@@ -134,22 +152,22 @@
@Override
public Object getSystemService(String name) {
switch (name) {
- case (Context.CARRIER_CONFIG_SERVICE) : {
+ case Context.CARRIER_CONFIG_SERVICE: {
return mMockCarrierConfigManager;
}
- case (Context.TELECOM_SERVICE) : {
+ case Context.TELECOM_SERVICE: {
return mMockTelecomManager;
}
- case (Context.TELEPHONY_SERVICE) : {
+ case Context.TELEPHONY_SERVICE: {
return mMockTelephonyManager;
}
- case (Context.TELEPHONY_SUBSCRIPTION_SERVICE) : {
+ case Context.TELEPHONY_SUBSCRIPTION_SERVICE: {
return mMockSubscriptionManager;
}
- case(Context.TELEPHONY_IMS_SERVICE) : {
+ case Context.TELEPHONY_IMS_SERVICE: {
return mMockImsManager;
}
- case(Context.USER_SERVICE) : {
+ case Context.USER_SERVICE: {
return mMockUserManager;
}
}
@@ -170,6 +188,9 @@
if (serviceClass == SubscriptionManager.class) {
return Context.TELEPHONY_SUBSCRIPTION_SERVICE;
}
+ if (serviceClass == ImsManager.class) {
+ return Context.TELEPHONY_IMS_SERVICE;
+ }
if (serviceClass == UserManager.class) {
return Context.USER_SERVICE;
}
@@ -231,18 +252,16 @@
public void grantPermission(String permission) {
synchronized (mPermissionTable) {
- if (mPermissionTable != null && permission != null) {
- mPermissionTable.remove(STUB_PERMISSION_ENABLE_ALL);
- mPermissionTable.add(permission);
- }
+ if (permission == null) return;
+ mPermissionTable.remove(STUB_PERMISSION_ENABLE_ALL);
+ mPermissionTable.add(permission);
}
}
public void revokePermission(String permission) {
synchronized (mPermissionTable) {
- if (mPermissionTable != null && permission != null) {
- mPermissionTable.remove(permission);
- }
+ if (permission == null) return;
+ mPermissionTable.remove(permission);
}
}
@@ -252,6 +271,22 @@
}
}
+ public Intent getBroadcast() {
+ return mIntent;
+ }
+
+ public BroadcastReceiver getBroadcastReceiver() {
+ return mReceiver;
+ }
+
+ private PersistableBundle getTestConfigs(int subId) {
+ if (subId < 0) {
+ return new PersistableBundle();
+ }
+ PersistableBundle b = getCarrierConfig(subId);
+ return (b != null ? b : new PersistableBundle());
+ }
+
private static void logd(String s) {
Log.d(TAG, s);
}
diff --git a/tests/src/com/android/phone/CarrierConfigLoaderTest.java b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
index 5473c94..b6f8ed8 100644
--- a/tests/src/com/android/phone/CarrierConfigLoaderTest.java
+++ b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -39,7 +40,6 @@
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Message;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.service.carrier.CarrierIdentifier;
@@ -54,7 +54,7 @@
import com.android.TelephonyTestBase;
import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.SubscriptionInfoUpdater;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import org.junit.After;
import org.junit.Before;
@@ -63,6 +63,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -86,7 +87,7 @@
@Mock Resources mResources;
@Mock PackageManager mPackageManager;
@Mock PackageInfo mPackageInfo;
- @Mock SubscriptionInfoUpdater mSubscriptionInfoUpdater;
+ @Mock SubscriptionManagerService mSubscriptionManagerService;
@Mock SharedPreferences mSharedPreferences;
@Mock TelephonyRegistryManager mTelephonyRegistryManager;
@@ -99,6 +100,9 @@
@Before
public void setUp() throws Exception {
super.setUp();
+ MockitoAnnotations.initMocks(this);
+ replaceInstance(SubscriptionManagerService.class, "sInstance", null,
+ mSubscriptionManagerService);
// TODO: replace doReturn/when with when/thenReturn which is more readable
doReturn(mSharedPreferences).when(mContext).getSharedPreferences(anyString(), anyInt());
@@ -128,8 +132,7 @@
mHandlerThread.start();
mTestableLooper = new TestableLooper(mHandlerThread.getLooper());
- mCarrierConfigLoader = new CarrierConfigLoader(mContext, mSubscriptionInfoUpdater,
- mTestableLooper.getLooper());
+ mCarrierConfigLoader = new CarrierConfigLoader(mContext, mTestableLooper.getLooper());
mHandler = mCarrierConfigLoader.getHandler();
// Clear all configs to have the same starting point.
@@ -206,6 +209,7 @@
* will return the right config in the XML.
*/
@Test
+ @Ignore("b/257169357")
public void testUpdateConfigForPhoneId_simLoaded_withCachedConfigInXml() throws Exception {
// Bypass case if default subId is not supported by device to reduce flakiness
if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
@@ -270,9 +274,9 @@
mTestableLooper.processAllMessages();
assertThat(mCarrierConfigLoader.getOverrideConfig(DEFAULT_PHONE_ID).isEmpty()).isTrue();
- verify(mSubscriptionInfoUpdater).updateSubscriptionByCarrierConfigAndNotifyComplete(
+ verify(mSubscriptionManagerService).updateSubscriptionByCarrierConfig(
eq(DEFAULT_PHONE_ID), eq(PLATFORM_CARRIER_CONFIG_PACKAGE),
- any(PersistableBundle.class), any(Message.class));
+ any(PersistableBundle.class), any(Runnable.class));
}
/**
@@ -293,9 +297,9 @@
assertThat(mCarrierConfigLoader.getOverrideConfig(DEFAULT_PHONE_ID).getInt(
CARRIER_CONFIG_EXAMPLE_KEY)).isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
- verify(mSubscriptionInfoUpdater).updateSubscriptionByCarrierConfigAndNotifyComplete(
+ verify(mSubscriptionManagerService).updateSubscriptionByCarrierConfig(
eq(DEFAULT_PHONE_ID), eq(PLATFORM_CARRIER_CONFIG_PACKAGE),
- any(PersistableBundle.class), any(Message.class));
+ any(PersistableBundle.class), any(Runnable.class));
}
/**
@@ -409,4 +413,21 @@
: TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS).when(
mockTelephonyManager).getCarrierPrivilegeStatus(anyInt());
}
+
+ @Test
+ public void testMultiSimConfigChanged() throws Exception {
+ replaceInstance(TelephonyManager.class, "sInstance", null, mTelephonyManager);
+ mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+ // Changed from 1 to 2.
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
+ doReturn(true).when(mContext).bindService(
+ any(Intent.class), any(ServiceConnection.class), anyInt());
+ doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class));
+ mHandler.sendMessage(mHandler.obtainMessage(17 /* EVENT_MULTI_SIM_CONFIG_CHANGED */));
+ mTestableLooper.processAllMessages();
+
+ mCarrierConfigLoader.updateConfigForPhoneId(1, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+ mTestableLooper.processAllMessages();
+ }
}
diff --git a/tests/src/com/android/phone/DiagnosticDataCollectorTest.java b/tests/src/com/android/phone/DiagnosticDataCollectorTest.java
new file mode 100644
index 0000000..e0d89bc
--- /dev/null
+++ b/tests/src/com/android/phone/DiagnosticDataCollectorTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 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.phone;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.DropBoxManager;
+import android.telephony.TelephonyManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * Unit Tests for DiagnosticDataCollector.
+ */
+@RunWith(JUnit4.class)
+public class DiagnosticDataCollectorTest {
+
+ private static final String[] TELECOM_DUMPSYS_COMMAND =
+ {"/system/bin/dumpsys", "telecom", "EmergencyDiagnostics"};
+ private static final String[] TELEPHONY_DUMPSYS_COMMAND =
+ {"/system/bin/dumpsys", "telephony.registry", "EmergencyDiagnostics"};
+ private static final String[] LOGCAT_BINARY = {"/system/bin/logcat"};
+
+
+ @Mock
+ DataCollectorConfig.Adapter mConfig;
+ private Runtime mRuntime;
+
+ @Mock
+ private DropBoxManager mDropBoxManager;
+
+ private DiagnosticDataCollector mDiagnosticDataCollector;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mRuntime = spy(Runtime.getRuntime());
+ mDiagnosticDataCollector = new DiagnosticDataCollector(mRuntime, Runnable::run,
+ mDropBoxManager, false);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ private void verifyCmdAndDropboxTag(String[] cmd, String tag, boolean startsWithMatch)
+ throws InterruptedException, IOException {
+ ArgumentCaptor<String[]> textArrayCaptor = ArgumentCaptor.forClass(String[].class);
+
+ //verify cmd passed to runtime
+ verify(mRuntime).exec(textArrayCaptor.capture());
+ String[] argList = textArrayCaptor.getValue();
+ if (startsWithMatch) {
+ assertEquals(cmd[0], argList[0]);
+ } else {
+ assertEquals(Arrays.toString(cmd), Arrays.toString(argList));
+ }
+ ArgumentCaptor<String> textCaptor = ArgumentCaptor.forClass(String.class);
+
+ //make sure logcat output does not have errors
+ verify(mDropBoxManager, times(1)).addText(eq(tag), textCaptor.capture());
+ assertFalse(textCaptor.getValue().contains(DiagnosticDataCollector.ERROR_MSG));
+ }
+
+ @Test
+ public void testPersistForTelecomDumpsys() throws IOException, InterruptedException {
+ TelephonyManager.EmergencyCallDiagnosticParams dp =
+ new TelephonyManager.EmergencyCallDiagnosticParams();
+ dp.setTelecomDumpSysCollection(true);
+ mDiagnosticDataCollector.persistEmergencyDianosticData(mConfig, dp, "test_tag_telecom");
+
+ verifyCmdAndDropboxTag(TELECOM_DUMPSYS_COMMAND, "test_tag_telecom", false);
+ }
+
+ @Test
+ public void testPersistForTelephonyDumpsys() throws IOException, InterruptedException {
+ TelephonyManager.EmergencyCallDiagnosticParams dp =
+ new TelephonyManager.EmergencyCallDiagnosticParams();
+ dp.setTelephonyDumpSysCollection(true);
+ mDiagnosticDataCollector.persistEmergencyDianosticData(mConfig, dp, "test_tag_telephony");
+
+ verifyCmdAndDropboxTag(TELEPHONY_DUMPSYS_COMMAND, "test_tag_telephony", false);
+ }
+
+ @Test
+ public void testPersistForLogcat() throws IOException, InterruptedException {
+ TelephonyManager.EmergencyCallDiagnosticParams dp =
+ new TelephonyManager.EmergencyCallDiagnosticParams();
+ dp.setLogcatCollection(true, System.currentTimeMillis());
+ mDiagnosticDataCollector.persistEmergencyDianosticData(mConfig, dp, "test_tag_logcat");
+
+ verifyCmdAndDropboxTag(LOGCAT_BINARY, "test_tag_logcat", true);
+ }
+
+}
diff --git a/tests/src/com/android/phone/ImsProvisioningControllerTest.java b/tests/src/com/android/phone/ImsProvisioningControllerTest.java
index 2094e20..db83cca 100644
--- a/tests/src/com/android/phone/ImsProvisioningControllerTest.java
+++ b/tests/src/com/android/phone/ImsProvisioningControllerTest.java
@@ -63,6 +63,7 @@
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.aidl.IFeatureProvisioningCallback;
import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
import android.telephony.ims.stub.ImsConfigImplBase;
@@ -159,6 +160,9 @@
@Mock
IFeatureProvisioningCallback mIFeatureProvisioningCallback1;
+ @Captor
+ ArgumentCaptor<IImsConfigCallback> mIImsConfigCallback;
+
@Mock
IBinder mIbinder0;
@Mock
@@ -347,6 +351,8 @@
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
+ verify(mImsConfig, times(1)).addConfigCallback((IImsConfigCallback) any());
+
int[] keys = {
ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE,
ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS,
@@ -390,6 +396,8 @@
mRcsConnectorListener0.getValue().connectionReady(mRcsFeatureManager, mSubId0);
processAllMessages();
+ verify(mImsConfig, times(1)).addConfigCallback((IImsConfigCallback) any());
+
// verify # of read data times from storage : # of Rcs storage length
verify(mImsProvisioningLoader, times(1))
.getProvisioningStatus(eq(mSubId0), eq(FEATURE_RCS), anyInt(), anyInt());
@@ -1736,6 +1744,110 @@
verifyNoMoreInteractions(mImsProvisioningLoader);
}
+ @Test
+ @SmallTest
+ public void changedProvisioningValue_withMmTel() throws Exception {
+ createImsProvisioningController();
+
+ // provisioning required capability
+ // voice, all tech
+ // video, all tech
+ setCarrierConfig(mSubId0, CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY,
+ RADIO_TECHS);
+ setCarrierConfig(mSubId0, CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY,
+ RADIO_TECHS);
+
+ try {
+ mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+ mSubId0, mIFeatureProvisioningCallback0);
+ } catch (Exception e) {
+ throw new AssertionError("not expected exception", e);
+ }
+
+ mMmTelConnectorListener0.getValue().connectionReady(mImsManager, mSubId0);
+
+ // clear interactions
+ clearInvocations(mIFeatureProvisioningCallback0);
+ clearInvocations(mImsConfig);
+ clearInvocations(mImsProvisioningLoader);
+
+ // MmTel valid
+ int[] keys = {
+ ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS,
+ ProvisioningManager.KEY_VT_PROVISIONING_STATUS,
+ ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
+ };
+ int[] capas = {
+ MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+ MmTelCapabilities.CAPABILITY_TYPE_VOICE
+ };
+ int[] techs = {
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
+ };
+
+ for (int index = 0; index < keys.length; index++) {
+ mIImsConfigCallback.getValue().onIntConfigChanged(keys[index],
+ PROVISIONING_VALUE_DISABLED);
+ processAllMessages();
+
+ // verify # of read data times from storage : # of MmTel storage length
+ verify(mImsProvisioningLoader, times(1))
+ .setProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capas[index]),
+ eq(techs[index]), eq(false));
+
+ verify(mIFeatureProvisioningCallback0, times(1))
+ .onFeatureProvisioningChanged(eq(capas[index]), eq(techs[index]), eq(false));
+ }
+
+ verifyNoMoreInteractions(mImsProvisioningLoader);
+ verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+ verifyNoMoreInteractions(mImsConfig);
+ }
+
+ @Test
+ @SmallTest
+ public void changedProvisioningValue_withRcs() throws Exception {
+ createImsProvisioningController();
+
+ // provisioning required capability : PRESENCE, tech : all
+ setCarrierConfig(mSubId0,
+ CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY, RADIO_TECHS);
+
+ try {
+ mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+ mSubId0, mIFeatureProvisioningCallback0);
+ } catch (Exception e) {
+ throw new AssertionError("not expected exception", e);
+ }
+
+ mRcsConnectorListener0.getValue().connectionReady(mRcsFeatureManager, mSubId0);
+
+ // clear interactions
+ clearInvocations(mIFeatureProvisioningCallback0);
+ clearInvocations(mImsConfig);
+ clearInvocations(mImsProvisioningLoader);
+
+ mIImsConfigCallback.getValue().onIntConfigChanged(KEY_EAB_PROVISIONING_STATUS,
+ PROVISIONING_VALUE_DISABLED);
+ processAllMessages();
+
+ // verify # of read data times from storage : # of MmTel storage length
+ verify(mImsProvisioningLoader, times(RADIO_TECHS.length))
+ .setProvisioningStatus(eq(mSubId0), eq(FEATURE_RCS),
+ eq(CAPABILITY_TYPE_PRESENCE_UCE), anyInt(), eq(false));
+
+ verify(mIFeatureProvisioningCallback0, times(RADIO_TECHS.length))
+ .onRcsFeatureProvisioningChanged(eq(CAPABILITY_TYPE_PRESENCE_UCE), anyInt(),
+ eq(false));
+
+ verifyNoMoreInteractions(mImsProvisioningLoader);
+ verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+ verifyNoMoreInteractions(mImsConfig);
+ }
+
private void createImsProvisioningController() throws Exception {
if (Looper.myLooper() == null) {
Looper.prepare();
@@ -1755,6 +1867,9 @@
.create(any(), eq(1), mRcsConnectorListener1.capture(), any(), any()))
.thenReturn(mRcsFeatureConnector1);
+ doNothing().when(mImsConfig).addConfigCallback(mIImsConfigCallback.capture());
+ doNothing().when(mImsConfig).removeConfigCallback(any());
+
when(mImsConfig.getConfigInt(anyInt()))
.thenAnswer(invocation -> {
int i = (Integer) (invocation.getArguments()[0]);
diff --git a/tests/src/com/android/phone/ImsStateCallbackControllerTest.java b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
index cb4321c..2bd87be 100644
--- a/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
+++ b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
@@ -28,6 +28,8 @@
import static com.android.ims.FeatureConnector.UNAVAILABLE_REASON_NOT_READY;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
@@ -50,6 +52,7 @@
import android.testing.TestableLooper;
import android.util.Log;
+import com.android.TelephonyTestBase;
import com.android.ims.FeatureConnector;
import com.android.ims.ImsManager;
import com.android.ims.RcsFeatureManager;
@@ -68,13 +71,12 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import java.lang.reflect.Field;
import java.util.concurrent.Executor;
/**
* Unit tests for RcsProvisioningMonitor
*/
-public class ImsStateCallbackControllerTest {
+public class ImsStateCallbackControllerTest extends TelephonyTestBase {
private static final String TAG = "ImsStateCallbackControllerTest";
private static final int FAKE_SUB_ID_BASE = 0x0FFFFFF0;
@@ -187,6 +189,7 @@
mLooper.destroy();
mLooper = null;
}
+ super.tearDown();
}
@Test
@@ -874,6 +877,36 @@
assertFalse(mImsStateCallbackController.isRegistered(mCallback1));
}
+ @Test
+ @SmallTest
+ public void testImsManagerInstance() throws Exception {
+ createController(1);
+
+ // MmTelConnection not ready
+ // check ImsManager instance
+ ImsManager imsManager = mImsStateCallbackController.getImsManager(SLOT_0_SUB_ID);
+ assertNull(imsManager);
+
+ // MmTelConnection ready
+ mMmTelConnectorListenerSlot0.getValue()
+ .connectionReady(mMmTelFeatureManager, SLOT_0_SUB_ID);
+ processAllMessages();
+
+ // check ImsManager instance
+ imsManager = mImsStateCallbackController.getImsManager(SLOT_0_SUB_ID);
+ assertNotNull(imsManager);
+
+ // MmTelConnection unavailable
+ mMmTelConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+
+ // MmTelConnection unavailable
+ // check ImsManager instance
+ imsManager = mImsStateCallbackController.getImsManager(SLOT_0_SUB_ID);
+ assertNull(imsManager);
+ }
+
private void createController(int slotCount) throws Exception {
if (Looper.myLooper() == null) {
Looper.prepare();
@@ -920,13 +953,6 @@
}
}
- private static void replaceInstance(final Class c,
- final String instanceName, final Object obj, final Object newValue) throws Exception {
- Field field = c.getDeclaredField(instanceName);
- field.setAccessible(true);
- field.set(obj, newValue);
- }
-
private void makeFakeActiveSubIds(int count) {
final int[] subIds = new int[count];
for (int i = 0; i < count; i++) {
diff --git a/tests/src/com/android/phone/NotificationMgrTest.java b/tests/src/com/android/phone/NotificationMgrTest.java
new file mode 100644
index 0000000..e009446
--- /dev/null
+++ b/tests/src/com/android/phone/NotificationMgrTest.java
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2022 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.phone;
+
+import static android.telephony.RadioAccessFamily.RAF_1xRTT;
+import static android.telephony.RadioAccessFamily.RAF_EDGE;
+import static android.telephony.RadioAccessFamily.RAF_EHRPD;
+import static android.telephony.RadioAccessFamily.RAF_EVDO_0;
+import static android.telephony.RadioAccessFamily.RAF_EVDO_A;
+import static android.telephony.RadioAccessFamily.RAF_EVDO_B;
+import static android.telephony.RadioAccessFamily.RAF_GPRS;
+import static android.telephony.RadioAccessFamily.RAF_GSM;
+import static android.telephony.RadioAccessFamily.RAF_HSDPA;
+import static android.telephony.RadioAccessFamily.RAF_HSPA;
+import static android.telephony.RadioAccessFamily.RAF_HSPAP;
+import static android.telephony.RadioAccessFamily.RAF_HSUPA;
+import static android.telephony.RadioAccessFamily.RAF_IS95A;
+import static android.telephony.RadioAccessFamily.RAF_IS95B;
+import static android.telephony.RadioAccessFamily.RAF_LTE;
+import static android.telephony.RadioAccessFamily.RAF_LTE_CA;
+import static android.telephony.RadioAccessFamily.RAF_TD_SCDMA;
+import static android.telephony.RadioAccessFamily.RAF_UMTS;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static com.android.phone.NotificationMgr.DATA_ROAMING_NOTIFICATION;
+import static com.android.phone.NotificationMgr.LIMITED_SIM_FUNCTION_NOTIFICATION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.PersistableBundle;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.TelephonyTestBase;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SignalStrengthController;
+import com.android.internal.telephony.data.DataConfigManager;
+import com.android.internal.telephony.data.DataNetworkController;
+import com.android.internal.telephony.data.DataSettingsManager;
+import com.android.internal.telephony.util.NotificationChannelController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit Test for NotificationMgr
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationMgrTest extends TelephonyTestBase {
+ private static final int TEST_SUB_ID = 1;
+ private static final long SERIAL_NUMBER_OF_USER = 1234567L;
+ private static final String TEST_LABEL_CF = "test_call_forwarding";
+ private static final String TEST_SUB_INFO_DISPLAY_NAME = "display_name";
+ private static final String TEST_PACKAGE_NAME = "com.android.phone";
+ private static final String TEST_SELECTED_NETWORK_OPERATOR_NAME = "TheOperator";
+ private static final String MOBILE_NETWORK_SELECTION_PACKAGE = "com.android.phone";
+ private static final String MOBILE_NETWORK_SELECTION_CLASS = ".testClass";
+ private static final String CARRIER_NAME = "CoolCarrier";
+
+ @Mock PhoneGlobals mApp;
+ @Mock StatusBarManager mStatusBarManager;
+ @Mock UserManager mUserManager;
+ @Mock SubscriptionManager mSubscriptionManager;
+ @Mock TelecomManager mTelecomManager;
+ @Mock TelephonyManager mTelephonyManager;
+ @Mock Phone mPhone;
+ @Mock SharedPreferences mSharedPreferences;
+ @Mock NotificationManager mNotificationManager;
+ @Mock SubscriptionInfo mSubscriptionInfo;
+ @Mock Resources mResources;
+ @Mock Context mMockedContext;
+ @Mock ServiceStateTracker mServiceStateTracker;
+ @Mock ServiceState mServiceState;
+ @Mock CarrierConfigManager mCarrierConfigManager;
+ @Mock DataNetworkController mDataNetworkController;
+ @Mock DataSettingsManager mDataSettingsManager;
+ @Mock DataConfigManager mDataConfigManager;
+ @Mock SignalStrengthController mSignalStrengthController;
+
+ private Phone[] mPhones;
+ private NotificationMgr mNotificationMgr;
+ private TestableLooper mTestableLooper;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mPhones = new Phone[]{mPhone};
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ when(mPhone.getContext()).thenReturn(mMockedContext);
+ when(mMockedContext.getResources()).thenReturn(mResources);
+ when(mPhone.getServiceState()).thenReturn(mServiceState);
+ when(mServiceState.getNetworkRegistrationInfo(anyInt(), anyInt())).thenReturn(
+ new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build());
+ when(mPhone.getServiceStateTracker()).thenReturn(mServiceStateTracker);
+ mServiceStateTracker.mSS = mServiceState;
+ when(mPhone.getSignalStrengthController()).thenReturn(mSignalStrengthController);
+ when(mPhone.getDataNetworkController()).thenReturn(mDataNetworkController);
+ when(mDataNetworkController.getInternetDataDisallowedReasons()).thenReturn(
+ Collections.emptyList());
+ when(mDataNetworkController.getDataConfigManager()).thenReturn(mDataConfigManager);
+ when(mPhone.getDataSettingsManager()).thenReturn(mDataSettingsManager);
+ when(mDataSettingsManager.isDataEnabledForReason(anyInt())).thenReturn(true);
+ when(mApp.getSharedPreferences(anyString(), anyInt())).thenReturn(mSharedPreferences);
+
+ when(mApp.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(mApp.getSystemService(Context.STATUS_BAR_SERVICE)).thenReturn(mStatusBarManager);
+ when(mApp.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mApp.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)).thenReturn(
+ mSubscriptionManager);
+ when(mApp.getSystemServiceName(TelecomManager.class)).thenReturn(Context.TELECOM_SERVICE);
+ when(mApp.getSystemService(TelecomManager.class)).thenReturn(mTelecomManager);
+ when(mApp.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+ when(mApp.getSystemServiceName(CarrierConfigManager.class)).thenReturn(
+ Context.CARRIER_CONFIG_SERVICE);
+ when(mApp.getSystemService(CarrierConfigManager.class)).thenReturn(mCarrierConfigManager);
+ when(mApp.getSystemServiceName(CarrierConfigManager.class)).thenReturn(
+ Context.CARRIER_CONFIG_SERVICE);
+ when(mApp.getSystemService(CarrierConfigManager.class)).thenReturn(mCarrierConfigManager);
+
+ when(mApp.createPackageContextAsUser(any(), eq(0), any())).thenReturn(mApp);
+ when(mApp.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(mNotificationManager);
+ when(mUserManager.getSerialNumbersOfUsers(true)).thenReturn(
+ new long[]{SERIAL_NUMBER_OF_USER});
+ when(mUserManager.getUserForSerialNumber(eq(SERIAL_NUMBER_OF_USER))).thenReturn(
+ UserHandle.SYSTEM);
+ when(mApp.getResources()).thenReturn(mResources);
+ when(mResources.getString(R.string.labelCF)).thenReturn(TEST_LABEL_CF);
+ ApplicationInfo appWithSdkS = buildApplicationInfo(Build.VERSION_CODES.S);
+ when(mApp.getApplicationInfo()).thenReturn(appWithSdkS);
+ when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+ when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
+
+ mTestableLooper = TestableLooper.get(this);
+ // Spy it only to avoid sleep for SystemClock.elapsedRealtime()
+ mNotificationMgr = spy(new NotificationMgr(mApp));
+ mTestableLooper.processAllMessages();
+ }
+
+ @Test
+ public void testUpdateCfi_visible_noActiveSubscription_notificationNeverSent() {
+ // Given no active subscription available
+ when(mSubscriptionManager.getActiveSubscriptionInfo(eq(TEST_SUB_ID))).thenReturn(null);
+
+ // When updateCfi method is called
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/true, /*isFresh=*/false);
+
+ // Then the notification should never be sent
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateCfi_visible_hasActiveSub_singleSIM_notificationSent() {
+ when(mTelephonyManager.getPhoneCount()).thenReturn(1);
+ when(mSubscriptionManager.getActiveSubscriptionInfo(eq(TEST_SUB_ID))).thenReturn(
+ mSubscriptionInfo);
+
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/true, /*isFresh=*/false);
+
+ verifyNotificationSentWithChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD);
+ }
+
+ @Test
+ public void testUpdateCfi_visible_hasActiveSub_multiSIM_notificationSentWithoutDisplayName() {
+ when(mTelephonyManager.getPhoneCount()).thenReturn(2);
+ when(mSubscriptionManager.getActiveSubscriptionInfo(eq(TEST_SUB_ID))).thenReturn(
+ mSubscriptionInfo);
+ when(mSubscriptionInfo.getDisplayName()).thenReturn(null);
+
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/true, /*isFresh=*/false);
+
+ verifyNotificationSentWithChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD);
+ }
+
+ @Test
+ public void testUpdateCfi_visible_hasActiveSub_multiSIM_notificationSentWithDisplayName() {
+ when(mTelephonyManager.getPhoneCount()).thenReturn(2);
+ when(mSubscriptionManager.getActiveSubscriptionInfo(eq(TEST_SUB_ID))).thenReturn(
+ mSubscriptionInfo);
+ when(mSubscriptionInfo.getDisplayName()).thenReturn(TEST_SUB_INFO_DISPLAY_NAME);
+
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/true, /*isFresh=*/false);
+
+ verifyNotificationSentWithChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD);
+ }
+
+ @Test
+ public void testUpdateCfi_invisible_hasUnmanagedProfile_notificationCanceled() {
+ when(mUserManager.isManagedProfile(anyInt())).thenReturn(false);
+
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/false, /*isFresh=*/false);
+
+ verify(mNotificationManager).cancel(any(), anyInt());
+ }
+
+ @Test
+ public void testUpdateCfi_invisible_allProfilesAreManaged_notificationNeverCanceled() {
+ when(mUserManager.isManagedProfile(anyInt())).thenReturn(true);
+
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/false, /*isFresh=*/false);
+
+ verify(mNotificationManager, never()).cancel(any(), anyInt());
+ }
+
+ @Test
+ public void testShowDataRoamingNotification_roamingOn() {
+ mNotificationMgr.showDataRoamingNotification(TEST_SUB_ID, /*roamingOn=*/true);
+
+ verifyNotificationSentWithChannelId(
+ NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS);
+ }
+
+ @Test
+ public void testShowDataRoamingNotification_roamingOff() {
+ mNotificationMgr.showDataRoamingNotification(TEST_SUB_ID, /*roamingOn=*/false);
+
+ verifyNotificationSentWithChannelId(
+ NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS);
+ }
+
+ @Test
+ public void testHideDataRoamingNotification() {
+ mNotificationMgr.hideDataRoamingNotification();
+
+ verify(mNotificationManager).cancel(any(), eq(DATA_ROAMING_NOTIFICATION));
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_justOutOfService_notificationNeverSent() {
+ prepareResourcesForNetworkSelection();
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ moveTimeForward(2 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_oosEnoughTime_selectionVisibleToUser_notificationSent() {
+ prepareResourcesForNetworkSelection();
+ when(mTelephonyManager.isManualNetworkSelectionAllowed()).thenReturn(true);
+ PersistableBundle config = new PersistableBundle();
+ config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+ config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+
+ // update to OOS as base state
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ // 10 seconds later
+ moveTimeForward(10 /* seconds */);
+ // verify the behavior on new request
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verifyNotificationSentWithChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_invalidSubscription_notificationNotSent() {
+ prepareResourcesForNetworkSelection();
+ when(mTelephonyManager.isManualNetworkSelectionAllowed()).thenReturn(true);
+ PersistableBundle config = new PersistableBundle();
+ config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+ config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE,
+ INVALID_SUBSCRIPTION_ID);
+ moveTimeForward(10 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE,
+ INVALID_SUBSCRIPTION_ID);
+
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_nullCarrierConfig_notificationNotSent() {
+ prepareResourcesForNetworkSelection();
+
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(null);
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ moveTimeForward(10 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_userNotAllowedToChooseOperator_notificationNotSent() {
+ prepareResourcesForNetworkSelection();
+
+ PersistableBundle config = new PersistableBundle();
+ // User is NOT allowed to choose operator
+ config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, false);
+ when(mTelephonyManager.isManualNetworkSelectionAllowed()).thenReturn(false);
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ moveTimeForward(10 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void
+ testUpdateNetworkSelection_OverrideHideCarrierNetworkSelection_notificationNotSent() {
+ prepareResourcesForNetworkSelection();
+
+ PersistableBundle config = new PersistableBundle();
+ // Hide network selection menu
+ config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, true);
+ config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+ config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, false);
+ when(mTelephonyManager.isManualNetworkSelectionAllowed()).thenReturn(false);
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ moveTimeForward(10 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_simPreventManualSelection_notificationNotSent()
+ throws Exception {
+ prepareResourcesForNetworkSelection();
+
+ PersistableBundle config = new PersistableBundle();
+ config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+ config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ // SIM card can prevent manual network selection which is forbidden
+ config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, true);
+ when(mTelephonyManager.isManualNetworkSelectionAllowed()).thenReturn(false);
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ moveTimeForward(10 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_worldMode_userSetLTE_notificationNotSent() {
+ prepareResourcesForNetworkSelection();
+
+ PersistableBundle config = new PersistableBundle();
+ config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+ config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
+
+ // World mode is on
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL, true);
+ // User set Network mode as LTE
+ when(mTelephonyManager.getAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)).thenReturn(
+ (long) (RAF_LTE | RAF_LTE_CA | RAF_IS95A | RAF_IS95B | RAF_1xRTT | RAF_EVDO_0
+ | RAF_EVDO_A | RAF_EVDO_B | RAF_EHRPD));
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ moveTimeForward(10 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_worldMode_userSetTDSCDMA_notSupported_notifNotSent() {
+ prepareResourcesForNetworkSelection();
+
+ PersistableBundle config = new PersistableBundle();
+ config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+ config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
+
+ // World mode is on
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL, true);
+ // User set Network mode as NETWORK_MODE_LTE_TDSCDMA_GSM
+ when(mTelephonyManager.getAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)).thenReturn(
+ (long) (RAF_LTE | RAF_LTE_CA | RAF_TD_SCDMA | RAF_GSM | RAF_GPRS | RAF_EDGE));
+ // But TDSCDMA is NOT supported
+ config.putBoolean(CarrierConfigManager.KEY_SUPPORT_TDSCDMA_BOOL, false);
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ moveTimeForward(10 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_worldMode_userSetWCDMA_notificationSent() {
+ prepareResourcesForNetworkSelection();
+
+ PersistableBundle config = new PersistableBundle();
+ config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+ config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
+
+ // World mode is on
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL, true);
+ // User set Network mode as NETWORK_MODE_LTE_TDSCDMA_GSM
+ when(mTelephonyManager.getAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)).thenReturn(
+ (long) (RAF_LTE | RAF_LTE_CA | RAF_GSM | RAF_GPRS | RAF_EDGE | RAF_HSUPA | RAF_HSDPA
+ | RAF_HSPA | RAF_HSPAP | RAF_UMTS));
+ // But TDSCDMA is NOT supported
+ config.putBoolean(CarrierConfigManager.KEY_SUPPORT_TDSCDMA_BOOL, false);
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ moveTimeForward(10 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verifyNotificationSentWithChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_worldPhone_networkSelectionNotHide_notificationSent() {
+ prepareResourcesForNetworkSelection();
+
+ PersistableBundle config = new PersistableBundle();
+ config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+ config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, false);
+ // World mode is off
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL, false);
+ // World phone is on
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ moveTimeForward(10 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verifyNotificationSentWithChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_gsmBasicOptionOn_notificationSent() {
+ prepareResourcesForNetworkSelection();
+
+ PersistableBundle config = new PersistableBundle();
+ config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+ config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, false);
+ // World phone is on
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
+ // World mode is off
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL, false);
+ when(mTelephonyManager.getPhoneType()).thenReturn(TelephonyManager.PHONE_TYPE_GSM);
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ moveTimeForward(10 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verifyNotificationSentWithChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_gsmBasicOptionOff_notificationNotSent() {
+ prepareResourcesForNetworkSelection();
+
+ PersistableBundle config = new PersistableBundle();
+ config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+ config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, false);
+ // World mode is off
+ config.putBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL, false);
+ when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+ when(mTelephonyManager.getPhoneType()).thenReturn(TelephonyManager.PHONE_TYPE_CDMA);
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ moveTimeForward(10 /* seconds */);
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testShowLimitedSimFunctionWarningNotification_forTheFirstTime_notificationSent() {
+ when(mResources.getText(R.string.limited_sim_function_notification_message)).thenReturn(
+ CARRIER_NAME);
+ when(mResources.getText(
+ R.string.limited_sim_function_with_phone_num_notification_message)).thenReturn(
+ "123");
+
+ mNotificationMgr.showLimitedSimFunctionWarningNotification(TEST_SUB_ID, CARRIER_NAME);
+
+ verifyNotificationSentWithChannelId(
+ NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY);
+ }
+
+ @Test
+ public void
+ testShowLimitedSimFunctionWarningNotification_consecutiveCall_notificationSentOnce() {
+ when(mResources.getText(R.string.limited_sim_function_notification_message)).thenReturn(
+ CARRIER_NAME);
+ when(mResources.getText(
+ R.string.limited_sim_function_with_phone_num_notification_message)).thenReturn(
+ "123");
+
+ // Call the method TWICE with the same subscription
+ mNotificationMgr.showLimitedSimFunctionWarningNotification(TEST_SUB_ID, CARRIER_NAME);
+ mNotificationMgr.showLimitedSimFunctionWarningNotification(TEST_SUB_ID, CARRIER_NAME);
+
+ // Verify the notification is only sent ONCE
+ verifyNotificationSentWithChannelId(
+ NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY);
+ }
+
+ @Test
+ public void testDismissLimitedSimFunctionWarningNotification_noShowCalledBefore_noCancelSent() {
+ // showLimitedSimFunctionWarningNotification was never called before
+
+ mNotificationMgr.dismissLimitedSimFunctionWarningNotification(TEST_SUB_ID);
+
+ verify(mNotificationManager, never()).cancel(any(), anyInt());
+ }
+
+ @Test
+ public void testDismissLimitedSimFunctionWarningNotification_showCalledBefore_cancelSent() {
+ when(mResources.getText(R.string.limited_sim_function_notification_message)).thenReturn(
+ CARRIER_NAME);
+ when(mResources.getText(
+ R.string.limited_sim_function_with_phone_num_notification_message)).thenReturn(
+ "123");
+ mNotificationMgr.showLimitedSimFunctionWarningNotification(TEST_SUB_ID, CARRIER_NAME);
+
+ mNotificationMgr.dismissLimitedSimFunctionWarningNotification(TEST_SUB_ID);
+
+ verify(mNotificationManager).cancel(any(), eq(LIMITED_SIM_FUNCTION_NOTIFICATION));
+ }
+
+ private ApplicationInfo buildApplicationInfo(int targetSdkVersion) {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = targetSdkVersion;
+ return applicationInfo;
+ }
+
+ private void verifyNotificationSentWithChannelId(String expectedNotificationChannelId) {
+ ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(
+ Notification.class);
+ verify(mNotificationManager).notify(any(), anyInt(), notificationArgumentCaptor.capture());
+ Notification capturedNotification = notificationArgumentCaptor.getAllValues().get(0);
+ assertThat(capturedNotification.getChannelId()).isEqualTo(expectedNotificationChannelId);
+ }
+
+ private void prepareResourcesForNetworkSelection() {
+ when(mSharedPreferences.getString(Phone.NETWORK_SELECTION_NAME_KEY + TEST_SUB_ID,
+ "")).thenReturn(TEST_SELECTED_NETWORK_OPERATOR_NAME);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.skip_restoring_network_selection)).thenReturn(false);
+ when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+ when(mApp.getString(R.string.mobile_network_settings_package)).thenReturn(
+ MOBILE_NETWORK_SELECTION_PACKAGE);
+ when(mApp.getString(R.string.mobile_network_settings_class)).thenReturn(
+ MOBILE_NETWORK_SELECTION_CLASS);
+ }
+
+ private void moveTimeForward(long seconds) {
+ final long millis = TimeUnit.SECONDS.toMillis(seconds);
+ mTestableLooper.moveTimeForward(millis);
+ mTestableLooper.processAllMessages();
+ doReturn(SystemClock.elapsedRealtime() + millis).when(mNotificationMgr).getTimeStamp();
+ }
+}
diff --git a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
index 3b6d5ee..e702279 100644
--- a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
+++ b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
@@ -17,15 +17,22 @@
package com.android.phone;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.content.pm.PackageManager;
+import android.content.SharedPreferences;
import android.content.res.Resources;
import android.telephony.RadioAccessFamily;
import android.telephony.TelephonyManager;
@@ -34,8 +41,10 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.TelephonyTestBase;
+import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import org.junit.Before;
import org.junit.Test;
@@ -45,27 +54,37 @@
import java.util.Locale;
/**
- * Unit Test for CarrierConfigLoader.
+ * Unit Test for PhoneInterfaceManager.
*/
@RunWith(AndroidJUnit4.class)
public class PhoneInterfaceManagerTest extends TelephonyTestBase {
private PhoneInterfaceManager mPhoneInterfaceManager;
+ private SharedPreferences mSharedPreferences;
+ private IIntegerConsumer mIIntegerConsumer;
@Mock
PhoneGlobals mPhoneGlobals;
@Mock
Phone mPhone;
+
@Mock
- PackageManager mPackageManager;
+ private SubscriptionManagerService mSubscriptionManagerService;
@Before
@UiThreadTest
public void setUp() throws Exception {
super.setUp();
- doReturn(mPackageManager).when(mPhoneGlobals).getPackageManager();
- doReturn(false).when(mPackageManager).hasSystemFeature(
- PackageManager.FEATURE_TELEPHONY_IMS);
- mPhoneInterfaceManager = PhoneInterfaceManager.init(mPhoneGlobals);
+ // Note that PhoneInterfaceManager is a singleton. Calling init gives us a handle to the
+ // global singleton, but the context that is passed in is unused if the phone app is already
+ // alive on a test devices. You must use the spy to mock behavior. Mocks stemming from the
+ // passed context will remain unused.
+ mPhoneInterfaceManager = spy(PhoneInterfaceManager.init(mPhoneGlobals));
+ doReturn(mSubscriptionManagerService).when(mPhoneInterfaceManager)
+ .getSubscriptionManagerService();
+ TelephonyManager.setupISubForTest(mSubscriptionManagerService);
+ mSharedPreferences = mPhoneInterfaceManager.getSharedPreferences();
+ mSharedPreferences.edit().remove(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED).commit();
+ mIIntegerConsumer = mock(IIntegerConsumer.class);
}
@Test
@@ -138,4 +157,130 @@
assertEquals("ff-Latn-BF", resultFfBf);
}
+
+ @Test
+ public void setNullCipherAndIntegrityEnabled_successfullyEnable() {
+ whenModemSupportsNullCiphers();
+ doReturn(201).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+ doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+ assertFalse(mSharedPreferences.contains(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED));
+
+ mPhoneInterfaceManager.setNullCipherAndIntegrityEnabled(true);
+
+ assertTrue(
+ mSharedPreferences.getBoolean(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED, false));
+ }
+
+ @Test
+ public void setNullCipherAndIntegrityEnabled_successfullyDisable() {
+ whenModemSupportsNullCiphers();
+ doReturn(201).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+ doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+ assertFalse(mSharedPreferences.contains(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED));
+
+ mPhoneInterfaceManager.setNullCipherAndIntegrityEnabled(false);
+
+ assertFalse(
+ mSharedPreferences.getBoolean(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED, true));
+ }
+
+ @Test
+ public void setNullCipherAndIntegrityEnabled_lackingNecessaryHal() {
+ doReturn(101).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+ doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+
+ assertThrows(UnsupportedOperationException.class, () -> {
+ mPhoneInterfaceManager.setNullCipherAndIntegrityEnabled(true);
+ });
+
+ }
+
+ @Test
+ public void setNullCipherAndIntegrityEnabled_lackingPermissions() {
+ doReturn(201).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+ doThrow(SecurityException.class).when(mPhoneInterfaceManager).enforceModifyPermission();
+
+ assertThrows(SecurityException.class, () -> {
+ mPhoneInterfaceManager.setNullCipherAndIntegrityEnabled(true);
+ });
+ }
+
+ @Test
+ public void isNullCipherAndIntegrityPreferenceEnabled() {
+ whenModemSupportsNullCiphers();
+ doReturn(201).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+ doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+
+ mPhoneInterfaceManager.setNullCipherAndIntegrityEnabled(false);
+ assertFalse(
+ mSharedPreferences.getBoolean(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED, true));
+ }
+
+ @Test
+ public void isNullCipherAndIntegrityPreferenceEnabled_lackingNecessaryHal() {
+ doReturn(101).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+ doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+
+ assertThrows(UnsupportedOperationException.class, () -> {
+ mPhoneInterfaceManager.isNullCipherAndIntegrityPreferenceEnabled();
+ });
+
+ }
+
+ @Test
+ public void isNullCipherAndIntegrityPreferenceEnabled_lackingModemSupport() {
+ whenModemDoesNotSupportNullCiphers();
+ doReturn(201).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+ doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+
+ assertThrows(UnsupportedOperationException.class, () -> {
+ mPhoneInterfaceManager.isNullCipherAndIntegrityPreferenceEnabled();
+ });
+
+ }
+
+ @Test
+ public void isNullCipherAndIntegrityPreferenceEnabled_lackingPermissions() {
+ doReturn(201).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+ doThrow(SecurityException.class).when(mPhoneInterfaceManager).enforceReadPermission();
+
+ assertThrows(SecurityException.class, () -> {
+ mPhoneInterfaceManager.isNullCipherAndIntegrityPreferenceEnabled();
+ });
+ }
+
+ private void whenModemDoesNotSupportNullCiphers() {
+ doReturn(false).when(mPhone).isNullCipherAndIntegritySupported();
+ doReturn(mPhone).when(
+ mPhoneInterfaceManager).getDefaultPhone();
+ }
+
+ private void whenModemSupportsNullCiphers() {
+ doReturn(true).when(mPhone).isNullCipherAndIntegritySupported();
+ doReturn(mPhone).when(
+ mPhoneInterfaceManager).getDefaultPhone();
+ }
+
+ /**
+ * Verify getCarrierRestrictionStatus throws exception for invalid caller package name.
+ */
+ @Test
+ public void getCarrierRestrictionStatus_ReadPrivilegedException2() {
+ doThrow(SecurityException.class).when(
+ mPhoneInterfaceManager).enforceReadPrivilegedPermission(anyString());
+ assertThrows(SecurityException.class, () -> {
+ mPhoneInterfaceManager.getCarrierRestrictionStatus(mIIntegerConsumer, "");
+ });
+ }
+
+ /**
+ * Verify getCarrierRestrictionStatus doesn't throw any exception with valid package name
+ * and with READ_PHONE_STATE permission granted.
+ */
+ @Test
+ public void getCarrierRestrictionStatus() {
+ when(mPhoneInterfaceManager.validateCallerAndGetCarrierId(anyString())).thenReturn(1);
+ mPhoneInterfaceManager.getCarrierRestrictionStatus(mIIntegerConsumer,
+ "com.test.package");
+ }
}
diff --git a/tests/src/com/android/phone/PhoneUtilsTest.java b/tests/src/com/android/phone/PhoneUtilsTest.java
index eb4c248..3d7815c 100644
--- a/tests/src/com/android/phone/PhoneUtilsTest.java
+++ b/tests/src/com/android/phone/PhoneUtilsTest.java
@@ -22,29 +22,26 @@
import static org.mockito.Mockito.when;
import android.content.ComponentName;
+import android.os.UserHandle;
import android.telecom.PhoneAccountHandle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import androidx.test.runner.AndroidJUnit4;
+import com.android.TelephonyTestBase;
import com.android.internal.telephony.GsmCdmaPhone;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-
@RunWith(AndroidJUnit4.class)
-public class PhoneUtilsTest {
+public class PhoneUtilsTest extends TelephonyTestBase {
@Mock
private SubscriptionManager mMockSubscriptionManager;
@Mock
@@ -59,37 +56,6 @@
private PhoneAccountHandle mPhoneAccountHandleTest = new PhoneAccountHandle(
PSTN_CONNECTION_SERVICE_COMPONENT, mPhoneAccountHandleIdString);
- private HashMap<InstanceKey, Object> mOldInstances = new HashMap<InstanceKey, Object>();
-
- private ArrayList<InstanceKey> mInstanceKeys = new ArrayList<InstanceKey>();
-
- private static class InstanceKey {
- public final Class mClass;
- public final String mInstName;
- public final Object mObj;
- InstanceKey(final Class c, final String instName, final Object obj) {
- mClass = c;
- mInstName = instName;
- mObj = obj;
- }
-
- @Override
- public int hashCode() {
- return (mClass.getName().hashCode() * 31 + mInstName.hashCode()) * 31;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null || !(obj instanceof InstanceKey)) {
- return false;
- }
-
- InstanceKey other = (InstanceKey) obj;
- return (other.mClass == mClass && other.mInstName.equals(mInstName)
- && other.mObj == mObj);
- }
- }
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -100,37 +66,6 @@
replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
}
- @After
- public void tearDown() throws Exception {
- restoreInstance(PhoneFactory.class, "sPhones", null);
- }
-
- protected synchronized void replaceInstance(final Class c, final String instanceName,
- final Object obj, final Object newValue)
- throws Exception {
- Field field = c.getDeclaredField(instanceName);
- field.setAccessible(true);
-
- InstanceKey key = new InstanceKey(c, instanceName, obj);
- if (!mOldInstances.containsKey(key)) {
- mOldInstances.put(key, field.get(obj));
- mInstanceKeys.add(key);
- }
- field.set(obj, newValue);
- }
-
- protected synchronized void restoreInstance(final Class c, final String instanceName,
- final Object obj) throws Exception {
- InstanceKey key = new InstanceKey(c, instanceName, obj);
- if (mOldInstances.containsKey(key)) {
- Field field = c.getDeclaredField(instanceName);
- field.setAccessible(true);
- field.set(obj, mOldInstances.get(key));
- mOldInstances.remove(key);
- mInstanceKeys.remove(key);
- }
- }
-
@Test
public void testIsPhoneAccountActive() throws Exception {
assertTrue(PhoneUtils.isPhoneAccountActive(
@@ -147,7 +82,16 @@
public void testMakePstnPhoneAccountHandleWithPrefix() throws Exception {
PhoneAccountHandle phoneAccountHandleTest = new PhoneAccountHandle(
PSTN_CONNECTION_SERVICE_COMPONENT, mPhoneAccountHandleIdString);
- assertEquals(phoneAccountHandleTest, PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
- mPhoneAccountHandleIdString, "", false));
+ assertEquals(phoneAccountHandleTest, PhoneUtils.makePstnPhoneAccountHandleWithId(
+ mPhoneAccountHandleIdString, null));
+ }
+
+ @Test
+ public void testMakePstnPhoneAccountHandleWithPrefixForAnotherUser() throws Exception {
+ UserHandle userHandle = new UserHandle(10);
+ PhoneAccountHandle phoneAccountHandleTest = new PhoneAccountHandle(
+ PSTN_CONNECTION_SERVICE_COMPONENT, mPhoneAccountHandleIdString, userHandle);
+ assertEquals(phoneAccountHandleTest, PhoneUtils.makePstnPhoneAccountHandleWithId(
+ mPhoneAccountHandleIdString, userHandle));
}
}
diff --git a/tests/src/com/android/phone/Telephony2gUpdaterTest.java b/tests/src/com/android/phone/Telephony2gUpdaterTest.java
deleted file mode 100644
index 3684f30..0000000
--- a/tests/src/com/android/phone/Telephony2gUpdaterTest.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (C) 2022 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.phone;
-
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Intent;
-import android.os.UserManager;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.TelephonyTestBase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-@RunWith(AndroidJUnit4.class)
-public class Telephony2gUpdaterTest extends TelephonyTestBase {
- private static final long DRAIN_TIMEOUT = 10;
- private Telephony2gUpdater mTelephony2gUpdater;
- private SubscriptionManager.OnSubscriptionsChangedListener mChangedListener;
- private Executor mExecutor;
- private CountDownLatch mLatch;
-
- private UserManager mMockUserManager;
- private TelephonyManager mMockTelephonyManager;
- private SubscriptionManager mMockSubscriptionManager;
-
- // Set up to be returned from mMockSubscriptionManager.getCompleteActiveSubscriptionInfoList()
- // Updates will be reflected in subsequent calls to the mock method.
- private List<SubscriptionInfo> mCurrentSubscriptions;
-
- // 2G Bitmask is 0b10000000_01001011
- private static final long BASE_NETWORK = 0b11111111_11111111;
- private static final long EXPECTED_DISABLED = 0b01111111_10110100;
- private static final long EXPECTED_ENABLED = 0b11111111_11111111;
-
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
-
- mMockTelephonyManager = mContext.getSystemService(TelephonyManager.class);
- mMockUserManager = mContext.getSystemService(UserManager.class);
- mMockSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
-
- mCurrentSubscriptions = new ArrayList<>();
- setupMutableSubscriptionInfoMock();
-
- mExecutor = Executors.newSingleThreadExecutor();
- mTelephony2gUpdater = new Telephony2gUpdater(mExecutor, getTestContext(), BASE_NETWORK);
- mTelephony2gUpdater.init();
- ArgumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener> argument =
- ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class);
- verify(mMockSubscriptionManager).addOnSubscriptionsChangedListener(any(Executor.class),
- argument.capture());
- mChangedListener = argument.getValue();
- }
-
- @Test
- public void onSubscriptionsChanged_noSubscriptions_noAllowedNetworksChanged() {
- triggerOnSubscriptionChangedAndWait();
- verify(mMockTelephonyManager, never()).setAllowedNetworkTypesForReason(anyInt(), anyInt());
- }
-
- @Test
- public void onSubscriptionsChanged_oneSubscription_allowedNetworksUpdated() {
- TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
- triggerOnSubscriptionChangedAndWait();
-
- verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- }
-
- @Test
- public void onSubscriptionsChanged_manySubscriptionsDisallow2g_allowedNetworkUpdated() {
- // 2g is disallowed
- when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
- true);
- triggerBroadcastReceiverAndWait();
-
- TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
- TelephonyManager tmSubscription2 = addSubscriptionAndGetMock(1002);
-
- triggerOnSubscriptionChangedAndWait();
-
- verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
- verify(tmSubscription2, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
- }
-
- @Test
- public void onSubscriptionsChanged_noNewSubscriptions_noAllowedNetworksChanged() {
- when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
- true);
- triggerBroadcastReceiverAndWait();
-
- TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
-
- triggerOnSubscriptionChangedAndWait();
- triggerOnSubscriptionChangedAndWait();
-
- // subscriptions were updated twice, but we have no new subIds so we only expect one update
- verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
- }
-
- @Test
- public void onSubscriptionsChanged_removeSubscription_noAdditionalNetworkChanges() {
- // We start with 2 subIds
- TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
- TelephonyManager tmSubscription2 = addSubscriptionAndGetMock(1002);
-
- triggerOnSubscriptionChangedAndWait();
-
- // 2g is still enabled since the default is to not set the user restriction
- verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- verify(tmSubscription2, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
-
-
- mCurrentSubscriptions.remove(1);
- triggerOnSubscriptionChangedAndWait();
-
- // Subscriptions have changed, but we've only removed a subscription so there should be no
- // extra updates to allowed network types
- verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- verify(tmSubscription2, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- }
-
- @Test
- public void onSubscriptionsChanged_removeSubscriptionAndReAdd() {
- when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
- true);
- triggerBroadcastReceiverAndWait();
-
- TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
- triggerOnSubscriptionChangedAndWait();
- mCurrentSubscriptions.remove(0);
- triggerOnSubscriptionChangedAndWait();
- mCurrentSubscriptions.add(getSubInfo(1001));
- triggerOnSubscriptionChangedAndWait();
-
- // subscriptions were updated thrice, but one of those updates removed a subscription
- // such that the sub list was empty, so we only expect an update on the first and last
- // updates.
- verify(tmSubscription1, times(2)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
- }
-
- @Test
- public void onSubscriptionsChanged_addSubscription_updateAllowedNetworks() {
- // We start with 2 subIds and update subscriptions
- TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
- TelephonyManager tmSubscription2 = addSubscriptionAndGetMock(1002);
- triggerOnSubscriptionChangedAndWait();
-
- // Then add a subId and update subscriptions again
- TelephonyManager tmSubscription3 = addSubscriptionAndGetMock(1003);
- triggerOnSubscriptionChangedAndWait();
-
- // we only need to update the new subscription
- verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- verify(tmSubscription2, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- verify(tmSubscription3, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- }
-
- @Test
- public void onUserRestrictionUnchanged_noChangeToRestriction_noAllowedNetworksUpdated() {
- TelephonyManager tmSubscription = addSubscriptionAndGetMock(1001);
- triggerOnSubscriptionChangedAndWait();
- // precondition: we've updated allowed networks to the default (2g enabled)
- verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- verify(tmSubscription, never()).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
-
- when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
- true);
- triggerBroadcastReceiverAndWait();
- triggerBroadcastReceiverAndWait();
-
- // expect we only updated once even though we got two broadcasts for user restriction
- // updates
- verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
- // extra check to ensure we haven't also somehow updated back to enabled along the way
- verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- }
-
- @Test
- public void onUserRestrictionChanged_restrictionChanged_allowedNetworksUpdated() {
- // precondition: we've updated allowed networks to the default (2g enabled)
- TelephonyManager tmSubscription = addSubscriptionAndGetMock(1001);
- triggerOnSubscriptionChangedAndWait();
- verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- verify(tmSubscription, never()).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
-
- // update the user restriction to disallow 2g
- reset(tmSubscription);
- when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
- true);
- triggerBroadcastReceiverAndWait();
- verify(tmSubscription, never()).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
-
-
- // update the user restriction to allow 2g again
- reset(tmSubscription);
- when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
- false);
- triggerBroadcastReceiverAndWait();
- verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
- verify(tmSubscription, never()).setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
-
- }
-
- private SubscriptionInfo getSubInfo(int id) {
- return new SubscriptionInfo(id, "890126042XXXXXXXXXXX", 0, "T-mobile", "T-mobile", 0, 255,
- "12345", 0, null, "310", "260", "156", false, null, null);
- }
-
- private void triggerOnSubscriptionChangedAndWait() {
- mExecutor.execute(() -> mChangedListener.onSubscriptionsChanged());
- drainSingleThreadedExecutor();
- }
-
- private void triggerBroadcastReceiverAndWait() {
- mTelephony2gUpdater.onReceive(mContext, new Intent());
- drainSingleThreadedExecutor();
- }
-
- /**
- * Wait for all tasks on executor up to the point of invocation to drain, then return.
- *
- * This helper takes advantage of the fact that we're using an immutable single threaded
- * executor that guarantees tasks are executed in the order they are enqueued. It enqueues a
- * task that decrements a latch and then waits on that task to finish. By definition, once the
- * test task finishes, all previously enqueued tasks will have also completed.
- */
- private void drainSingleThreadedExecutor() {
- resetExecutorLatch();
- mExecutor.execute(() -> mLatch.countDown());
- try {
- mLatch.await(DRAIN_TIMEOUT, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- fail(e.getMessage());
- }
- }
-
- private void resetExecutorLatch() {
- mLatch = new CountDownLatch(1);
- }
-
- /**
- * Helper that allows you to update subInfo and have that change reflected on subsequent calls
- * to {@link SubscriptionManager#getCompleteActiveSubscriptionInfoList()}
- */
- private void setupMutableSubscriptionInfoMock() {
- var answer = new Answer<List<SubscriptionInfo>>() {
- @Override
- public List<SubscriptionInfo> answer(InvocationOnMock invocation) throws Throwable {
- return mCurrentSubscriptions;
- }
- };
- when(mMockSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenAnswer(answer);
- }
-
- private TelephonyManager addSubscriptionAndGetMock(int subId) {
- mCurrentSubscriptions.add(getSubInfo(subId));
- TelephonyManager tmSubscription = mock(TelephonyManager.class);
- when(mMockTelephonyManager.createForSubscriptionId(subId)).thenReturn(tmSubscription);
- return tmSubscription;
- }
-
-}
diff --git a/tests/src/com/android/phone/ecc/EccDataTest.java b/tests/src/com/android/phone/ecc/EccDataTest.java
index 911d3c5..baa4c7b 100644
--- a/tests/src/com/android/phone/ecc/EccDataTest.java
+++ b/tests/src/com/android/phone/ecc/EccDataTest.java
@@ -32,6 +32,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
+import java.util.Locale;
import java.util.zip.GZIPInputStream;
/**
@@ -49,10 +50,12 @@
HashSet loadedIsos = new HashSet(300);
HashSet loadedNumbers = new HashSet(5);
+ HashSet loadedMncs = new HashSet(5);
for (ProtobufEccData.CountryInfo countryInfo : allEccMessages.countries) {
assertThat(countryInfo.isoCode).isNotEmpty();
- assertThat(countryInfo.isoCode).isEqualTo(countryInfo.isoCode.toUpperCase().trim());
+ assertThat(countryInfo.isoCode).isEqualTo(countryInfo.isoCode.toUpperCase(
+ Locale.ROOT).trim());
assertThat(loadedIsos.contains(countryInfo.isoCode)).isFalse();
loadedIsos.add(countryInfo.isoCode);
@@ -63,6 +66,17 @@
assertThat(loadedNumbers.contains(eccInfo.phoneNumber)).isFalse();
assertThat(eccInfo.types).isNotEmpty();
loadedNumbers.add(eccInfo.phoneNumber);
+ if (eccInfo.routing == ProtobufEccData.EccInfo.Routing.NORMAL) {
+ loadedMncs.clear();
+ for (String mnc : eccInfo.normalRoutingMncs) {
+ assertThat(mnc).isNotEmpty();
+ assertThat(mnc).isEqualTo(mnc.trim());
+ assertThat(loadedMncs.contains(mnc)).isFalse();
+ assertThat(mnc.length()).isGreaterThan(1);
+ assertThat(mnc.length()).isLessThan(4);
+ loadedMncs.add(mnc);
+ }
+ }
}
}
}
diff --git a/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java b/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
index 8b0ac5c..817220c 100644
--- a/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
+++ b/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.service.euicc.EuiccService;
import android.telephony.euicc.EuiccManager;
import androidx.test.InstrumentationRegistry;
@@ -111,6 +112,24 @@
assertEquals("bar", euiccUiIntent.getStringExtra("foo"));
}
+ @Test
+ public void testTransferEmbeddedSubscriptionsAction() {
+ mIntent = new Intent(EuiccManager.ACTION_TRANSFER_EMBEDDED_SUBSCRIPTIONS);
+ Intent euiccUiIntent = mActivity.resolveEuiccUiIntent();
+ assertNotNull(euiccUiIntent);
+ assertEquals(EuiccService.ACTION_TRANSFER_EMBEDDED_SUBSCRIPTIONS,
+ euiccUiIntent.getAction());
+ }
+
+ @Test
+ public void testConvertToEmbeddedSubscriptionAction() {
+ mIntent = new Intent(EuiccManager.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION);
+ Intent euiccUiIntent = mActivity.resolveEuiccUiIntent();
+ assertNotNull(euiccUiIntent);
+ assertEquals(EuiccService.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION,
+ euiccUiIntent.getAction());
+ }
+
class TestEuiccUiDispatcherActivity extends EuiccUiDispatcherActivity {
public TestEuiccUiDispatcherActivity() {
attachBaseContext(mMockContext);
diff --git a/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
new file mode 100644
index 0000000..b2a4a9f
--- /dev/null
+++ b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) 2022 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.phone.slice;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.NetworkSlicingConfig;
+import android.testing.TestableLooper;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.data.DataSettingsManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import java.time.LocalDate;
+import java.util.Collections;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+public class SlicePurchaseControllerTest extends TelephonyTestBase {
+ private static final String CARRIER = "Some Carrier";
+ private static final String DAILY_NOTIFICATION_COUNT_KEY = "daily_notification_count0";
+ private static final String MONTHLY_NOTIFICATION_COUNT_KEY = "monthly_notification_count0";
+ private static final int YEAR = 2000;
+ private static final int MONTH = 6;
+ private static final int DATE = 1;
+ private static final int PHONE_ID = 0;
+ private static final int DAILY_NOTIFICATION_MAX = 3;
+ private static final int MONTHLY_NOTIFICATION_MAX = 5;
+ private static final long NOTIFICATION_TIMEOUT = 1000;
+ private static final long PURCHASE_CONDITION_TIMEOUT = 2000;
+ private static final long NETWORK_SETUP_TIMEOUT = 3000;
+ private static final long THROTTLE_TIMEOUT = 4000;
+
+ @Mock Phone mPhone;
+ @Mock CarrierConfigManager mCarrierConfigManager;
+ @Mock CommandsInterface mCommandsInterface;
+ @Mock ServiceState mServiceState;
+ @Mock DataSettingsManager mDataSettingsManager;
+ @Mock PremiumNetworkEntitlementApi mPremiumNetworkEntitlementApi;
+ @Mock SharedPreferences mSharedPreferences;
+ @Mock SharedPreferences.Editor mEditor;
+
+ private SlicePurchaseController mSlicePurchaseController;
+ private PersistableBundle mBundle;
+ private PremiumNetworkEntitlementResponse mEntitlementResponse;
+ private Handler mHandler;
+ private TestableLooper mTestableLooper;
+ @TelephonyManager.PurchasePremiumCapabilityResult private int mResult;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ HandlerThread handlerThread = new HandlerThread("SlicePurchaseControllerTest");
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ mResult = (int) ar.result;
+ }
+ };
+ mTestableLooper = new TestableLooper(mHandler.getLooper());
+
+ doReturn(PHONE_ID).when(mPhone).getPhoneId();
+ doReturn(mContext).when(mPhone).getContext();
+ doReturn(mServiceState).when(mPhone).getServiceState();
+ doReturn(mDataSettingsManager).when(mPhone).getDataSettingsManager();
+ mPhone.mCi = mCommandsInterface;
+
+ doReturn(mCarrierConfigManager).when(mContext)
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ mBundle = new PersistableBundle();
+ mBundle.putInt(
+ CarrierConfigManager.KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT,
+ DAILY_NOTIFICATION_MAX);
+ mBundle.putInt(
+ CarrierConfigManager.KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT,
+ MONTHLY_NOTIFICATION_MAX);
+ doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+
+ doReturn(mSharedPreferences).when(mContext).getSharedPreferences(anyString(), anyInt());
+ doReturn(mEditor).when(mSharedPreferences).edit();
+ doAnswer(invocation -> {
+ doReturn(invocation.getArgument(1)).when(mSharedPreferences)
+ .getInt(eq(invocation.getArgument(0)), anyInt());
+ return null;
+ }).when(mEditor).putInt(anyString(), anyInt());
+ doAnswer(invocation -> {
+ doReturn(invocation.getArgument(1)).when(mSharedPreferences)
+ .getString(eq(invocation.getArgument(0)), anyString());
+ return null;
+ }).when(mEditor).putString(anyString(), anyString());
+
+ // create a spy to mock final PendingIntent methods
+ SlicePurchaseController slicePurchaseController =
+ new SlicePurchaseController(mPhone, mHandler.getLooper());
+ mSlicePurchaseController = spy(slicePurchaseController);
+ doReturn(null).when(mSlicePurchaseController).createPendingIntent(
+ anyString(), anyInt(), anyBoolean());
+ doReturn(CARRIER).when(mSlicePurchaseController).getSimOperator();
+ replaceInstance(SlicePurchaseController.class, "sInstances", mSlicePurchaseController,
+ Map.of(PHONE_ID, mSlicePurchaseController));
+ replaceInstance(SlicePurchaseController.class, "mIsSlicingUpsellEnabled",
+ mSlicePurchaseController, true);
+ mEntitlementResponse = new PremiumNetworkEntitlementResponse();
+ doReturn(mPremiumNetworkEntitlementApi).when(mSlicePurchaseController)
+ .getPremiumNetworkEntitlementApi();
+ doReturn(mEntitlementResponse).when(mPremiumNetworkEntitlementApi)
+ .checkEntitlementStatus(anyInt());
+ }
+
+ @Test
+ public void testCreatePendingIntent() {
+ doCallRealMethod().when(mSlicePurchaseController).createPendingIntent(
+ anyString(), anyInt(), anyBoolean());
+ try {
+ mSlicePurchaseController.createPendingIntent(
+ "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_CANCELED",
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY,
+ true);
+ } catch (Exception expected) {
+ return;
+ }
+ fail("Expected createPendingIntent to throw an exception");
+ }
+
+ @Test
+ public void testIsPremiumCapabilityAvailableForPurchase() {
+ assertFalse(mSlicePurchaseController.isPremiumCapabilityAvailableForPurchase(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY));
+
+ // all conditions met
+ doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+ .getCachedAllowedNetworkTypesBitmask();
+ mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+ new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+ mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING,
+ SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
+ doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
+
+ // retry to verify available
+ assertTrue(mSlicePurchaseController.isPremiumCapabilityAvailableForPurchase(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY));
+ }
+
+ @Test
+ public void testGetPurchaseURL() {
+ mEntitlementResponse.mServiceFlowURL = SlicePurchaseController.SLICE_PURCHASE_TEST_FILE;
+ String purchaseUrl = mSlicePurchaseController.getPurchaseUrl(mEntitlementResponse);
+ assertEquals(purchaseUrl, SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
+
+ mEntitlementResponse.mServiceFlowURL = null;
+ mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING,
+ SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
+ purchaseUrl = mSlicePurchaseController.getPurchaseUrl(mEntitlementResponse);
+ assertEquals(purchaseUrl, SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
+
+ String[] invalidUrls = new String[] {
+ null,
+ "",
+ "www.google.com",
+ "htt://www.google.com",
+ "http//www.google.com",
+ "http:/www.google.com",
+ "file:///android_asset/",
+ "file:///android_asset/slice_store_test.html"
+ };
+ for (String url : invalidUrls) {
+ mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING, url);
+ assertEquals("", mSlicePurchaseController.getPurchaseUrl(mEntitlementResponse));
+ }
+ }
+
+ @Test
+ public void testUpdateNotificationCounts() {
+ mSlicePurchaseController.setLocalDate(LocalDate.of(YEAR, MONTH, DATE));
+ mSlicePurchaseController.updateNotificationCounts();
+
+ // change only date, month and year remain the same
+ Mockito.clearInvocations(mEditor);
+ mSlicePurchaseController.setLocalDate(LocalDate.of(YEAR, MONTH, DATE + 1));
+ mSlicePurchaseController.updateNotificationCounts();
+ verify(mEditor).putInt(eq(DAILY_NOTIFICATION_COUNT_KEY), eq(0));
+ verify(mEditor, never()).putInt(eq(MONTHLY_NOTIFICATION_COUNT_KEY), eq(0));
+
+ // change only month, date and year remain the same
+ Mockito.clearInvocations(mEditor);
+ mSlicePurchaseController.setLocalDate(LocalDate.of(YEAR, MONTH + 1, DATE + 1));
+ mSlicePurchaseController.updateNotificationCounts();
+ verify(mEditor).putInt(eq(DAILY_NOTIFICATION_COUNT_KEY), eq(0));
+ verify(mEditor).putInt(eq(MONTHLY_NOTIFICATION_COUNT_KEY), eq(0));
+
+ // change only year, date and month remain the same
+ Mockito.clearInvocations(mEditor);
+ mSlicePurchaseController.setLocalDate(LocalDate.of(YEAR + 1, MONTH + 1, DATE + 1));
+ mSlicePurchaseController.updateNotificationCounts();
+ verify(mEditor).putInt(eq(DAILY_NOTIFICATION_COUNT_KEY), eq(0));
+ verify(mEditor).putInt(eq(MONTHLY_NOTIFICATION_COUNT_KEY), eq(0));
+
+ // change only month and year, date remains the same
+ Mockito.clearInvocations(mEditor);
+ mSlicePurchaseController.setLocalDate(LocalDate.of(YEAR + 2, MONTH + 2, DATE + 1));
+ mSlicePurchaseController.updateNotificationCounts();
+ verify(mEditor).putInt(eq(DAILY_NOTIFICATION_COUNT_KEY), eq(0));
+ verify(mEditor).putInt(eq(MONTHLY_NOTIFICATION_COUNT_KEY), eq(0));
+
+ // change only date and year, month remains the same
+ Mockito.clearInvocations(mEditor);
+ mSlicePurchaseController.setLocalDate(LocalDate.of(YEAR + 3, MONTH + 2, DATE + 2));
+ mSlicePurchaseController.updateNotificationCounts();
+ verify(mEditor).putInt(eq(DAILY_NOTIFICATION_COUNT_KEY), eq(0));
+ verify(mEditor).putInt(eq(MONTHLY_NOTIFICATION_COUNT_KEY), eq(0));
+
+ // change only date and month, year remains the same
+ Mockito.clearInvocations(mEditor);
+ mSlicePurchaseController.setLocalDate(LocalDate.of(YEAR + 3, MONTH + 3, DATE + 3));
+ mSlicePurchaseController.updateNotificationCounts();
+ verify(mEditor).putInt(eq(DAILY_NOTIFICATION_COUNT_KEY), eq(0));
+ verify(mEditor).putInt(eq(MONTHLY_NOTIFICATION_COUNT_KEY), eq(0));
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultFeatureNotSupported() {
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
+ mResult);
+
+ // retry after enabling feature
+ doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+ .getCachedAllowedNetworkTypesBitmask();
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
+ mResult);
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultCarrierDisabled() {
+ doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+ .getCachedAllowedNetworkTypesBitmask();
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED, mResult);
+
+ // retry after enabling carrier configs
+ mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+ new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+ mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING,
+ SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
+ mResult);
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultNotDefaultDataSubscription() {
+ doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+ .getCachedAllowedNetworkTypesBitmask();
+ mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+ new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+ mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING,
+ SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(
+ TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
+ mResult);
+
+ // retry on default data subscription
+ doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertNotEquals(
+ TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
+ mResult);
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultNetworkNotAvailable() {
+ doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+ .getCachedAllowedNetworkTypesBitmask();
+ mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+ new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+ mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING,
+ SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
+ doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
+ mResult);
+
+ // retry with valid network
+ doReturn(TelephonyManager.NETWORK_TYPE_NR).when(mServiceState).getDataNetworkType();
+ doReturn(true).when(mDataSettingsManager).isDataEnabledForReason(anyInt());
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
+ mResult);
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultEntitlementCheckFailed() {
+ doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+ .getCachedAllowedNetworkTypesBitmask();
+ mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+ new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+ mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING,
+ SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
+ doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
+ doReturn(TelephonyManager.NETWORK_TYPE_NR).when(mServiceState).getDataNetworkType();
+ doReturn(true).when(mDataSettingsManager).isDataEnabledForReason(anyInt());
+ doReturn(null).when(mPremiumNetworkEntitlementApi).checkEntitlementStatus(anyInt());
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR, mResult);
+
+ // retry with provisioned response
+ mEntitlementResponse.mEntitlementStatus =
+ PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED;
+ mEntitlementResponse.mProvisionStatus =
+ PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED;
+ doReturn(mEntitlementResponse).when(mPremiumNetworkEntitlementApi)
+ .checkEntitlementStatus(anyInt());
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
+ mResult);
+
+ // retry with provisioning response
+ mEntitlementResponse.mEntitlementStatus =
+ PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING;
+ mEntitlementResponse.mProvisionStatus =
+ PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS;
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
+ mResult);
+
+ // retry with disallowed response and throttling
+ mEntitlementResponse.mProvisionStatus =
+ PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED;
+ mEntitlementResponse.mEntitlementStatus =
+ PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCOMPATIBLE;
+ mBundle.putLong(CarrierConfigManager
+ .KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
+ PURCHASE_CONDITION_TIMEOUT);
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED,
+ mResult);
+
+ // retry to verify throttled
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
+
+ // retry with valid entitlement check to verify unthrottled
+ mTestableLooper.moveTimeForward(PURCHASE_CONDITION_TIMEOUT);
+ mTestableLooper.processAllMessages();
+
+ testPurchasePremiumCapabilityResultSuccess();
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultAlreadyInProgress() {
+ sendValidPurchaseRequest();
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
+ mResult);
+
+ // retry to verify same result
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
+ mResult);
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultSuccess() {
+ sendValidPurchaseRequest();
+
+ // broadcast SUCCESS response from slice purchase application
+ Intent intent = new Intent();
+ intent.setAction("com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_SUCCESS");
+ intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+ intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+ mContext.getBroadcastReceiver().onReceive(mContext, intent);
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS, mResult);
+
+ // retry tested in testPurchasePremiumCapabilityResultPendingNetworkSetup
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultPendingNetworkSetup() {
+ testPurchasePremiumCapabilityResultSuccess();
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP,
+ mResult);
+
+ // retry to verify unthrottled
+ mTestableLooper.moveTimeForward(NETWORK_SETUP_TIMEOUT);
+ mTestableLooper.processAllMessages();
+
+ testPurchasePremiumCapabilityResultSuccess();
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultAlreadyPurchased() {
+ testPurchasePremiumCapabilityResultSuccess();
+
+ sendNetworkSlicingConfig(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, true);
+
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
+ mResult);
+
+ // retry to verify same result
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
+ mResult);
+
+ // retry to verify purchase expired
+ sendNetworkSlicingConfig(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, false);
+
+ testPurchasePremiumCapabilityResultSuccess();
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultTimeout() {
+ sendValidPurchaseRequest();
+
+ mTestableLooper.moveTimeForward(NOTIFICATION_TIMEOUT);
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT, mResult);
+
+ // retry to verify throttled
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
+
+ // retry to verify unthrottled
+ mTestableLooper.moveTimeForward(THROTTLE_TIMEOUT);
+ mTestableLooper.processAllMessages();
+
+ testPurchasePremiumCapabilityResultSuccess();
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultUserCanceled() {
+ sendValidPurchaseRequest();
+
+ // broadcast CANCELED response from slice purchase application
+ Intent intent = new Intent();
+ intent.setAction("com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_CANCELED");
+ intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+ intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+ mContext.getBroadcastReceiver().onReceive(mContext, intent);
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED, mResult);
+
+ // retry to verify throttled
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
+
+ // retry to verify unthrottled
+ mTestableLooper.moveTimeForward(THROTTLE_TIMEOUT);
+ mTestableLooper.processAllMessages();
+
+ testPurchasePremiumCapabilityResultSuccess();
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultCarrierError() {
+ sendValidPurchaseRequest();
+
+ // broadcast CARRIER_ERROR response from slice purchase application
+ Intent intent = new Intent();
+ intent.setAction(
+ "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR");
+ intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+ intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+ intent.putExtra(SlicePurchaseController.EXTRA_FAILURE_CODE,
+ SlicePurchaseController.FAILURE_CODE_SERVER_UNREACHABLE);
+ mContext.getBroadcastReceiver().onReceive(mContext, intent);
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR, mResult);
+
+ // retry to verify throttled
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
+
+ // retry to verify unthrottled
+ mTestableLooper.moveTimeForward(PURCHASE_CONDITION_TIMEOUT);
+ mTestableLooper.processAllMessages();
+
+ testPurchasePremiumCapabilityResultSuccess();
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultRequestFailed() {
+ sendValidPurchaseRequest();
+
+ // broadcast REQUEST_FAILED response from slice purchase application
+ Intent intent = new Intent();
+ intent.setAction(
+ "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED");
+ intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+ intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+ mContext.getBroadcastReceiver().onReceive(mContext, intent);
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED, mResult);
+
+ // retry to verify no throttling
+ testPurchasePremiumCapabilityResultSuccess();
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultNotDefaultDataSubscriptionResponse() {
+ sendValidPurchaseRequest();
+
+ Intent intent = new Intent();
+ intent.setAction("com.android.phone.slice.action."
+ + "SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION");
+ intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+ intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+ mContext.getBroadcastReceiver().onReceive(mContext, intent);
+ mTestableLooper.processAllMessages();
+ assertEquals(
+ TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
+ mResult);
+
+ // retry to verify no throttling
+ testPurchasePremiumCapabilityResultSuccess();
+ }
+
+ @Test
+ public void testPurchasePremiumCapabilityResultNotificationThrottled() {
+ mSlicePurchaseController.setLocalDate(LocalDate.of(YEAR, MONTH, DATE));
+ mSlicePurchaseController.updateNotificationCounts();
+
+ for (int count = 1; count <= DAILY_NOTIFICATION_MAX; count++) {
+ completeSuccessfulPurchase();
+ verify(mEditor).putInt(eq(DAILY_NOTIFICATION_COUNT_KEY), eq(count));
+ verify(mEditor).putInt(eq(MONTHLY_NOTIFICATION_COUNT_KEY), eq(count));
+ }
+
+ // retry to verify throttled
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
+
+ // change the date to trigger daily reset
+ mSlicePurchaseController.setLocalDate(LocalDate.of(YEAR, MONTH, DATE + 1));
+ Mockito.clearInvocations(mEditor);
+
+ for (int count = 1; count <= (MONTHLY_NOTIFICATION_MAX - DAILY_NOTIFICATION_MAX); count++) {
+ completeSuccessfulPurchase();
+ verify(mEditor).putInt(eq(DAILY_NOTIFICATION_COUNT_KEY), eq(count));
+ verify(mEditor).putInt(eq(MONTHLY_NOTIFICATION_COUNT_KEY),
+ eq(count + DAILY_NOTIFICATION_MAX));
+ }
+
+ // retry to verify throttled
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
+ }
+
+ private void completeSuccessfulPurchase() {
+ sendValidPurchaseRequest();
+
+ // broadcast NOTIFICATION_SHOWN response from slice purchase application
+ Intent intent = new Intent();
+ intent.setAction(
+ "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN");
+ intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+ intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+ mContext.getBroadcastReceiver().onReceive(mContext, intent);
+ mTestableLooper.processAllMessages();
+
+ // broadcast SUCCESS response from slice purchase application
+ intent.setAction("com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_SUCCESS");
+ mContext.getBroadcastReceiver().onReceive(mContext, intent);
+ mTestableLooper.processAllMessages();
+ assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS, mResult);
+
+ // complete network setup
+ sendNetworkSlicingConfig(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, true);
+ // purchase expired
+ sendNetworkSlicingConfig(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, false);
+ }
+
+ private void sendValidPurchaseRequest() {
+ clearInvocations(mContext);
+
+ // feature supported
+ doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+ .getCachedAllowedNetworkTypesBitmask();
+ // carrier supported
+ mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+ new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+ mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING,
+ SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
+ mBundle.putLong(CarrierConfigManager
+ .KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG,
+ NOTIFICATION_TIMEOUT);
+ mBundle.putLong(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG,
+ NETWORK_SETUP_TIMEOUT);
+ mBundle.putLong(CarrierConfigManager
+ .KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
+ THROTTLE_TIMEOUT);
+ mBundle.putLong(CarrierConfigManager
+ .KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
+ PURCHASE_CONDITION_TIMEOUT);
+ // default data subscription
+ doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
+ // network available
+ doReturn(TelephonyManager.NETWORK_TYPE_NR).when(mServiceState).getDataNetworkType();
+ doReturn(true).when(mDataSettingsManager).isDataEnabledForReason(anyInt());
+ // entitlement check passed
+ mEntitlementResponse.mEntitlementStatus =
+ PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED;
+ mEntitlementResponse.mProvisionStatus =
+ PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED;
+
+ // send purchase request
+ mSlicePurchaseController.purchasePremiumCapability(
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+ mTestableLooper.processAllMessages();
+
+ // verify that the purchase request was sent successfully
+ verify(mContext).sendBroadcast(any(Intent.class));
+ assertEquals(SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP,
+ mContext.getBroadcast().getAction());
+ assertTrue(mSlicePurchaseController.hasMessages(4 /* EVENT_PURCHASE_TIMEOUT */,
+ TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY));
+ verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class),
+ eq(Context.RECEIVER_NOT_EXPORTED));
+ }
+
+ private void sendNetworkSlicingConfig(int capability, boolean configActive) {
+ int sliceServiceType = capability == TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY
+ ? NetworkSliceInfo.SLICE_SERVICE_TYPE_URLLC
+ : NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE;
+ NetworkSliceInfo sliceInfo = new NetworkSliceInfo.Builder()
+ .setStatus(configActive ? NetworkSliceInfo.SLICE_STATUS_ALLOWED
+ : NetworkSliceInfo.SLICE_STATUS_UNKNOWN)
+ .setSliceServiceType(sliceServiceType)
+ .build();
+ NetworkSlicingConfig slicingConfig = new NetworkSlicingConfig(Collections.emptyList(),
+ Collections.singletonList(sliceInfo));
+ mSlicePurchaseController.obtainMessage(2 /* EVENT_SLICING_CONFIG_CHANGED */,
+ new AsyncResult(null, slicingConfig, null)).sendToTarget();
+ mTestableLooper.processAllMessages();
+ }
+}
diff --git a/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
index 28a7b02..969622a 100644
--- a/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
+++ b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
@@ -38,19 +38,14 @@
import com.android.internal.telephony.PhoneFactory;
import com.android.phone.common.R;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Locale;
-
@RunWith(AndroidJUnit4.class)
public class DisconnectCauseUtilTest extends TelephonyTestBase {
@@ -60,42 +55,11 @@
// dynamic
private Context mContext;
- private HashMap<InstanceKey, Object> mOldInstances = new HashMap<InstanceKey, Object>();
- private ArrayList<InstanceKey> mInstanceKeys = new ArrayList<InstanceKey>();
//Mocks
@Mock
private GsmCdmaPhone mMockPhone;
- // inner classes
- private static class InstanceKey {
- public final Class mClass;
- public final String mInstName;
- public final Object mObj;
-
- InstanceKey(final Class c, final String instName, final Object obj) {
- mClass = c;
- mInstName = instName;
- mObj = obj;
- }
-
- @Override
- public int hashCode() {
- return (mClass.getName().hashCode() * 31 + mInstName.hashCode()) * 31;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null || !(obj instanceof InstanceKey)) {
- return false;
- }
-
- InstanceKey other = (InstanceKey) obj;
- return (other.mClass == mClass && other.mInstName.equals(mInstName)
- && other.mObj == mObj);
- }
- }
-
@Before
public void setUp() throws Exception {
super.setUp();
@@ -106,15 +70,6 @@
setSinglePhone();
}
- @After
- public void tearDown() throws Exception {
- // restoreInstance.
- // Not doing so will potentially "confuse" other tests with the mocked instance
- restoreInstance(PhoneFactory.class, "sPhones", null);
- super.tearDown();
- }
-
-
/**
* Verifies that a call drop due to loss of WIFI results in a disconnect cause of error and that
* the label, description and tone are all present.
@@ -176,33 +131,6 @@
replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
}
-
- protected synchronized void replaceInstance(final Class c, final String instanceName,
- final Object obj, final Object newValue)
- throws Exception {
- Field field = c.getDeclaredField(instanceName);
- field.setAccessible(true);
-
- InstanceKey key = new InstanceKey(c, instanceName, obj);
- if (!mOldInstances.containsKey(key)) {
- mOldInstances.put(key, field.get(obj));
- mInstanceKeys.add(key);
- }
- field.set(obj, newValue);
- }
-
- protected synchronized void restoreInstance(final Class c, final String instanceName,
- final Object obj) throws Exception {
- InstanceKey key = new InstanceKey(c, instanceName, obj);
- if (mOldInstances.containsKey(key)) {
- Field field = c.getDeclaredField(instanceName);
- field.setAccessible(true);
- field.set(obj, mOldInstances.get(key));
- mOldInstances.remove(key);
- mInstanceKeys.remove(key);
- }
- }
-
private Resources getResourcesForLocale(Context context, Locale locale) {
Configuration config = new Configuration();
config.setToDefaults();
diff --git a/tests/src/com/android/services/telephony/HoldTrackerTest.java b/tests/src/com/android/services/telephony/HoldTrackerTest.java
index 772a1a2..83195db 100644
--- a/tests/src/com/android/services/telephony/HoldTrackerTest.java
+++ b/tests/src/com/android/services/telephony/HoldTrackerTest.java
@@ -19,9 +19,6 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import android.content.ComponentName;
-import android.telecom.PhoneAccountHandle;
-
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
@@ -32,72 +29,53 @@
public class HoldTrackerTest {
private HoldTracker mHoldTrackerUT;
- private PhoneAccountHandle mPhoneAccountHandle1;
- private PhoneAccountHandle mPhoneAccountHandle2;
@Before
public void setUp() throws Exception {
mHoldTrackerUT = new HoldTracker();
- mPhoneAccountHandle1 =
- new PhoneAccountHandle(new ComponentName("pkg1", "cls1"), "0");
- mPhoneAccountHandle2 =
- new PhoneAccountHandle(new ComponentName("pkg2", "cls2"), "1");
}
@Test
public void oneTopHoldableCanBeHeld() {
FakeHoldable topHoldable = createHoldable(false);
- mHoldTrackerUT.addHoldable(mPhoneAccountHandle1, topHoldable);
+ mHoldTrackerUT.addHoldable(topHoldable);
assertTrue(topHoldable.canBeHeld());
}
@Test
- public void childHoldableCanNotBeHeld() {
+ public void childHoldableCannotBeHeld() {
FakeHoldable topHoldable = createHoldable(false);
FakeHoldable childHoldable = createHoldable(true);
- mHoldTrackerUT.addHoldable(mPhoneAccountHandle1, topHoldable);
- mHoldTrackerUT.addHoldable(mPhoneAccountHandle1, childHoldable);
+ mHoldTrackerUT.addHoldable(topHoldable);
+ mHoldTrackerUT.addHoldable(childHoldable);
assertTrue(topHoldable.canBeHeld());
assertFalse(childHoldable.canBeHeld());
}
@Test
- public void twoTopHoldableWithTheSamePhoneAccountCanNotBeHeld() {
+ public void twoTopHoldablesCannotBeHeld() {
FakeHoldable topHoldable1 = createHoldable(false);
FakeHoldable topHoldable2 = createHoldable(false);
- mHoldTrackerUT.addHoldable(mPhoneAccountHandle1, topHoldable1);
- mHoldTrackerUT.addHoldable(mPhoneAccountHandle1, topHoldable2);
+ mHoldTrackerUT.addHoldable(topHoldable1);
+ mHoldTrackerUT.addHoldable(topHoldable2);
- mHoldTrackerUT.updateHoldCapability(mPhoneAccountHandle1);
+ mHoldTrackerUT.updateHoldCapability();
assertFalse(topHoldable1.canBeHeld());
assertFalse(topHoldable2.canBeHeld());
}
@Test
- public void holdableWithDifferentPhoneAccountDoesNotAffectEachOther() {
- FakeHoldable topHoldable1 = createHoldable(false);
- FakeHoldable topHoldable2 = createHoldable(false);
- mHoldTrackerUT.addHoldable(mPhoneAccountHandle1, topHoldable1);
- mHoldTrackerUT.addHoldable(mPhoneAccountHandle2, topHoldable2);
-
- // Both phones account have only one top holdable, so the holdable of each phone account can
- // be held.
- assertTrue(topHoldable1.canBeHeld());
- assertTrue(topHoldable2.canBeHeld());
- }
-
- @Test
public void removeOneTopHoldableAndUpdateHoldCapabilityCorrectly() {
FakeHoldable topHoldable1 = createHoldable(false);
FakeHoldable topHoldable2 = createHoldable(false);
- mHoldTrackerUT.addHoldable(mPhoneAccountHandle1, topHoldable1);
- mHoldTrackerUT.addHoldable(mPhoneAccountHandle1, topHoldable2);
+ mHoldTrackerUT.addHoldable(topHoldable1);
+ mHoldTrackerUT.addHoldable(topHoldable2);
assertFalse(topHoldable1.canBeHeld());
assertFalse(topHoldable2.canBeHeld());
- mHoldTrackerUT.removeHoldable(mPhoneAccountHandle1, topHoldable1);
+ mHoldTrackerUT.removeHoldable(topHoldable1);
assertTrue(topHoldable2.canBeHeld());
}
diff --git a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
index 33c8b8a..a9207e6 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
@@ -24,7 +24,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.os.Looper;
+import android.telecom.PhoneAccountHandle;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Before;
@@ -46,6 +48,14 @@
@Mock
private TelecomAccountRegistry mMockTelecomAccountRegistry;
+ private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
+ "com.android.phone.tests", ImsConferenceControllerTest.class.getName());
+ private static final String TEST_ACCOUNT_ID1 = "id1";
+ private static final String TEST_ACCOUNT_ID2 = "id2";
+ private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_1 = new PhoneAccountHandle(
+ TEST_COMPONENT_NAME, TEST_ACCOUNT_ID1);
+ private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_2 = new PhoneAccountHandle(
+ TEST_COMPONENT_NAME, TEST_ACCOUNT_ID2);
private TestTelephonyConnection mTestTelephonyConnectionA;
private TestTelephonyConnection mTestTelephonyConnectionB;
@@ -61,6 +71,9 @@
mTestTelephonyConnectionA = new TestTelephonyConnection();
mTestTelephonyConnectionB = new TestTelephonyConnection();
+ mTestTelephonyConnectionA.setPhoneAccountHandle(PHONE_ACCOUNT_HANDLE_1);
+ mTestTelephonyConnectionB.setPhoneAccountHandle(PHONE_ACCOUNT_HANDLE_1);
+
mControllerTest = new ImsConferenceController(mTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, () -> false);
}
@@ -99,6 +112,27 @@
}
/**
+ * Behavior: add telephony connections A and B to conference controller;
+ * Assumption: Connection A and B have different PhoneAccountHandles, belong to different subs;
+ * Expected: Connection A and Connection B are not conferenceable with each other;
+ */
+ @Test
+ @SmallTest
+ public void testCallsOnDifferentSubsNotConferenceable() {
+ mTestTelephonyConnectionB.setPhoneAccountHandle(PHONE_ACCOUNT_HANDLE_2);
+ mControllerTest.add(mTestTelephonyConnectionA);
+ mControllerTest.add(mTestTelephonyConnectionB);
+
+ mTestTelephonyConnectionA.setActive();
+ mTestTelephonyConnectionB.setTelephonyConnectionOnHold();
+
+ assertFalse(mTestTelephonyConnectionA.getConferenceables()
+ .contains(mTestTelephonyConnectionB));
+ assertFalse(mTestTelephonyConnectionB.getConferenceables()
+ .contains(mTestTelephonyConnectionA));
+ }
+
+ /**
* Behavior: add telephony connection B and A to conference controller,
* set status for merged connections
* Assumption: after performing the behaviors, the status of Connection A is STATE_ACTIVE;
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index 9d2f5ac..e2a199b 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -121,6 +122,58 @@
}
}
+ /**
+ * Verifies that an ImsConference will inform listeners when the "fullness" of the conference
+ * changes as participants come and go.
+ */
+ @Test
+ @SmallTest
+ public void testNotifyOnConferenceCapacityChanged() {
+ ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+ mMockTelephonyConnectionServiceProxy, mConferenceHost,
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder()
+ .setIsMaximumConferenceSizeEnforced(true)
+ .setMaximumConferenceSize(2)
+ .build());
+ TelephonyConferenceBase.TelephonyConferenceListener listener =
+ mock(TelephonyConferenceBase.TelephonyConferenceListener.class);
+ imsConference.addTelephonyConferenceListener(listener);
+
+ ConferenceParticipant participant1 = new ConferenceParticipant(
+ Uri.parse("tel:6505551212"),
+ "A",
+ Uri.parse("sip:6505551212@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_OUTGOING);
+ ConferenceParticipant participant2 = new ConferenceParticipant(
+ Uri.parse("tel:6505551213"),
+ "A",
+ Uri.parse("sip:6505551213@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_INCOMING);
+
+ // no capacity change since we haven't hit the limit yet.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1));
+ verify(listener, never()).onConferenceCapacityChanged();
+
+ // Now we should get a capacity change
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2));
+ verify(listener, times(1)).onConferenceCapacityChanged();
+
+ // And another when we go back to a non-full conference.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1));
+ verify(listener, times(2)).onConferenceCapacityChanged();
+
+ // But not when we reduce count further.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Collections.emptyList());
+ verify(listener, times(2)).onConferenceCapacityChanged();
+ }
+
@Test
@SmallTest
public void testSinglePartyEmulation() {
@@ -590,7 +643,7 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> false /* featureFlagProxy */,
+ null /* phoneAccountHandle */, () -> true /* isUsingSinglePartyCallEmulation */,
new ImsConference.CarrierConfiguration.Builder()
.setShouldLocalDisconnectEmptyConference(true)
.build());
@@ -622,6 +675,109 @@
}
/**
+ * Preconditions: both single party emulation and local disconnect of empty conferences is
+ * enabled.
+ * Tests the case where we receive a repeat with the same single-party data that caused a
+ * conference to be treated as a single party; we need to verify that we do not disconnect the
+ * conference locally in this case.
+ * @throws Exception
+ */
+ @Test
+ @SmallTest
+ public void testNoLocalDisconnectSinglePartyConferenceOnRepeatedCep() throws Exception {
+ when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+ .thenReturn(false);
+
+ ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+ mMockTelephonyConnectionServiceProxy, mConferenceHost,
+ null /* phoneAccountHandle */, () -> true /* isUsingSinglePartyCallEmulation */,
+ new ImsConference.CarrierConfiguration.Builder()
+ .setShouldLocalDisconnectEmptyConference(true)
+ .build());
+
+ ConferenceParticipant participant1 = new ConferenceParticipant(
+ Uri.parse("tel:6505551212"),
+ "A",
+ Uri.parse("sip:6505551212@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_INCOMING);
+ ConferenceParticipant participant2 = new ConferenceParticipant(
+ Uri.parse("tel:6505551213"),
+ "A",
+ Uri.parse("sip:6505551213@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_INCOMING);
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2));
+ assertEquals(2, imsConference.getNumberOfParticipants());
+
+ // Drop to 1 participant which enters single party mode.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1));
+ assertEquals(0, imsConference.getNumberOfParticipants());
+
+ // Get a repeat CEP with the same participant data; we should still be in single party mode
+ // but we should NOT disconnect the conference.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1));
+ assertEquals(0, imsConference.getNumberOfParticipants());
+ verify(mConferenceHost.mMockCall, never()).hangup();
+ }
+
+ /**
+ * An extension of {@link #testNoLocalDisconnectSinglePartyConferenceOnRepeatedCep()} where we
+ * get a repeated CEP with the same single party state, but then finally get a CEP with no
+ * participants anymore. In this case we do expect a local disconnect as the final state.
+ * @throws Exception
+ */
+ @Test
+ @SmallTest
+ public void testLocalDisconnectSinglePartyConferenceOnRepeatedCep() throws Exception {
+ when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+ .thenReturn(false);
+
+ ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+ mMockTelephonyConnectionServiceProxy, mConferenceHost,
+ null /* phoneAccountHandle */, () -> true /* isUsingSinglePartyCallEmulation */,
+ new ImsConference.CarrierConfiguration.Builder()
+ .setShouldLocalDisconnectEmptyConference(true)
+ .build());
+
+ ConferenceParticipant participant1 = new ConferenceParticipant(
+ Uri.parse("tel:6505551212"),
+ "A",
+ Uri.parse("sip:6505551212@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_INCOMING);
+ ConferenceParticipant participant2 = new ConferenceParticipant(
+ Uri.parse("tel:6505551213"),
+ "A",
+ Uri.parse("sip:6505551213@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_INCOMING);
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2));
+ assertEquals(2, imsConference.getNumberOfParticipants());
+
+ // Drop to 1 participant which enters single party mode.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1));
+ assertEquals(0, imsConference.getNumberOfParticipants());
+
+ // Get a repeat CEP with the same participant data; we should still be in single party mode
+ // but we should NOT disconnect the conference.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1));
+ assertEquals(0, imsConference.getNumberOfParticipants());
+ verify(mConferenceHost.mMockCall, never()).hangup();
+
+ // Got another CEP that has no participants at all; we should disconnet in this case
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost, Collections.emptyList());
+ assertEquals(0, imsConference.getNumberOfParticipants());
+ verify(mConferenceHost.mMockCall).hangup();
+ }
+
+ /**
* Tests a scenario where a handover connection arrives via
* {@link TelephonyConnection#onOriginalConnectionRedialed(
* com.android.internal.telephony.Connection)}. During this process, the conference properties
diff --git a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
deleted file mode 100644
index d55a2fa..0000000
--- a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2016 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.services.telephony;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.telephony.ServiceState;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.TelephonyTestBase;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-/**
- * Tests the RadioOnStateListener, which listens to one Phone and waits until its service
- * state changes to accepting emergency calls or in service. If it can not find a tower to camp onto
- * for emergency calls, then it will fail after a timeout period.
- */
-@RunWith(AndroidJUnit4.class)
-public class RadioOnStateListenerTest extends TelephonyTestBase {
-
- private static final long TIMEOUT_MS = 1000;
-
- @Mock Phone mMockPhone;
- @Mock RadioOnStateListener.Callback mCallback;
- @Mock CommandsInterface mMockCi;
- RadioOnStateListener mListener;
-
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- mListener = new RadioOnStateListener();
- }
-
- @Override
- @After
- public void tearDown() throws Exception {
- mListener.setTimeBetweenRetriesMillis(5000);
- mListener.setMaxNumRetries(5);
- mListener.getHandler().removeCallbacksAndMessages(null);
- // Wait for the queue to clear...
- waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS /*ms timeout*/);
- mListener = null;
- super.tearDown();
- }
-
- /**
- * Ensure that we successfully register for the ServiceState changed messages in Telephony.
- */
- @Test
- @SmallTest
- public void testRegisterForCallback() {
- mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
-
- waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
-
- verify(mMockPhone).unregisterForServiceStateChanged(any(Handler.class));
- verify(mMockPhone).registerForServiceStateChanged(any(Handler.class),
- eq(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED), isNull());
-
- verify(mMockCi).registerForOffOrNotAvailable(any(Handler.class),
- eq(RadioOnStateListener.MSG_RADIO_OFF_OR_NOT_AVAILABLE), isNull());
- }
-
- /**
- * {@link RadioOnStateListener.Callback#isOkToCall(Phone, int)} returns true, so we are
- * expecting {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} to
- * return true.
- */
- @Test
- @SmallTest
- public void testPhoneChangeState_OkToCallTrue() {
- ServiceState state = new ServiceState();
- state.setState(ServiceState.STATE_IN_SERVICE);
- when(mMockPhone.getServiceState()).thenReturn(state);
- when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
- when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(true);
- mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
- waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
-
- mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
- new AsyncResult(null, state, null)).sendToTarget();
-
- waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
- verify(mCallback).onComplete(eq(mListener), eq(true));
- }
-
- /**
- * We never receive a
- * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} because
- * {@link RadioOnStateListener.Callback#isOkToCall(Phone, int)} returns false.
- */
- @Test
- @SmallTest
- public void testPhoneChangeState_NoOkToCall_Timeout() {
- ServiceState state = new ServiceState();
- state.setState(ServiceState.STATE_OUT_OF_SERVICE);
- when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
- when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
- when(mMockPhone.getServiceState()).thenReturn(state);
- mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
- waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
-
- mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
- new AsyncResult(null, state, null)).sendToTarget();
-
- waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
- verify(mCallback, never()).onComplete(any(RadioOnStateListener.class), anyBoolean());
- }
-
- /**
- * Tests {@link RadioOnStateListener.Callback#isOkToCall(Phone, int)} returning false and
- * hitting the max number of retries. This should result in
- * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} returning
- * false.
- */
- @Test
- @SmallTest
- public void testTimeout_RetryFailure() {
- ServiceState state = new ServiceState();
- state.setState(ServiceState.STATE_POWER_OFF);
- when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
- when(mMockPhone.getServiceState()).thenReturn(state);
- when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
- mListener.setTimeBetweenRetriesMillis(0/*ms*/);
- mListener.setMaxNumRetries(2);
-
- // Wait for the timer to expire and check state manually in onRetryTimeout
- mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
- waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
-
- verify(mCallback).onComplete(eq(mListener), eq(false));
- verify(mMockPhone, times(2)).setRadioPower(eq(true),
- eq(false), eq(false), eq(false));
- }
-
- @Test
- @SmallTest
- public void testTimeout_RetryFailure_ForEmergency() {
- ServiceState state = new ServiceState();
- state.setState(ServiceState.STATE_POWER_OFF);
- when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
- when(mMockPhone.getServiceState()).thenReturn(state);
- when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
- mListener.setTimeBetweenRetriesMillis(0/*ms*/);
- mListener.setMaxNumRetries(2);
-
- // Wait for the timer to expire and check state manually in onRetryTimeout
- mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback, true, true);
- waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
-
- verify(mCallback).onComplete(eq(mListener), eq(false));
- verify(mMockPhone, times(2)).setRadioPower(eq(true),
- eq(true), eq(true), eq(false));
- }
-}
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index efa906e..4f9b879 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -16,7 +16,18 @@
package com.android.services.telephony;
+import static android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
+import static android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE;
+import static android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+import static android.telephony.DisconnectCause.NOT_DISCONNECTED;
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE;
+import static android.telephony.ims.ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL;
+
import static com.android.internal.telephony.RILConstants.GSM_PHONE;
+import static com.android.services.telephony.TelephonyConnectionService.TIMEOUT_TO_DYNAMIC_ROUTING_MS;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -25,6 +36,7 @@
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
@@ -33,7 +45,9 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
@@ -42,29 +56,51 @@
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
+import android.telecom.Conference;
+import android.telecom.Conferenceable;
import android.telecom.ConnectionRequest;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
+import android.telephony.DataSpecificRegistrationInfo;
+import android.telephony.DomainSelectionService;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsReasonInfo;
import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.TelephonyTestBase;
+import com.android.ims.ImsManager;
import com.android.internal.telecom.IConnectionService;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.data.PhoneSwitcher;
+import com.android.internal.telephony.domainselection.DomainSelectionConnection;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.domainselection.EmergencyCallDomainSelectionConnection;
+import com.android.internal.telephony.domainselection.NormalCallDomainSelectionConnection;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
+import com.android.internal.telephony.emergency.RadioOnHelper;
+import com.android.internal.telephony.emergency.RadioOnStateListener;
import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.satellite.SatelliteController;
+import com.android.internal.telephony.satellite.SatelliteSOSMessageRecommender;
import org.junit.After;
import org.junit.Before;
@@ -72,11 +108,16 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
/**
* Unit tests for TelephonyConnectionService.
@@ -90,6 +131,8 @@
*/
public static class SimpleTelephonyConnection extends TelephonyConnection {
public boolean wasDisconnected = false;
+ public boolean wasUnheld = false;
+ public boolean wasHeld = false;
@Override
public TelephonyConnection cloneConnection() {
@@ -100,6 +143,29 @@
public void hangup(int telephonyDisconnectCode) {
wasDisconnected = true;
}
+
+ @Override
+ public void onUnhold() {
+ wasUnheld = true;
+ }
+
+ @Override
+ public void onHold() {
+ wasHeld = true;
+ }
+ }
+
+ public static class SimpleConference extends Conference {
+ public boolean wasUnheld = false;
+
+ public SimpleConference(PhoneAccountHandle phoneAccountHandle) {
+ super(phoneAccountHandle);
+ }
+
+ @Override
+ public void onUnhold() {
+ wasUnheld = true;
+ }
}
private static final long TIMEOUT_MS = 100;
@@ -115,6 +181,8 @@
private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_2 = new PhoneAccountHandle(
TEST_COMPONENT_NAME, TEST_ACCOUNT_ID2);
private static final Uri TEST_ADDRESS = Uri.parse("tel:+16505551212");
+ private static final String TELECOM_CALL_ID1 = "TC1";
+ private static final String TEST_EMERGENCY_NUMBER = "911";
private android.telecom.Connection mConnection;
@Mock TelephonyConnectionService.TelephonyManagerProxy mTelephonyManagerProxy;
@@ -125,7 +193,7 @@
@Mock TelephonyConnectionService.PhoneNumberUtilsProxy mPhoneNumberUtilsProxy;
@Mock TelephonyConnectionService.PhoneUtilsProxy mPhoneUtilsProxy;
@Mock TelephonyConnectionService.DisconnectCauseFactory mDisconnectCauseFactory;
- @Mock Handler mMockHandler;
+ @Mock SatelliteController mSatelliteController;
@Mock EmergencyNumberTracker mEmergencyNumberTracker;
@Mock PhoneSwitcher mPhoneSwitcher;
@Mock RadioOnHelper mRadioOnHelper;
@@ -134,6 +202,12 @@
@Mock Call mCall2;
@Mock com.android.internal.telephony.Connection mInternalConnection;
@Mock com.android.internal.telephony.Connection mInternalConnection2;
+ @Mock DomainSelectionResolver mDomainSelectionResolver;
+ @Mock EmergencyCallDomainSelectionConnection mEmergencyCallDomainSelectionConnection;
+ @Mock NormalCallDomainSelectionConnection mNormalCallDomainSelectionConnection;
+ @Mock ImsPhone mImsPhone;
+ @Mock private SatelliteSOSMessageRecommender mSatelliteSOSMessageRecommender;
+ @Mock private EmergencyStateTracker mEmergencyStateTracker;
private Phone mPhone0;
private Phone mPhone1;
@@ -159,6 +233,7 @@
@Before
public void setUp() throws Exception {
super.setUp();
+ doReturn(Looper.getMainLooper()).when(mContext).getMainLooper();
mTestConnectionService = new TestTelephonyConnectionService(mContext);
mTestConnectionService.setPhoneFactoryProxy(mPhoneFactoryProxy);
mTestConnectionService.setSubscriptionManagerProxy(mSubscriptionManagerProxy);
@@ -172,13 +247,35 @@
mTestConnectionService.setPhoneUtilsProxy(mPhoneUtilsProxy);
mTestConnectionService.setDeviceState(mDeviceState);
mTestConnectionService.setRadioOnHelper(mRadioOnHelper);
- doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
- .toTelecomDisconnectCause(anyInt(), any());
- doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
- .toTelecomDisconnectCause(anyInt(), any(), anyInt());
+ doAnswer(invocation -> DisconnectCauseUtil.toTelecomDisconnectCause(
+ invocation.getArgument(0), invocation.getArgument(1)))
+ .when(mDisconnectCauseFactory).toTelecomDisconnectCause(anyInt(), any());
+ doAnswer(invocation -> DisconnectCauseUtil.toTelecomDisconnectCause(
+ invocation.getArgument(0), invocation.getArgument(1),
+ (int) invocation.getArgument(2)))
+ .when(mDisconnectCauseFactory).toTelecomDisconnectCause(anyInt(), any(), anyInt());
mTestConnectionService.setDisconnectCauseFactory(mDisconnectCauseFactory);
mTestConnectionService.onCreate();
mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy);
+ replaceInstance(TelephonyConnectionService.class, "mDomainSelectionResolver",
+ mTestConnectionService, mDomainSelectionResolver);
+ replaceInstance(TelephonyConnectionService.class, "mEmergencyStateTracker",
+ mTestConnectionService, mEmergencyStateTracker);
+ replaceInstance(TelephonyConnectionService.class, "mSatelliteSOSMessageRecommender",
+ mTestConnectionService, mSatelliteSOSMessageRecommender);
+ doNothing().when(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), any());
+ doNothing().when(mSatelliteSOSMessageRecommender).onEmergencyCallConnectionStateChanged(
+ anyString(), anyInt());
+ doReturn(CompletableFuture.completedFuture(NOT_DISCONNECTED))
+ .when(mEmergencyStateTracker)
+ .startEmergencyCall(any(), anyString(), eq(false));
+ replaceInstance(TelephonyConnectionService.class,
+ "mDomainSelectionMainExecutor", mTestConnectionService, getExecutor());
+ doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ doReturn(null).when(mDomainSelectionResolver).getDomainSelectionConnection(
+ any(), anyInt(), anyBoolean());
+ replaceInstance(TelephonyConnectionService.class,
+ "mSatelliteController", mTestConnectionService, mSatelliteController);
mBinderStub = (IConnectionService.Stub) mTestConnectionService.onBind(null);
}
@@ -191,6 +288,54 @@
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
+ * - Slot 0 is IN_SERVICE, Slot 1 is OUT_OF_SERVICE (emergency calls only)
+ * - Slot 1 is in Emergency SMS Mode
+ *
+ * Result: getFirstPhoneForEmergencyCall returns the slot 1 phone
+ */
+ @Test
+ @SmallTest
+ public void testEmergencySmsModeSimEmergencyOnly() {
+ Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
+ false /*isEmergencyOnly*/);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ true /*isEmergencyOnly*/);
+ setDefaultPhone(slot0Phone);
+ setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
+ setEmergencySmsMode(slot1Phone, true);
+
+ Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+ assertEquals(slot1Phone, resultPhone);
+ }
+
+ /**
+ * Prerequisites:
+ * - MSIM Device, two slots with SIMs inserted
+ * - Slot 0 is IN_SERVICE, Slot 1 is OUT_OF_SERVICE
+ * - Slot 1 is in Emergency SMS Mode
+ *
+ * Result: getFirstPhoneForEmergencyCall returns the slot 0 phone
+ */
+ @Test
+ @SmallTest
+ public void testEmergencySmsModeSimOutOfService() {
+ Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
+ false /*isEmergencyOnly*/);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ setDefaultPhone(slot0Phone);
+ setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
+ setEmergencySmsMode(slot1Phone, true);
+
+ Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+ assertEquals(slot0Phone, resultPhone);
+ }
+
+ /**
+ * Prerequisites:
+ * - MSIM Device, two slots with SIMs inserted
* - Users default Voice SIM choice is IN_SERVICE
*
* Result: getFirstPhoneForEmergencyCall returns the default Voice SIM choice.
@@ -213,6 +358,52 @@
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
+ * - Users default data SIM choice is OUT_OF_SERVICE (emergency calls only)
+ *
+ * Result: getFirstPhoneForEmergencyCall returns the default data SIM choice.
+ */
+ @Test
+ @SmallTest
+ public void testDefaultDataSimEmergencyOnly() {
+ Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
+ false /*isEmergencyOnly*/);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ true /*isEmergencyOnly*/);
+ setDefaultPhone(slot0Phone);
+ setupDeviceConfig(slot0Phone, slot1Phone, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ setDefaultDataPhoneId(SLOT_1_PHONE_ID);
+
+ Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+ assertEquals(slot1Phone, resultPhone);
+ }
+
+ /**
+ * Prerequisites:
+ * - MSIM Device, two slots with SIMs inserted
+ * - Users default data SIM choice is OUT_OF_SERVICE
+ *
+ * Result: getFirstPhoneForEmergencyCall does not return the default data SIM choice.
+ */
+ @Test
+ @SmallTest
+ public void testDefaultDataSimOutOfService() {
+ Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
+ false /*isEmergencyOnly*/);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ setDefaultPhone(slot0Phone);
+ setupDeviceConfig(slot0Phone, slot1Phone, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ setDefaultDataPhoneId(SLOT_1_PHONE_ID);
+
+ Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+ assertEquals(slot0Phone, resultPhone);
+ }
+
+ /**
+ * Prerequisites:
+ * - MSIM Device, two slots with SIMs inserted
* - Slot 0 is OUT_OF_SERVICE, Slot 1 is OUT_OF_SERVICE (emergency calls only)
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone
@@ -396,9 +587,6 @@
// Slot 1 has more capabilities
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_GSM);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
- // Slot 1 has SIM inserted.
- setSlotHasIccCard(SLOT_0_PHONE_ID, false /*isInserted*/);
- setSlotHasIccCard(SLOT_1_PHONE_ID, true /*isInserted*/);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
@@ -414,7 +602,7 @@
*/
@Test
@SmallTest
- public void testSlot1HigherCapablity() {
+ public void testSlot1HigherCapability() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
@@ -511,9 +699,6 @@
// Make Capability the same
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
- // Two SIMs inserted
- setSlotHasIccCard(SLOT_0_PHONE_ID, true /*isInserted*/);
- setSlotHasIccCard(SLOT_1_PHONE_ID, true /*isInserted*/);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
@@ -542,9 +727,6 @@
// Make Capability the same
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
- // Slot 0 has SIM inserted.
- setSlotHasIccCard(SLOT_0_PHONE_ID, true /*isInserted*/);
- setSlotHasIccCard(SLOT_1_PHONE_ID, false /*isInserted*/);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
@@ -573,9 +755,35 @@
// Make Capability the same
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
- // Slot 1 has SIM inserted.
- setSlotHasIccCard(SLOT_0_PHONE_ID, false /*isInserted*/);
- setSlotHasIccCard(SLOT_1_PHONE_ID, true /*isInserted*/);
+
+ Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+ assertEquals(slot1Phone, resultPhone);
+ }
+
+ /**
+ * Prerequisites:
+ * - MSIM Device with one ESIM, only slot 1 inserted has PSIM inserted
+ * - Both phones have the same capability
+ *
+ * Result: getFirstPhoneForEmergencyCall returns the slot 1 phone because it is the only one
+ * with a SIM inserted
+ */
+ @Test
+ @SmallTest
+ public void testEqualCapabilitySim1Inserted_WithOneEsim() {
+ Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ setDefaultPhone(slot0Phone);
+ setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
+ when(slot0Phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_READY);
+ setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
+ // Make Capability the same
+ setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
+ setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
@@ -604,9 +812,6 @@
// Make Capability the same
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_GSM);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
- // No SIMs inserted
- setSlotHasIccCard(SLOT_0_PHONE_ID, false /*isInserted*/);
- setSlotHasIccCard(SLOT_1_PHONE_ID, false /*isInserted*/);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
@@ -634,9 +839,63 @@
// Make Capability the same
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_UNKNOWN);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_UNKNOWN);
- // No SIMs inserted
- setSlotHasIccCard(SLOT_0_PHONE_ID, false /*isInserted*/);
- setSlotHasIccCard(SLOT_1_PHONE_ID, false /*isInserted*/);
+
+ Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+ assertEquals(slot0Phone, resultPhone);
+ }
+
+ /**
+ * Prerequisites:
+ * - MSIM Device, no SIMs inserted (one ESIM)
+ * - Both SIMs have the same capability (Unknown)
+ *
+ * Result: getFirstPhoneForEmergencyCall returns the slot 0 phone, since it is the first slot.
+ */
+ @Test
+ @SmallTest
+ public void testEqualCapabilityNoSimsInserted_WithOneESim() {
+ Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ setDefaultPhone(slot0Phone);
+ setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
+ setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_ABSENT);
+ when(slot1Phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
+ // Make Capability the samesvim
+ setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_UNKNOWN);
+ setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_UNKNOWN);
+
+ Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+ assertEquals(slot0Phone, resultPhone);
+ }
+
+ /**
+ * Prerequisites:
+ * - MSIM Device, both ESIMS (no profile activated)
+ * - Both phones have the same capability (Unknown)
+ *
+ * Result: getFirstPhoneForEmergencyCall returns the slot 0 phone, since it is the first slot.
+ */
+ @Test
+ @SmallTest
+ public void testEqualCapabilityNoSimsInserted_WithTwoESims() {
+ Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ setDefaultPhone(slot0Phone);
+ setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
+ when(slot0Phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_READY);
+ when(slot1Phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
+ // Make Capability the sames
+ setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_UNKNOWN);
+ setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_UNKNOWN);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
@@ -661,7 +920,8 @@
setPhones(phones);
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
- mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), false /*isPermanentFailure*/);
// We never need to be notified in telecom that the PhoneAccount has changed, because it
// was redialed on the same slot
@@ -692,7 +952,8 @@
setPhones(phones);
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
- mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), true /*isPermanentFailure*/);
// We never need to be notified in telecom that the PhoneAccount has changed, because it
// was never redialed
@@ -733,7 +994,8 @@
doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot1Phone);
- mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), false /*isPermanentFailure*/);
// The cache should still contain all of the Phones, since it was a temporary failure.
assertEquals(2, mTestConnectionService.mEmergencyRetryCache.second.size());
@@ -774,7 +1036,8 @@
doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot1Phone);
- mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), true /*isPermanentFailure*/);
// The cache should only contain the slot1Phone.
assertEquals(1, mTestConnectionService.mEmergencyRetryCache.second.size());
@@ -816,7 +1079,8 @@
slot1Phone);
// First Temporary failure
- mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), false /*isPermanentFailure*/);
// Set the Phone to the new phone that was just used to dial.
c.setMockPhone(slot1Phone);
// The cache should still contain all of the Phones, since it was a temporary failure.
@@ -824,7 +1088,8 @@
// Make sure slot 1 is next in the queue.
assertEquals(slot1Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
// Second Temporary failure
- mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), false /*isPermanentFailure*/);
// Set the Phone to the new phone that was just used to dial.
c.setMockPhone(slot0Phone);
// The cache should still contain all of the Phones, since it was a temporary failure.
@@ -871,7 +1136,8 @@
slot1Phone);
// First Permanent failure
- mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), true /*isPermanentFailure*/);
// Set the Phone to the new phone that was just used to dial.
c.setMockPhone(slot1Phone);
// The cache should only contain one phone
@@ -879,7 +1145,8 @@
// Make sure slot 1 is next in the queue.
assertEquals(slot1Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
// Second Permanent failure
- mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), true /*isPermanentFailure*/);
// The cache should be empty
assertEquals(true, mTestConnectionService.mEmergencyRetryCache.second.isEmpty());
@@ -981,11 +1248,13 @@
ArgumentCaptor<RadioOnStateListener.Callback> callback =
ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
- eq(testPhone), eq(false));
+ eq(testPhone), eq(false), eq(0));
- assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+ assertFalse(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
when(mSST.isRadioOn()).thenReturn(true);
- assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+ assertTrue(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
mConnection.setDisconnected(null);
callback.getValue().onComplete(null, true);
@@ -1007,11 +1276,13 @@
ArgumentCaptor<RadioOnStateListener.Callback> callback =
ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
- eq(testPhone), eq(false));
+ eq(testPhone), eq(false), eq(0));
- assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+ assertFalse(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
when(mSST.isRadioOn()).thenReturn(true);
- assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+ assertTrue(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
callback.getValue().onComplete(null, true);
@@ -1022,6 +1293,43 @@
// This shouldn't happen
fail();
}
+ verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), any());
+ }
+
+ /**
+ * Test that the TelephonyConnectionService successfully turns satellite off before placing the
+ * emergency call.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingEmergencyConnection_exitingSatellite_placeCall() {
+ when(mSatelliteController.isSatelliteEnabled()).thenReturn(true);
+ Phone testPhone = setupConnectionServiceInApm();
+
+ ArgumentCaptor<RadioOnStateListener.Callback> callback =
+ ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+ eq(testPhone), eq(false), eq(0));
+
+ assertFalse(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+ when(mSST.isRadioOn()).thenReturn(true);
+ assertFalse(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+ when(mSatelliteController.isSatelliteEnabled()).thenReturn(false);
+ assertTrue(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+
+ callback.getValue().onComplete(null, true);
+
+ try {
+ doAnswer(invocation -> null).when(mContext).startActivity(any());
+ verify(testPhone).dial(anyString(), any(), any());
+ } catch (CallStateException e) {
+ // This shouldn't happen
+ fail();
+ }
+ verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), any());
}
/**
@@ -1055,7 +1363,7 @@
@Test
@SmallTest
public void testCreateOutgoingEmergencyConnection_delayDial_supportsuplondds() {
- // If the non-DDS supports SUPL, dont switch data
+ // If the non-DDS supports SUPL, don't switch data
doReturn(false).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
@@ -1213,7 +1521,7 @@
}
/**
- * Verifies where there is another call on the same sub, we don't set
+ * Verifies where there is another call on a different sub, we set
* {@link android.telecom.Connection#EXTRA_ANSWERING_DROPS_FG_CALL} on the incoming call extras.
* @throws Exception
*/
@@ -1244,6 +1552,40 @@
.count());
}
+ /**
+ * For virtual DSDA-enabled devices, verifies where there is another call on the same sub, we
+ * don't set {@link android.telecom.Connection#EXTRA_ANSWERING_DROPS_FG_CALL} on the incoming
+ * call extras.
+ * @throws Exception
+ */
+ @Test
+ @SmallTest
+ public void testSecondCallDifferentSubWontDisconnectForDsdaDevice() throws Exception {
+ // Re-uses existing test for setup, then configures device as virtual DSDA for test duration
+ testIncomingDoesntRequestDisconnect();
+ when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+
+ when(mCall.getState()).thenReturn(Call.State.ACTIVE);
+ when(mCall2.getState()).thenReturn(Call.State.WAITING);
+ when(mCall2.getLatestConnection()).thenReturn(mInternalConnection2);
+ // At this point the call is ringing on the second phone.
+ when(mPhone0.getRingingCall()).thenReturn(null);
+ when(mPhone1.getRingingCall()).thenReturn(mCall2);
+
+ mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_2, "TC@2",
+ new ConnectionRequest(PHONE_ACCOUNT_HANDLE_2, Uri.parse("tel:16505551213"),
+ new Bundle()),
+ true, false, null);
+ waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
+ assertEquals(2, mTestConnectionService.getAllConnections().size());
+
+ // None of the connections should have the extra set.
+ assertEquals(0, mTestConnectionService.getAllConnections().stream()
+ .filter(c -> c.getExtras() != null && c.getExtras().containsKey(
+ android.telecom.Connection.EXTRA_ANSWERING_DROPS_FG_CALL))
+ .count());
+ }
+
private static final PhoneAccountHandle SUB1_HANDLE = new PhoneAccountHandle(
new ComponentName("test", "class"), "1");
private static final PhoneAccountHandle SUB2_HANDLE = new PhoneAccountHandle(
@@ -1255,7 +1597,8 @@
ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
tcs.add(tc1);
- TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(tcs, SUB1_HANDLE);
+ TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
+ tcs, SUB1_HANDLE, mTelephonyManagerProxy);
// Would've preferred to use mockito, but can't mock out TelephonyConnection/Connection
// easily.
assertFalse(tc1.wasDisconnected);
@@ -1267,7 +1610,8 @@
ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, true);
tcs.add(tc1);
- TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(tcs, SUB2_HANDLE);
+ TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
+ tcs, SUB2_HANDLE, mTelephonyManagerProxy);
// Other call is an emergency call, so don't disconnect it.
assertFalse(tc1.wasDisconnected);
}
@@ -1279,7 +1623,8 @@
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE,
android.telecom.Connection.PROPERTY_IS_EXTERNAL_CALL, false);
tcs.add(tc1);
- TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(tcs, SUB2_HANDLE);
+ TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
+ tcs, SUB2_HANDLE, mTelephonyManagerProxy);
// Other call is an external call, so don't disconnect it.
assertFalse(tc1.wasDisconnected);
}
@@ -1290,7 +1635,8 @@
ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
tcs.add(tc1);
- TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(tcs, SUB2_HANDLE);
+ TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
+ tcs, SUB2_HANDLE, mTelephonyManagerProxy);
assertTrue(tc1.wasDisconnected);
}
@@ -1303,11 +1649,1315 @@
tcs.add(tc1);
tcs.add(tc2);
- TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(tcs, SUB2_HANDLE);
+ TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
+ tcs, SUB2_HANDLE, mTelephonyManagerProxy);
assertTrue(tc1.wasDisconnected);
assertTrue(tc2.wasDisconnected);
}
+ /**
+ * Verifies that DSDA or virtual DSDA-enabled devices can support active non-emergency calls on
+ * separate subs.
+ */
+ @Test
+ @SmallTest
+ public void testDontDisconnectDifferentSubForVirtualDsdaDevice() {
+ when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+
+ ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+ SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+ tcs.add(tc1);
+ TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
+ tcs, SUB2_HANDLE, mTelephonyManagerProxy);
+ assertFalse(tc1.wasDisconnected);
+ }
+
+
+ /**
+ * For calls on the same sub, the Dialer implements the 'swap' functionality to perform hold and
+ * unhold, so we do not additionally unhold when 'hold' button is pressed.
+ */
+ @Test
+ @SmallTest
+ public void testDontUnholdOnSameSubForVirtualDsdaDevice() {
+ when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+
+ ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+ Collection<Conference> conferences = new ArrayList<>();
+ SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+ tcs.add(tc1);
+ TelephonyConnectionService.maybeUnholdCallsOnOtherSubs(
+ tcs, conferences, SUB1_HANDLE, mTelephonyManagerProxy);
+ assertFalse(tc1.wasUnheld);
+ }
+
+ /**
+ * Triggering 'Hold' on 1 call will unhold the other call for DSDA or Virtual DSDA
+ * enabled devices, effectively constituting 'swap' functionality.
+ */
+ @Test
+ @SmallTest
+ public void testUnholdOnOtherSubForVirtualDsdaDevice() {
+ when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+
+ ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+ SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+ tcs.add(tc1);
+ TelephonyConnectionService.maybeUnholdCallsOnOtherSubs(
+ tcs, new ArrayList<>(), SUB2_HANDLE, mTelephonyManagerProxy);
+ assertTrue(tc1.wasUnheld);
+ }
+
+ /**
+ * Verifies hold/unhold behavior for a conference on the other sub. It does not disturb the
+ * individual connections that participate in the conference.
+ */
+ @Test
+ @SmallTest
+ public void testUnholdConferenceOnOtherSubForVirtualDsdaDevice() {
+ when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+ SimpleTelephonyConnection tc1 =
+ createTestConnection(SUB1_HANDLE, 0, false);
+ SimpleTelephonyConnection tc2 =
+ createTestConnection(SUB1_HANDLE, 0, false);
+ List<android.telecom.Connection> conferenceParticipants = Arrays.asList(tc1, tc2);
+
+ SimpleConference testConference = createTestConference(SUB1_HANDLE, 0);
+ List<Conference> conferences = Arrays.asList(testConference);
+
+ TelephonyConnectionService.maybeUnholdCallsOnOtherSubs(
+ conferenceParticipants, conferences, SUB2_HANDLE, mTelephonyManagerProxy);
+
+ assertTrue(testConference.wasUnheld);
+ assertFalse(tc1.wasUnheld);
+ assertFalse(tc2.wasUnheld);
+ }
+
+ /**
+ * For DSDA devices, placing an outgoing call on a 2nd sub will hold the existing connection on
+ * the first sub.
+ */
+ @Test
+ @SmallTest
+ public void testHoldOnOtherSubForVirtualDsdaDevice() {
+ when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+
+ ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+ SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+ tcs.add(tc1);
+ Conferenceable c = TelephonyConnectionService.maybeHoldCallsOnOtherSubs(
+ tcs, new ArrayList<>(), SUB2_HANDLE, mTelephonyManagerProxy);
+ assertTrue(c.equals(tc1));
+ assertTrue(tc1.wasHeld);
+ }
+
+ // For 'Virtual DSDA' devices, if there is an existing call on sub1, an outgoing call on sub2
+ // will place the sub1 call on hold.
+ @Test
+ @SmallTest
+ public void testOutgoingCallOnOtherSubPutsFirstCallOnHoldForVirtualDsdaDevice()
+ throws Exception {
+ setupForCallTest();
+ when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+ doNothing().when(mContext).startActivity(any());
+
+ mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_1, "TC@1",
+ new ConnectionRequest(PHONE_ACCOUNT_HANDLE_1, Uri.parse("tel:16505551212"),
+ new Bundle()),
+ true, false, null);
+ waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
+ assertEquals(1, mTestConnectionService.getAllConnections().size());
+
+ TelephonyConnection connection1 = (TelephonyConnection)
+ mTestConnectionService.getAllConnections().toArray()[0];
+
+ TelephonyConnection connection2 = (TelephonyConnection) mTestConnectionService
+ .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_2,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_2, "1234", "TC@2"));
+ assertNotNull("test connection was not set up correctly.", connection2);
+
+ // Simulates that connection1 is placed on HOLD.
+ connection1.setTelephonyConnectionOnHold();
+
+ verify(mPhone1).dial(anyString(), any(), any());
+ assertEquals(connection1.getState(), android.telecom.Connection.STATE_HOLDING);
+ }
+
+ // For 'Virtual DSDA' devices, if the carrier config 'KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL'
+ // is not configured, or set to true, an outgoing emergency call will place the existing call on
+ // a different sub on hold.
+ @Test
+ @SmallTest
+ public void testEmergencyCallOnOtherSubPutsFirstCallOnHoldForVirtualDsdaDevice()
+ throws Exception {
+ setupForCallTest();
+ when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+ doNothing().when(mContext).startActivity(any());
+
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+ mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_1, "TC@1",
+ new ConnectionRequest(PHONE_ACCOUNT_HANDLE_1, Uri.parse("tel:16505551212"),
+ new Bundle()),
+ true, false, null);
+ waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
+ assertEquals(1, mTestConnectionService.getAllConnections().size());
+
+ TelephonyConnection connection1 = (TelephonyConnection)
+ mTestConnectionService.getAllConnections().toArray()[0];
+
+ // Simulates an outgoing emergency call.
+ TelephonyConnection connection2 = (TelephonyConnection) mTestConnectionService
+ .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_2,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_2,
+ TEST_EMERGENCY_NUMBER, "TC@2"));
+ assertNotNull("test connection was not set up correctly.", connection2);
+
+ // Simulates that connection1 is placed on HOLD.
+ connection1.setTelephonyConnectionOnHold();
+
+ verify(mPhone1).dial(anyString(), any(), any());
+ assertEquals(connection1.getState(), android.telecom.Connection.STATE_HOLDING);
+ }
+
+ // For 'Virtual DSDA' devices If the carrier config 'KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL'
+ // is explicitly configured false, an outgoing emergency call will disconnect all existing
+ // calls, across subscriptions.
+ @Test
+ @SmallTest
+ public void testEmergencyCallOnOtherSubDisconnectsExistingCallForVirtualDsdaDevice()
+ throws Exception {
+ setupForCallTest();
+ when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+ doNothing().when(mContext).startActivity(any());
+
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+ getTestContext().getCarrierConfig(0 /*subId*/).putBoolean(
+ CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, false);
+
+ mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_1, "TC@1",
+ new ConnectionRequest(PHONE_ACCOUNT_HANDLE_1, Uri.parse("tel:16505551212"),
+ new Bundle()),
+ true, false, null);
+ waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
+ assertEquals(1, mTestConnectionService.getAllConnections().size());
+
+ TelephonyConnection connection1 = (TelephonyConnection)
+ mTestConnectionService.getAllConnections().toArray()[0];
+
+ // Simulates an outgoing emergency call.
+ TelephonyConnection connection2 = (TelephonyConnection) mTestConnectionService
+ .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_2,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_2,
+ TEST_EMERGENCY_NUMBER, "TC@2"));
+ assertNotNull("test connection was not set up correctly.", connection2);
+
+ verify(mPhone1).dial(anyString(), any(), any());
+ assertEquals(connection1.getState(), android.telecom.Connection.STATE_DISCONNECTED);
+ }
+
+ /**
+ * Verifies that TelephonyManager is used to determine whether a connection is Emergency when
+ * creating an outgoing connection.
+ */
+ @Test
+ @SmallTest
+ public void testIsEmergencyDeterminedByTelephonyManager() {
+ ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+ .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+ .setAddress(TEST_ADDRESS)
+ .build();
+ mConnection = mTestConnectionService.onCreateOutgoingConnection(
+ PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+
+ verify(mTelephonyManagerProxy)
+ .isCurrentEmergencyNumber(TEST_ADDRESS.getSchemeSpecificPart());
+ }
+
+ @Test
+ public void testDomainSelectionCs() throws Exception {
+ setupForCallTest();
+
+ int selectedDomain = DOMAIN_CS;
+
+ setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ verify(mDomainSelectionResolver)
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
+ verify(mEmergencyStateTracker)
+ .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+ verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
+
+ ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
+
+ verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+ DialArgs dialArgs = argsCaptor.getValue();
+ assertNotNull("DialArgs param is null", dialArgs);
+ assertNotNull("intentExtras is null", dialArgs.intentExtras);
+ assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+ assertEquals(selectedDomain,
+ dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+ }
+
+ @Test
+ public void testDomainSelectionPs() throws Exception {
+ setupForCallTest();
+
+ int selectedDomain = DOMAIN_PS;
+
+ setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ verify(mDomainSelectionResolver)
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
+ verify(mEmergencyStateTracker)
+ .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+ verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
+
+ ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
+
+ verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+ DialArgs dialArgs = argsCaptor.getValue();
+ assertNotNull("DialArgs param is null", dialArgs);
+ assertNotNull("intentExtras is null", dialArgs.intentExtras);
+ assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+ assertEquals(selectedDomain,
+ dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+ }
+
+ @Test
+ public void testDomainSelectionCsForTty() throws Exception {
+ setupForCallTest();
+
+ ImsManager imsManager = Mockito.mock(ImsManager.class);
+ doReturn(false).when(imsManager).isNonTtyOrTtyOnVolteEnabled();
+ replaceInstance(TelephonyConnectionService.class,
+ "mImsManager", mTestConnectionService, imsManager);
+
+ setupForDialForDomainSelection(mPhone0, DOMAIN_PS, true);
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ verify(mEmergencyStateTracker, times(1))
+ .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+ verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
+ verify(mDomainSelectionResolver, times(0))
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
+ verify(mEmergencyCallDomainSelectionConnection, times(0))
+ .createEmergencyConnection(any(), any());
+
+ ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
+
+ verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+ DialArgs dialArgs = argsCaptor.getValue();
+ assertNotNull("DialArgs param is null", dialArgs);
+ assertNotNull("intentExtras is null", dialArgs.intentExtras);
+ assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+ assertEquals(DOMAIN_CS, dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+ }
+
+ @Test
+ public void testDomainSelectionRedialCs() throws Exception {
+ setupForCallTest();
+
+ int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
+ int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+ int selectedDomain = DOMAIN_CS;
+
+ TestTelephonyConnection c = setupForReDialForDomainSelection(
+ mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
+
+ assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
+ verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
+
+ ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
+
+ Connection nc = Mockito.mock(Connection.class);
+ doReturn(nc).when(mPhone0).dial(anyString(), any(), any());
+
+ verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+ DialArgs dialArgs = argsCaptor.getValue();
+ assertNotNull("DialArgs param is null", dialArgs);
+ assertNotNull("intentExtras is null", dialArgs.intentExtras);
+ assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+ assertEquals(selectedDomain,
+ dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+ }
+
+ @Test
+ public void testDomainSelectionRedialPs() throws Exception {
+ setupForCallTest();
+
+ int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
+ int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+ int selectedDomain = DOMAIN_PS;
+
+ TestTelephonyConnection c = setupForReDialForDomainSelection(
+ mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
+
+ assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
+ verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
+
+ ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
+
+ Connection nc = Mockito.mock(Connection.class);
+ doReturn(nc).when(mPhone0).dial(anyString(), any(), any());
+
+ verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+ DialArgs dialArgs = argsCaptor.getValue();
+ assertNotNull("DialArgs param is null", dialArgs);
+ assertNotNull("intentExtras is null", dialArgs.intentExtras);
+ assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+ assertEquals(selectedDomain,
+ dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+ }
+
+ @Test
+ public void testDomainSelectionNormalRoutingEmergencyNumber() throws Exception {
+ setupForCallTest();
+ int selectedDomain = DOMAIN_PS;
+
+ EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ Collections.emptyList(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ setupForDialForDomainSelection(mPhone0, selectedDomain, false);
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+ doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ verify(mDomainSelectionResolver)
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
+ verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
+ verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
+
+ ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
+
+ verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+ DialArgs dialArgs = argsCaptor.getValue();
+ assertNotNull("DialArgs param is null", dialArgs);
+ assertNotNull("intentExtras is null", dialArgs.intentExtras);
+ assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+ assertEquals(
+ selectedDomain, dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+ }
+
+ @Test
+ public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_InService()
+ throws Exception {
+ setupForCallTest();
+
+ doReturn(false).when(mPhone0).isRadioOn();
+ ServiceState ss = new ServiceState();
+ ss.setState(ServiceState.STATE_POWER_OFF);
+ when(mPhone0.getServiceState()).thenReturn(ss);
+ when(mSST.getServiceState()).thenReturn(ss);
+
+ setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
+
+ EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ Collections.emptyList(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+ doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+
+ when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ ArgumentCaptor<RadioOnStateListener.Callback> callback =
+ ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+ any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+
+ assertFalse(callback.getValue()
+ .isOkToCall(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+
+ when(mSST.isRadioOn()).thenReturn(true);
+
+ assertFalse(callback.getValue()
+ .isOkToCall(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+
+ ss.setState(ServiceState.STATE_IN_SERVICE);
+
+ assertTrue(callback.getValue()
+ .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+ }
+
+ @Test
+ public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_Timeout()
+ throws Exception {
+ setupForCallTest();
+
+ doReturn(false).when(mPhone0).isRadioOn();
+ ServiceState ss = new ServiceState();
+ ss.setState(ServiceState.STATE_POWER_OFF);
+ when(mPhone0.getServiceState()).thenReturn(ss);
+ when(mSST.getServiceState()).thenReturn(ss);
+
+ setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
+
+ EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ Collections.emptyList(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+ doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+
+ when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ ArgumentCaptor<RadioOnStateListener.Callback> callback =
+ ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+ any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+
+ when(mSST.isRadioOn()).thenReturn(true);
+
+ assertFalse(callback.getValue()
+ .isOkToCall(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+ assertTrue(callback.getValue()
+ .onTimeout(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+ }
+
+ @Test
+ public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_CombinedAttach()
+ throws Exception {
+ setupForCallTest();
+
+ doReturn(false).when(mPhone0).isRadioOn();
+ ServiceState ss = new ServiceState();
+ ss.setState(ServiceState.STATE_POWER_OFF);
+ when(mPhone0.getServiceState()).thenReturn(ss);
+ when(mSST.getServiceState()).thenReturn(ss);
+
+ setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
+
+ EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ Collections.emptyList(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+ doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+
+ when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ ArgumentCaptor<RadioOnStateListener.Callback> callback =
+ ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+ any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+
+ when(mSST.isRadioOn()).thenReturn(true);
+ ss.setState(ServiceState.STATE_IN_SERVICE);
+
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(3)
+ .setLteAttachResultType(DataSpecificRegistrationInfo.LTE_ATTACH_TYPE_COMBINED)
+ .build();
+
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setDataSpecificInfo(dsri)
+ .build();
+ ss.addNetworkRegistrationInfo(nri);
+
+ assertTrue(callback.getValue()
+ .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+ }
+
+ @Test
+ public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_PsOnly()
+ throws Exception {
+ setupForCallTest();
+
+ doReturn(false).when(mPhone0).isRadioOn();
+ ServiceState ss = new ServiceState();
+ ss.setState(ServiceState.STATE_POWER_OFF);
+ when(mPhone0.getServiceState()).thenReturn(ss);
+ when(mSST.getServiceState()).thenReturn(ss);
+
+ setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
+
+ EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ Collections.emptyList(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+ doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+
+ when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ ArgumentCaptor<RadioOnStateListener.Callback> callback =
+ ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+ any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+
+ when(mSST.isRadioOn()).thenReturn(true);
+ ss.setState(ServiceState.STATE_IN_SERVICE);
+
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(3)
+ .build();
+
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setDataSpecificInfo(dsri)
+ .build();
+ ss.addNetworkRegistrationInfo(nri);
+
+ assertFalse(callback.getValue()
+ .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+ assertTrue(callback.getValue()
+ .onTimeout(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+ }
+
+ @Test
+ public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_PsOnly_ImsRegistered()
+ throws Exception {
+ setupForCallTest();
+
+ doReturn(false).when(mPhone0).isRadioOn();
+ ServiceState ss = new ServiceState();
+ ss.setState(ServiceState.STATE_POWER_OFF);
+ when(mPhone0.getServiceState()).thenReturn(ss);
+ when(mSST.getServiceState()).thenReturn(ss);
+
+ setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
+
+ EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ Collections.emptyList(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+ doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+
+ when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ ArgumentCaptor<RadioOnStateListener.Callback> callback =
+ ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+ any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+
+ when(mSST.isRadioOn()).thenReturn(true);
+ ss.setState(ServiceState.STATE_IN_SERVICE);
+
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(3)
+ .build();
+
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setDataSpecificInfo(dsri)
+ .build();
+ ss.addNetworkRegistrationInfo(nri);
+
+ assertFalse(callback.getValue()
+ .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+ assertTrue(callback.getValue()
+ .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, true));
+ }
+
+ @Test
+ public void testDomainSelectionNormalToEmergencyCs() throws Exception {
+ setupForCallTest();
+
+ int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
+ int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+ int eccCategory = EMERGENCY_SERVICE_CATEGORY_POLICE;
+ int selectedDomain = DOMAIN_CS;
+
+ setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+ doReturn(mPhone0).when(mImsPhone).getDefaultPhone();
+
+ TestTelephonyConnection c = setupForReDialForDomainSelection(
+ mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
+ c.setEmergencyServiceCategory(eccCategory);
+ c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
+
+ ImsReasonInfo reasonInfo = new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null);
+ assertTrue(mTestConnectionService.maybeReselectDomain(c,
+ preciseDisconnectCause, reasonInfo));
+
+ verify(mDomainSelectionResolver)
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
+ verify(mEmergencyStateTracker)
+ .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+ verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
+
+ ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
+
+ verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+ DialArgs dialArgs = argsCaptor.getValue();
+ assertNotNull("DialArgs param is null", dialArgs);
+ assertNotNull("intentExtras is null", dialArgs.intentExtras);
+ assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+ assertEquals(selectedDomain,
+ dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+ assertTrue(dialArgs.isEmergency);
+ assertEquals(eccCategory, dialArgs.eccCategory);
+ }
+
+ @Test
+ public void testDomainSelectionNormalToEmergencyPs() throws Exception {
+ setupForCallTest();
+
+ int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
+ int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+ int eccCategory = EMERGENCY_SERVICE_CATEGORY_POLICE;
+ int selectedDomain = DOMAIN_PS;
+
+ setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+ doReturn(mPhone0).when(mImsPhone).getDefaultPhone();
+
+ TestTelephonyConnection c = setupForReDialForDomainSelection(
+ mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
+ c.setEmergencyServiceCategory(eccCategory);
+ c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
+
+ ImsReasonInfo reasonInfo = new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null);
+ assertTrue(mTestConnectionService.maybeReselectDomain(c,
+ preciseDisconnectCause, reasonInfo));
+
+ verify(mDomainSelectionResolver)
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
+ verify(mEmergencyStateTracker)
+ .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+ verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
+
+ ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
+
+ verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+ DialArgs dialArgs = argsCaptor.getValue();
+ assertNotNull("DialArgs param is null", dialArgs);
+ assertNotNull("intentExtras is null", dialArgs.intentExtras);
+ assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+ assertEquals(selectedDomain,
+ dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+ assertTrue(dialArgs.isEmergency);
+ assertEquals(eccCategory, dialArgs.eccCategory);
+ }
+
+ @Test
+ public void testOnSelectionTerminatedPerm() throws Exception {
+ setupForCallTest();
+
+ doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
+ .getDomainSelectionConnection(any(), anyInt(), eq(true));
+ doReturn(mPhone0).when(mEmergencyCallDomainSelectionConnection).getPhone();
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ doReturn(mImsPhone).when(mPhone0).getImsPhone();
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(
+ any(), callbackCaptor.capture());
+
+ DomainSelectionConnection.DomainSelectionConnectionCallback callback =
+ callbackCaptor.getValue();
+
+ assertNotNull(callback);
+
+ EmergencyCallDomainSelectionConnection ecdsc =
+ Mockito.mock(EmergencyCallDomainSelectionConnection.class);
+ doReturn(ecdsc).when(mDomainSelectionResolver)
+ .getDomainSelectionConnection(any(), anyInt(), eq(true));
+
+ callback.onSelectionTerminated(EMERGENCY_PERM_FAILURE);
+
+ ArgumentCaptor<DomainSelectionService.SelectionAttributes> attrCaptor =
+ ArgumentCaptor.forClass(
+ DomainSelectionService.SelectionAttributes.class);
+
+ verify(ecdsc).createEmergencyConnection(attrCaptor.capture(), any());
+
+ DomainSelectionService.SelectionAttributes attr = attrCaptor.getValue();
+
+ assertEquals(mPhone1.getPhoneId(), attr.getSlotId());
+ }
+
+ @Test
+ public void testOnSelectionTerminatedTemp() throws Exception {
+ setupForCallTest();
+
+ doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
+ .getDomainSelectionConnection(any(), anyInt(), eq(true));
+ doReturn(mPhone0).when(mEmergencyCallDomainSelectionConnection).getPhone();
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ doReturn(mImsPhone).when(mPhone0).getImsPhone();
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(
+ any(), callbackCaptor.capture());
+
+ DomainSelectionConnection.DomainSelectionConnectionCallback callback =
+ callbackCaptor.getValue();
+
+ assertNotNull(callback);
+
+ EmergencyCallDomainSelectionConnection ecdsc =
+ Mockito.mock(EmergencyCallDomainSelectionConnection.class);
+ doReturn(ecdsc).when(mDomainSelectionResolver)
+ .getDomainSelectionConnection(any(), anyInt(), eq(true));
+
+ callback.onSelectionTerminated(EMERGENCY_TEMP_FAILURE);
+
+ ArgumentCaptor<DomainSelectionService.SelectionAttributes> attrCaptor =
+ ArgumentCaptor.forClass(
+ DomainSelectionService.SelectionAttributes.class);
+
+ verify(ecdsc).createEmergencyConnection(attrCaptor.capture(), any());
+
+ DomainSelectionService.SelectionAttributes attr = attrCaptor.getValue();
+
+ assertEquals(mPhone1.getPhoneId(), attr.getSlotId());
+ }
+
+ @Test
+ public void testOnSelectionTerminatedUnspecified() throws Exception {
+ setupForCallTest();
+
+ doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
+ .getDomainSelectionConnection(any(), anyInt(), eq(true));
+ doReturn(mPhone0).when(mEmergencyCallDomainSelectionConnection).getPhone();
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ doReturn(mImsPhone).when(mPhone0).getImsPhone();
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(
+ any(), callbackCaptor.capture());
+
+ DomainSelectionConnection.DomainSelectionConnectionCallback callback =
+ callbackCaptor.getValue();
+
+ assertNotNull(callback);
+
+ callback.onSelectionTerminated(ERROR_UNSPECIFIED);
+
+ verify(mEmergencyCallDomainSelectionConnection).cancelSelection();
+ verify(mEmergencyStateTracker).endCall(eq(TELECOM_CALL_ID1));
+ }
+
+ @Test
+ public void testDomainSelectionLocalHangupStartEmergencyCall() throws Exception {
+ setupForCallTest();
+
+ int selectedDomain = DOMAIN_CS;
+
+ setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+
+ CompletableFuture<Integer> future = new CompletableFuture<>();
+ doReturn(future).when(mEmergencyStateTracker)
+ .startEmergencyCall(any(), anyString(), eq(false));
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ verify(mEmergencyStateTracker)
+ .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+
+ TelephonyConnection c = new TestTelephonyConnection();
+ c.setTelecomCallId(TELECOM_CALL_ID1);
+
+ // dialing is canceled
+ mTestConnectionService.onLocalHangup(c);
+
+ // startEmergencyCall has completed
+ future.complete(NOT_DISCONNECTED);
+
+ // verify that createEmergencyConnection is discarded
+ verify(mEmergencyCallDomainSelectionConnection, times(0))
+ .createEmergencyConnection(any(), any());
+ }
+
+ @Test
+ public void testDomainSelectionLocalHangupCreateEmergencyConnection() throws Exception {
+ setupForCallTest();
+
+ int selectedDomain = DOMAIN_CS;
+
+ setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+
+ CompletableFuture<Integer> future = new CompletableFuture<>();
+ doReturn(future).when(mEmergencyCallDomainSelectionConnection)
+ .createEmergencyConnection(any(), any());
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
+
+ TelephonyConnection c = new TestTelephonyConnection();
+ c.setTelecomCallId(TELECOM_CALL_ID1);
+
+ // dialing is canceled
+ mTestConnectionService.onLocalHangup(c);
+
+ // domain selection has completed
+ future.complete(selectedDomain);
+
+ // verify that dialing is discarded
+ verify(mPhone0, times(0)).dial(anyString(), any(), any());
+ }
+
+ @Test
+ public void testDomainSelectionRedialLocalHangupReselectDomain() throws Exception {
+ setupForCallTest();
+
+ int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
+ int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+ int selectedDomain = DOMAIN_CS;
+
+ TestTelephonyConnection c = setupForReDialForDomainSelection(
+ mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
+ c.setTelecomCallId(TELECOM_CALL_ID1);
+
+ CompletableFuture<Integer> future = new CompletableFuture<>();
+ doReturn(future).when(mEmergencyCallDomainSelectionConnection)
+ .reselectDomain(any());
+
+ assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
+ verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
+
+ // dialing is canceled
+ mTestConnectionService.onLocalHangup(c);
+
+ // domain selection has completed
+ future.complete(selectedDomain);
+
+ // verify that dialing is discarded
+ verify(mPhone0, times(0)).dial(anyString(), any(), any());
+ }
+
+ @Test
+ public void testDomainSelectionNormalToEmergencyLocalHangupStartEmergencyCall()
+ throws Exception {
+ setupForCallTest();
+
+ int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
+ int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+ int eccCategory = EMERGENCY_SERVICE_CATEGORY_POLICE;
+ int selectedDomain = DOMAIN_CS;
+
+ setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+ doReturn(mPhone0).when(mImsPhone).getDefaultPhone();
+
+ TestTelephonyConnection c = setupForReDialForDomainSelection(
+ mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
+ c.setEmergencyServiceCategory(eccCategory);
+ c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
+ c.setTelecomCallId(TELECOM_CALL_ID1);
+
+ CompletableFuture<Integer> future = new CompletableFuture<>();
+ doReturn(future).when(mEmergencyStateTracker)
+ .startEmergencyCall(any(), anyString(), eq(false));
+
+ ImsReasonInfo reasonInfo = new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null);
+ assertTrue(mTestConnectionService.maybeReselectDomain(c,
+ preciseDisconnectCause, reasonInfo));
+
+ verify(mEmergencyStateTracker)
+ .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+ verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
+
+ // dialing is canceled
+ mTestConnectionService.onLocalHangup(c);
+
+ // startEmergencyCall has completed
+ future.complete(NOT_DISCONNECTED);
+
+ // verify that createEmergencyConnection is discarded
+ verify(mEmergencyCallDomainSelectionConnection, times(0))
+ .createEmergencyConnection(any(), any());
+ }
+
+ @Test
+ public void testDomainSelectionNormalToEmergencyLocalHangupCreateEmergencyConnection()
+ throws Exception {
+ setupForCallTest();
+
+ int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
+ int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+ int eccCategory = EMERGENCY_SERVICE_CATEGORY_POLICE;
+ int selectedDomain = DOMAIN_CS;
+
+ setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+ doReturn(mPhone0).when(mImsPhone).getDefaultPhone();
+
+ TestTelephonyConnection c = setupForReDialForDomainSelection(
+ mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
+ c.setEmergencyServiceCategory(eccCategory);
+ c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
+ c.setTelecomCallId(TELECOM_CALL_ID1);
+
+ CompletableFuture<Integer> future = new CompletableFuture<>();
+ doReturn(future).when(mEmergencyCallDomainSelectionConnection)
+ .createEmergencyConnection(any(), any());
+
+ ImsReasonInfo reasonInfo = new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null);
+ assertTrue(mTestConnectionService.maybeReselectDomain(c,
+ preciseDisconnectCause, reasonInfo));
+
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
+
+ // dialing is canceled
+ mTestConnectionService.onLocalHangup(c);
+
+ // domain selection has completed
+ future.complete(selectedDomain);
+
+ // verify that dialing is discarded
+ verify(mPhone0, times(0)).dial(anyString(), any(), any());
+ }
+
+ @Test
+ public void testDomainSelectionListenOriginalConnectionConfigChange() throws Exception {
+ setupForCallTest();
+
+ int selectedDomain = DOMAIN_PS;
+
+ setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ verify(mDomainSelectionResolver)
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
+ verify(mEmergencyStateTracker)
+ .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+ verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
+ verify(mPhone0).dial(anyString(), any(), any());
+
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ c.setTelecomCallId(TELECOM_CALL_ID1);
+ c.setIsImsConnection(true);
+ Connection orgConn = c.getOriginalConnection();
+ doReturn(PhoneConstants.PHONE_TYPE_IMS).when(orgConn).getPhoneType();
+
+ TelephonyConnection.TelephonyConnectionListener connectionListener =
+ mTestConnectionService.getEmergencyConnectionListener();
+ TelephonyConnection.TelephonyConnectionListener connectionSatelliteListener =
+ mTestConnectionService.getEmergencyConnectionSatelliteListener();
+
+ connectionListener.onOriginalConnectionConfigured(c);
+
+ verify(mEmergencyStateTracker, times(1)).onEmergencyCallDomainUpdated(
+ eq(PhoneConstants.PHONE_TYPE_IMS), eq(TELECOM_CALL_ID1));
+
+ verify(mEmergencyStateTracker, times(0)).onEmergencyCallStateChanged(
+ any(), eq(TELECOM_CALL_ID1));
+ verify(mSatelliteSOSMessageRecommender, times(0))
+ .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
+
+ c.setActive();
+ doReturn(Call.State.ACTIVE).when(orgConn).getState();
+ connectionListener.onStateChanged(c, c.getState());
+ connectionSatelliteListener.onStateChanged(c, c.getState());
+
+ // ACTIVE sate is notified
+ verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
+ eq(Call.State.ACTIVE), eq(TELECOM_CALL_ID1));
+ verify(mSatelliteSOSMessageRecommender, times(1))
+ .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1),
+ eq(android.telecom.Connection.STATE_ACTIVE));
+
+ // state change to HOLDING
+ c.setOnHold();
+ doReturn(Call.State.HOLDING).when(orgConn).getState();
+ connectionListener.onStateChanged(c, c.getState());
+ connectionSatelliteListener.onStateChanged(c, c.getState());
+
+ // state change not notified any more after CONNECTED once
+ verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
+ any(), eq(TELECOM_CALL_ID1));
+ verify(mSatelliteSOSMessageRecommender, times(1))
+ .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
+
+ // state change to ACTIVE again
+ c.setActive();
+ doReturn(Call.State.ACTIVE).when(orgConn).getState();
+ connectionListener.onStateChanged(c, c.getState());
+ connectionSatelliteListener.onStateChanged(c, c.getState());
+
+ // state change not notified any more after CONNECTED once
+ verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
+ any(), eq(TELECOM_CALL_ID1));
+ verify(mSatelliteSOSMessageRecommender, times(1))
+ .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
+
+ // SRVCC happens
+ c.setIsImsConnection(false);
+ orgConn = c.getOriginalConnection();
+ doReturn(PhoneConstants.PHONE_TYPE_GSM).when(orgConn).getPhoneType();
+ connectionListener.onOriginalConnectionConfigured(c);
+
+ // domain change notified
+ verify(mEmergencyStateTracker, times(1)).onEmergencyCallDomainUpdated(
+ eq(PhoneConstants.PHONE_TYPE_GSM), eq(TELECOM_CALL_ID1));
+
+ // state change to DISCONNECTED
+ c.setDisconnected(null);
+ doReturn(Call.State.DISCONNECTED).when(orgConn).getState();
+ connectionListener.onStateChanged(c, c.getState());
+ connectionSatelliteListener.onStateChanged(c, c.getState());
+
+ // state change not notified
+ verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
+ any(), eq(TELECOM_CALL_ID1));
+ verify(mSatelliteSOSMessageRecommender, times(1))
+ .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
+ }
+
+ @Test
+ public void testDomainSelectionTempFailure() throws Exception {
+ setupForCallTest();
+
+ int preciseDisconnectCause =
+ com.android.internal.telephony.CallFailCause.EMERGENCY_TEMP_FAILURE;
+ int disconnectCause = android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE;
+ int selectedDomain = DOMAIN_CS;
+
+ TestTelephonyConnection c = setupForReDialForDomainSelection(
+ mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
+
+ doReturn(new CompletableFuture()).when(mEmergencyCallDomainSelectionConnection)
+ .reselectDomain(any());
+
+ assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
+ verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
+ }
+
+ @Test
+ public void testDomainSelectionPermFailure() throws Exception {
+ setupForCallTest();
+
+ int preciseDisconnectCause =
+ com.android.internal.telephony.CallFailCause.EMERGENCY_PERM_FAILURE;
+ int disconnectCause = android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
+ int selectedDomain = DOMAIN_CS;
+
+ TestTelephonyConnection c = setupForReDialForDomainSelection(
+ mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
+
+ doReturn(new CompletableFuture()).when(mEmergencyCallDomainSelectionConnection)
+ .reselectDomain(any());
+
+ assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
+ verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
+ }
+
+ @Test
+ public void testDomainSelectionWithMmiCode() {
+ //UT domain selection should not be handled by new domain selector.
+ doNothing().when(mContext).startActivity(any());
+ setupForCallTest();
+ setupForDialForDomainSelection(mPhone0, 0, false);
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "*%2321%23", TELECOM_CALL_ID1));
+
+ verifyZeroInteractions(mNormalCallDomainSelectionConnection);
+ }
+
+ @Test
+ public void testNormalCallPsDomainSelection() throws Exception {
+ setupForCallTest();
+ int selectedDomain = DOMAIN_PS;
+ setupForDialForDomainSelection(mPhone0, selectedDomain, false);
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "1234", TELECOM_CALL_ID1));
+
+ verify(mDomainSelectionResolver)
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
+ verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
+ verify(mSatelliteSOSMessageRecommender, never()).onEmergencyCallStarted(any(), any());
+
+ ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
+
+ verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+ DialArgs dialArgs = argsCaptor.getValue();
+ assertNotNull("DialArgs param is null", dialArgs);
+ assertNotNull("intentExtras is null", dialArgs.intentExtras);
+ assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+ assertEquals(
+ selectedDomain, dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+ }
+
+ @Test
+ public void testNormalCallCsDomainSelection() throws Exception {
+ setupForCallTest();
+ int selectedDomain = DOMAIN_CS;
+ setupForDialForDomainSelection(mPhone0, selectedDomain, false);
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "1234", TELECOM_CALL_ID1));
+
+ verify(mDomainSelectionResolver)
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
+ verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
+ verify(mSatelliteSOSMessageRecommender, never()).onEmergencyCallStarted(any(), any());
+
+ ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
+
+ verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+ DialArgs dialArgs = argsCaptor.getValue();
+ assertNotNull("DialArgs param is null", dialArgs);
+ assertNotNull("intentExtras is null", dialArgs.intentExtras);
+ assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+ assertEquals(
+ selectedDomain, dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+ }
+
+ @Test
+ public void testNormalCallSatelliteEnabled() {
+ setupForCallTest();
+ doReturn(true).when(mSatelliteController).isSatelliteEnabled();
+ mConnection = mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "1234", TELECOM_CALL_ID1));
+ DisconnectCause disconnectCause = mConnection.getDisconnectCause();
+ assertEquals(android.telephony.DisconnectCause.SATELLITE_ENABLED,
+ disconnectCause.getTelephonyDisconnectCause());
+ }
+
+ private void setupForDialForDomainSelection(Phone mockPhone, int domain, boolean isEmergency) {
+ if (isEmergency) {
+ doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
+ .getDomainSelectionConnection(any(), anyInt(), eq(true));
+ doReturn(CompletableFuture.completedFuture(domain))
+ .when(mEmergencyCallDomainSelectionConnection)
+ .createEmergencyConnection(any(), any());
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+ } else {
+ doReturn(mNormalCallDomainSelectionConnection).when(mDomainSelectionResolver)
+ .getDomainSelectionConnection(any(), eq(SELECTOR_TYPE_CALLING), eq(false));
+ doReturn(CompletableFuture.completedFuture(domain))
+ .when(mNormalCallDomainSelectionConnection)
+ .createNormalConnection(any(), any());
+ doReturn(false).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+ }
+
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ doReturn(mImsPhone).when(mockPhone).getImsPhone();
+ }
+
+ private TestTelephonyConnection setupForReDialForDomainSelection(
+ Phone mockPhone, int domain, int preciseDisconnectCause,
+ int disconnectCause, boolean fromEmergency) throws Exception {
+ try {
+ if (fromEmergency) {
+ doReturn(CompletableFuture.completedFuture(domain))
+ .when(mEmergencyCallDomainSelectionConnection)
+ .reselectDomain(any());
+ replaceInstance(TelephonyConnectionService.class,
+ "mEmergencyCallDomainSelectionConnection",
+ mTestConnectionService, mEmergencyCallDomainSelectionConnection);
+ replaceInstance(TelephonyConnectionService.class, "mEmergencyCallId",
+ mTestConnectionService, TELECOM_CALL_ID1);
+ } else {
+ doReturn(CompletableFuture.completedFuture(domain))
+ .when(mNormalCallDomainSelectionConnection).reselectDomain(any());
+ replaceInstance(TelephonyConnectionService.class, "mDomainSelectionConnection",
+ mTestConnectionService, mNormalCallDomainSelectionConnection);
+ }
+ } catch (Exception e) {
+ // This shouldn't happen
+ fail();
+ }
+
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ c.setTelecomCallId(TELECOM_CALL_ID1);
+ c.setMockPhone(mockPhone);
+ c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
+
+ Connection oc = c.getOriginalConnection();
+ doReturn(disconnectCause).when(oc).getDisconnectCause();
+ doReturn(preciseDisconnectCause).when(oc).getPreciseDisconnectCause();
+
+ return c;
+ }
+
private SimpleTelephonyConnection createTestConnection(PhoneAccountHandle handle,
int properties, boolean isEmergency) {
SimpleTelephonyConnection connection = new SimpleTelephonyConnection();
@@ -1317,6 +2967,12 @@
return connection;
}
+ private SimpleConference createTestConference(PhoneAccountHandle handle, int properties) {
+ SimpleConference conference = new SimpleConference(handle);
+ conference.setConnectionProperties(properties);
+ return conference;
+ }
+
/**
* Setup the mess of mocks for {@link #testSecondCallSameSubWontDisconnect()} and
* {@link #testIncomingDoesntRequestDisconnect()}.
@@ -1484,21 +3140,26 @@
// Setup 2 SIM device
private void setupDeviceConfig(Phone slot0Phone, Phone slot1Phone, int defaultVoicePhoneId) {
when(mTelephonyManagerProxy.getPhoneCount()).thenReturn(2);
+ when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(false);
when(mSubscriptionManagerProxy.getDefaultVoicePhoneId()).thenReturn(defaultVoicePhoneId);
when(mPhoneFactoryProxy.getPhone(eq(SLOT_0_PHONE_ID))).thenReturn(slot0Phone);
when(mPhoneFactoryProxy.getPhone(eq(SLOT_1_PHONE_ID))).thenReturn(slot1Phone);
}
+ private void setDefaultDataPhoneId(int defaultDataPhoneId) {
+ when(mSubscriptionManagerProxy.getDefaultDataPhoneId()).thenReturn(defaultDataPhoneId);
+ }
+
private void setPhoneRadioAccessFamily(Phone phone, int radioAccessFamily) {
when(phone.getRadioAccessFamily()).thenReturn(radioAccessFamily);
}
- private void setPhoneSlotState(int slotId, int slotState) {
- when(mSubscriptionManagerProxy.getSimStateForSlotIdx(slotId)).thenReturn(slotState);
+ private void setEmergencySmsMode(Phone phone, boolean isInEmergencySmsMode) {
+ when(phone.isInEmergencySmsMode()).thenReturn(isInEmergencySmsMode);
}
- private void setSlotHasIccCard(int slotId, boolean isInserted) {
- when(mTelephonyManagerProxy.hasIccCard(slotId)).thenReturn(isInserted);
+ private void setPhoneSlotState(int slotId, int slotState) {
+ when(mSubscriptionManagerProxy.getSimStateForSlotIdx(slotId)).thenReturn(slotState);
}
private void setDefaultPhone(Phone phone) {
@@ -1518,4 +3179,18 @@
fail();
}
}
+
+ private ConnectionRequest createConnectionRequest(
+ PhoneAccountHandle accountHandle, String address, String callId) {
+ return new ConnectionRequest.Builder()
+ .setAccountHandle(accountHandle)
+ .setAddress(Uri.parse("tel:" + address))
+ .setExtras(new Bundle())
+ .setTelecomCallId(callId)
+ .build();
+ }
+
+ private Executor getExecutor() {
+ return Runnable::run;
+ }
}
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
index 388fd29..bf9fa01 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
@@ -1,5 +1,10 @@
package com.android.services.telephony;
+import static android.telecom.Connection.STATE_DISCONNECTED;
+import static android.telephony.ims.ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED;
+import static android.telephony.ims.ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL;
+import static android.telephony.ims.ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
@@ -7,6 +12,9 @@
import static junit.framework.Assert.fail;
import static junit.framework.TestCase.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -14,9 +22,12 @@
import static org.mockito.Mockito.when;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.telecom.Connection;
import android.telephony.CarrierConfigManager;
import android.telephony.DisconnectCause;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsReasonInfo;
import androidx.test.runner.AndroidJUnit4;
@@ -33,10 +44,14 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+
@RunWith(AndroidJUnit4.class)
public class TelephonyConnectionTest {
@Mock
private ImsPhoneConnection mImsPhoneConnection;
+ @Mock
+ private TelephonyConnectionService mTelephonyConnectionService;
@Before
public void setUp() throws Exception {
@@ -212,4 +227,144 @@
fail("refreshConferenceSupported threw ClassCastException");
}
}
+
+ /**
+ * Tests TelephonyConnection#getCarrierConfig never returns a null given all cases that can
+ * cause a potential null.
+ */
+ @Test
+ public void testGetCarrierConfigBehaviorWithNull() throws Exception {
+ TestTelephonyConnectionSimple c = new TestTelephonyConnectionSimple();
+
+ // case: return a valid carrier config (good case)
+ when(c.mPhoneGlobals.getCarrierConfigForSubId(c.getPhone().getSubId())).
+ thenReturn(CarrierConfigManager.getDefaultConfig());
+ assertNotNull(c.getCarrierConfig());
+
+ // case: PhoneGlobals.getInstance().getCarrierConfigForSubId(int) returns null
+ when(c.mPhoneGlobals.getCarrierConfigForSubId(c.getPhone().getSubId()))
+ .thenReturn(null);
+ assertNotNull(c.getCarrierConfig());
+
+ // case: phone is null
+ c.setMockPhone(null);
+ assertNull(c.getPhone());
+ assertNotNull(c.getCarrierConfig());
+ }
+
+ /**
+ * Tests the behavior of TelephonyConnection#isRttMergeSupported(@NonNull PersistableBundle).
+ * Note, the function should be able to handle an empty PersistableBundle and should NEVER
+ * receive a null object as denoted in by @NonNull annotation.
+ */
+ @Test
+ public void testIsRttMergeSupportedBehavior() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ // ensure isRttMergeSupported(PersistableBundle) does not throw NPE when given an Empty PB
+ assertFalse(c.isRttMergeSupported(new PersistableBundle()));
+
+ // simulate the passing situation
+ c.getCarrierConfigBundle().putBoolean(
+ CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL,
+ true);
+ assertTrue(c.isRttMergeSupported(c.getCarrierConfig()));
+ }
+
+ @Test
+ public void testDomainSelectionDisconnected() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ c.setOriginalConnection(mImsPhoneConnection);
+ doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
+ .getState();
+ c.setTelephonyConnectionService(mTelephonyConnectionService);
+ c.updateState();
+
+ verify(mTelephonyConnectionService)
+ .maybeReselectDomain(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testDomainSelectionDisconnected_NoRedial() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ c.setOriginalConnection(mImsPhoneConnection);
+ doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
+ .getState();
+ c.setTelephonyConnectionService(mTelephonyConnectionService);
+ doReturn(false).when(mTelephonyConnectionService)
+ .maybeReselectDomain(any(), anyInt(), any());
+ c.updateState();
+
+ assertEquals(STATE_DISCONNECTED, c.getState());
+ }
+
+ @Test
+ public void testDomainSelectionDisconnected_Redial() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ c.setOriginalConnection(mImsPhoneConnection);
+
+ doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
+ .getState();
+ c.setTelephonyConnectionService(mTelephonyConnectionService);
+ doReturn(true).when(mTelephonyConnectionService)
+ .maybeReselectDomain(any(), anyInt(), any());
+ c.resetOriginalConnectionCleared();
+ c.updateState();
+
+ assertNotEquals(STATE_DISCONNECTED, c.getState());
+ assertTrue(c.isOriginalConnectionCleared());
+ }
+
+ @Test
+ public void testDomainSelectionDisconnected_AlternateService() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ c.setOriginalConnection(mImsPhoneConnection);
+ c.setIsImsConnection(true);
+ doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
+ .getState();
+ doReturn(new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null))
+ .when(mImsPhoneConnection).getImsReasonInfo();
+ doReturn(getEmergencyNumber(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE))
+ .when(mImsPhoneConnection).getEmergencyNumberInfo();
+ c.setTelephonyConnectionService(mTelephonyConnectionService);
+ doReturn(true).when(mTelephonyConnectionService)
+ .maybeReselectDomain(any(), anyInt(), any());
+ c.updateState();
+
+ Integer serviceCategory = c.getEmergencyServiceCategory();
+
+ assertNotNull(serviceCategory);
+ assertEquals(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+ serviceCategory.intValue());
+ }
+
+ @Test
+ public void testDomainSelectionDisconnected_SilentRedialEmergency() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ c.setOriginalConnection(mImsPhoneConnection);
+ c.setIsImsConnection(true);
+ doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
+ .getState();
+ doReturn(new ImsReasonInfo(CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
+ EXTRA_CODE_CALL_RETRY_EMERGENCY, null))
+ .when(mImsPhoneConnection).getImsReasonInfo();
+ doReturn(getEmergencyNumber(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE))
+ .when(mImsPhoneConnection).getEmergencyNumberInfo();
+ c.setTelephonyConnectionService(mTelephonyConnectionService);
+ doReturn(true).when(mTelephonyConnectionService)
+ .maybeReselectDomain(any(), anyInt(), any());
+ c.updateState();
+
+ Integer serviceCategory = c.getEmergencyServiceCategory();
+
+ assertNotNull(serviceCategory);
+ assertEquals(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+ serviceCategory.intValue());
+ }
+
+ private EmergencyNumber getEmergencyNumber(int eccCategory) {
+ return new EmergencyNumber("", "", "", eccCategory,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ }
}
diff --git a/tests/src/com/android/services/telephony/TelephonyManagerTest.java b/tests/src/com/android/services/telephony/TelephonyManagerTest.java
index cf1ae8f..20c062f 100644
--- a/tests/src/com/android/services/telephony/TelephonyManagerTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyManagerTest.java
@@ -15,11 +15,16 @@
*/
package com.android.services.telephony;
-
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -28,6 +33,8 @@
import android.app.PropertyInvalidatedCache;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
import android.telecom.PhoneAccountHandle;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -37,6 +44,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.IPhoneSubInfo;
+import com.android.internal.telephony.PhoneConstants;
import org.junit.After;
import org.junit.Before;
@@ -47,6 +56,10 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
/** Unit tests for {@link TelephonyManager}. */
@RunWith(AndroidJUnit4.class)
@@ -61,8 +74,10 @@
private static final int TEST_SUBID_2 = 2;
private ITelephony mMockITelephony;
+ private IPhoneSubInfo mMockIPhoneSubInfo;
private SubscriptionManager mMockSubscriptionManager;
private Context mMockContext;
+ private final PackageManager mPackageManager = mock(PackageManager.class);
private TelephonyManager mTelephonyManager;
@@ -89,18 +104,23 @@
}
return null;
}
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
};
@Before
public void setUp() throws Exception {
mMockITelephony = mock(ITelephony.class);
+ mMockIPhoneSubInfo = mock(IPhoneSubInfo.class);
mMockSubscriptionManager = mock(SubscriptionManager.class);
mMockContext = mock(Context.class);
when(mMockContext.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)))
.thenReturn(mMockSubscriptionManager);
-
mTelephonyManager = new TelephonyManager(mContext);
TelephonyManager.setupITelephonyForTest(mMockITelephony);
+ TelephonyManager.setupIPhoneSubInfoForTest(mMockIPhoneSubInfo);
TelephonyManager.enableServiceHandleCaching();
}
@@ -218,4 +238,61 @@
verify(mMockITelephony, times(1)).getSubIdForPhoneAccountHandle(eq(TEST_HANDLE2),
anyString(), anyString());
}
-}
+
+ @Test
+ public void testGetSimServiceTable_USIM() throws RemoteException {
+ assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, true));
+ when(mMockIPhoneSubInfo.getSimServiceTable(anyInt(), anyInt())).thenReturn("12345");
+ assertEquals("12345", mTelephonyManager.getSimServiceTable(PhoneConstants.APPTYPE_USIM));
+ verify(mMockIPhoneSubInfo, times(1)).getSimServiceTable(anyInt(), anyInt());
+ }
+
+ @Test
+ public void testGetSimServiceTable_ISIM() throws RemoteException {
+ when(mMockIPhoneSubInfo.getIsimIst(anyInt())).thenReturn("12345");
+ assertEquals("12345", mTelephonyManager.getSimServiceTable(PhoneConstants.APPTYPE_ISIM));
+ verify(mMockIPhoneSubInfo, times(1)).getIsimIst(anyInt());
+ }
+
+ @Test
+ public void testGetSimServiceTable_RUSIM() throws RemoteException {
+ assumeFalse(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, false));
+ assertEquals(null, mTelephonyManager.getSimServiceTable(PhoneConstants.APPTYPE_RUIM));
+ }
+
+ private boolean hasFeature(String feature, boolean status) {
+ doReturn(status)
+ .when(mPackageManager).hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION);
+ return mContext.getPackageManager().hasSystemFeature(feature);
+ }
+
+ @Test
+ public void getPrimaryImei() throws RemoteException {
+ assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, true));
+ when(mMockITelephony.getPrimaryImei(anyString(), anyString())).thenReturn(
+ "12345");
+ assertEquals("12345", mTelephonyManager.getPrimaryImei());
+ verify(mMockITelephony, times(1)).getPrimaryImei(anyString(), anyString());
+ }
+
+ /**
+ * Verify calling getCarrierRestrictionStatus() with out exception
+ */
+ @Test
+ public void getCarrierRestrictionStatus() {
+ int TIMEOUT = 2 * 60; // 2 minutes
+ LinkedBlockingQueue<Integer> carrierRestrictionStatusResult = new LinkedBlockingQueue<>(1);
+ Executor executor = Executors.newSingleThreadExecutor();
+ mTelephonyManager.getCarrierRestrictionStatus(executor,
+ carrierRestrictionStatusResult::offer);
+ executor.execute(() -> {
+ try {
+ carrierRestrictionStatusResult.poll(TIMEOUT, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ fail();
+ }
+ });
+
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index e149d3b..d91435c 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -50,8 +50,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.List;
@@ -107,6 +105,7 @@
private List<Bundle> mLastConnectionEventExtras = new ArrayList<>();
private Object mLock = new Object();
private PersistableBundle mCarrierConfig = new PersistableBundle();
+ private boolean mOriginalConnectionCleared;
@Override
public com.android.internal.telephony.Connection getOriginalConnection() {
@@ -211,7 +210,15 @@
@Override
void clearOriginalConnection() {
- // Do nothing since the original connection is mock object
+ mOriginalConnectionCleared = true;
+ }
+
+ boolean isOriginalConnectionCleared() {
+ return mOriginalConnectionCleared;
+ }
+
+ void resetOriginalConnectionCleared() {
+ mOriginalConnectionCleared = false;
}
@Override
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnectionSimple.java b/tests/src/com/android/services/telephony/TestTelephonyConnectionSimple.java
new file mode 100644
index 0000000..9dc2551
--- /dev/null
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnectionSimple.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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.services.telephony;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.AttributionSource;
+import android.content.Context;
+import android.os.Process;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.phone.PhoneGlobals;
+
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class TestTelephonyConnectionSimple extends TelephonyConnection{
+
+ @Mock
+ Context mMockContext;
+
+ @Mock
+ PhoneGlobals mPhoneGlobals;
+
+ private Phone mMockPhone;
+
+ public TelephonyConnection cloneConnection() {
+ return this;
+ }
+
+ public TestTelephonyConnectionSimple(){
+ super(null, null, android.telecom.Call.Details.DIRECTION_INCOMING);
+ MockitoAnnotations.initMocks(this);
+
+ AttributionSource attributionSource = new AttributionSource.Builder(
+ Process.myUid()).build();
+
+ mMockPhone = mock(Phone.class);
+ mMockContext = mock(Context.class);
+ mPhoneGlobals = mock(PhoneGlobals.class);
+
+ when(mMockPhone.getSubId()).thenReturn(1);
+ }
+
+ public void setMockPhone(Phone newPhone) {
+ mMockPhone = newPhone;
+ }
+
+ @Override
+ public Phone getPhone() {
+ return mMockPhone;
+ }
+
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/CrossSimRedialingControllerTest.java b/tests/src/com/android/services/telephony/domainselection/CrossSimRedialingControllerTest.java
new file mode 100644
index 0000000..a32329d
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/CrossSimRedialingControllerTest.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2023 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.services.telephony.domainselection;
+
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.REDIAL_TIMER_DISABLED;
+import static android.telephony.PreciseDisconnectCause.EMERGENCY_PERM_FAILURE;
+import static android.telephony.PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE;
+
+import static com.android.services.telephony.domainselection.CrossSimRedialingController.MSG_CROSS_STACK_TIMEOUT;
+import static com.android.services.telephony.domainselection.CrossSimRedialingController.MSG_QUICK_CROSS_STACK_TIMEOUT;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import com.android.TestContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for CrossSimRedialingController
+ */
+public class CrossSimRedialingControllerTest {
+ private static final String TAG = "CrossSimRedialingControllerTest";
+
+ private static final int SLOT_0 = 0;
+ private static final int SLOT_1 = 1;
+
+ private static final String TELECOM_CALL_ID1 = "TC1";
+ private static final String TEST_EMERGENCY_NUMBER = "911";
+
+ @Mock private CarrierConfigManager mCarrierConfigManager;
+ @Mock private TelephonyManager mTelephonyManager;
+ @Mock private EmergencyCallDomainSelector mEcds;
+ @Mock private CrossSimRedialingController.EmergencyNumberHelper mEmergencyNumberHelper;
+
+ private Context mContext;
+
+ private HandlerThread mHandlerThread;
+ private TestableLooper mLooper;
+ private CrossSimRedialingController mCsrController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = new TestContext() {
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ if (serviceClass == TelephonyManager.class) {
+ return Context.TELEPHONY_SERVICE;
+ } else if (serviceClass == CarrierConfigManager.class) {
+ return Context.CARRIER_CONFIG_SERVICE;
+ }
+ return super.getSystemServiceName(serviceClass);
+ }
+
+ @Override
+ public String getOpPackageName() {
+ return "";
+ }
+ };
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mHandlerThread = new HandlerThread("CrossSimRedialingControllerTest");
+ mHandlerThread.start();
+
+ try {
+ mLooper = new TestableLooper(mHandlerThread.getLooper());
+ } catch (Exception e) {
+ logd("Unable to create looper from handler.");
+ }
+
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ when(mTelephonyManager.createForSubscriptionId(anyInt()))
+ .thenReturn(mTelephonyManager);
+ when(mTelephonyManager.getNetworkCountryIso()).thenReturn("");
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
+ doReturn(TelephonyManager.SIM_STATE_READY)
+ .when(mTelephonyManager).getSimState(anyInt());
+
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+ doReturn(getDefaultPersistableBundle()).when(mCarrierConfigManager)
+ .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+ doReturn(true).when(mEmergencyNumberHelper).isEmergencyNumber(anyInt(), anyString());
+
+ doReturn(SLOT_0).when(mEcds).getSlotId();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mCsrController != null) {
+ mCsrController.destroy();
+ mCsrController = null;
+ }
+
+ if (mLooper != null) {
+ mLooper.destroy();
+ mLooper = null;
+ }
+ }
+
+ @Test
+ public void testDefaultStartTimerInService() throws Exception {
+ createController();
+
+ boolean inService = true;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ mCsrController.sendEmptyMessage(MSG_CROSS_STACK_TIMEOUT);
+ processAllMessages();
+
+ verify(mEcds).notifyCrossStackTimerExpired();
+ }
+
+ @Test
+ public void testDefaultStartTimerInServiceRoaming() throws Exception {
+ createController();
+
+ boolean inService = true;
+ boolean inRoaming = true;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+ }
+
+ @Test
+ public void testDefaultStartTimerOutOfService() throws Exception {
+ createController();
+
+ boolean inService = false;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+ }
+
+ @Test
+ public void testDefaultStartTimerOutOfServiceRoaming() throws Exception {
+ createController();
+
+ boolean inService = false;
+ boolean inRoaming = true;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+ }
+
+ @Test
+ public void testQuickStartTimerInService() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+ doReturn(bundle).when(mCarrierConfigManager)
+ .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+ createController();
+
+ boolean inService = true;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+ assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ mCsrController.sendEmptyMessage(MSG_QUICK_CROSS_STACK_TIMEOUT);
+ processAllMessages();
+
+ verify(mEcds).notifyCrossStackTimerExpired();
+ }
+
+ @Test
+ public void testQuickStartTimerInServiceRoaming() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+ doReturn(bundle).when(mCarrierConfigManager)
+ .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+ createController();
+
+ boolean inService = true;
+ boolean inRoaming = true;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+ }
+
+ @Test
+ public void testQuickStartTimerOutOfService() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+ doReturn(bundle).when(mCarrierConfigManager)
+ .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+ createController();
+
+ boolean inService = false;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+ assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+ }
+
+ @Test
+ public void testQuickStartTimerOutOfServiceRoaming() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+ doReturn(bundle).when(mCarrierConfigManager)
+ .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+ createController();
+
+ boolean inService = false;
+ boolean inRoaming = true;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+ }
+
+ @Test
+ public void testNoNormalStartTimerInService() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putInt(KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);
+ doReturn(bundle).when(mCarrierConfigManager)
+ .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+ createController();
+
+ boolean inService = true;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+ assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+ }
+
+ @Test
+ public void testQuickWhenInServiceStartTimerOutOfService() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+ bundle.putBoolean(KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL, true);
+ doReturn(bundle).when(mCarrierConfigManager)
+ .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+ createController();
+
+ boolean inService = false;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+ }
+
+ @Test
+ public void testQuickNoNormalStartTimerInService() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+ bundle.putInt(KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);
+ doReturn(bundle).when(mCarrierConfigManager)
+ .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+ createController();
+
+ boolean inService = true;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+ assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+ }
+
+ @Test
+ public void testDefaultSlot0ThenSlot1() throws Exception {
+ createController();
+
+ boolean inService = true;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ mCsrController.removeMessages(MSG_CROSS_STACK_TIMEOUT);
+ assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ doReturn(SLOT_1).when(mEcds).getSlotId();
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+ }
+
+ @Test
+ public void testDefaultSlot0PermThenSlot1Timeout() throws Exception {
+ createController();
+
+ boolean inService = true;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ mCsrController.notifyCallFailure(EMERGENCY_PERM_FAILURE);
+ mCsrController.stopTimer();
+ assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ doReturn(SLOT_1).when(mEcds).getSlotId();
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ mCsrController.sendEmptyMessage(MSG_CROSS_STACK_TIMEOUT);
+ processAllMessages();
+
+ verify(mEcds, times(0)).notifyCrossStackTimerExpired();
+ }
+
+ @Test
+ public void testDefaultSlot0TempThenSlot1Timeout() throws Exception {
+ createController();
+
+ boolean inService = true;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ mCsrController.notifyCallFailure(EMERGENCY_TEMP_FAILURE);
+ mCsrController.stopTimer();
+ assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ doReturn(SLOT_1).when(mEcds).getSlotId();
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ mCsrController.sendEmptyMessage(MSG_CROSS_STACK_TIMEOUT);
+ processAllMessages();
+
+ verify(mEcds).notifyCrossStackTimerExpired();
+ }
+
+ @Test
+ public void testDefaultSlot0TempThenSlot1TimeoutNotEmergencyNumber() throws Exception {
+ createController();
+
+ boolean inService = true;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ mCsrController.notifyCallFailure(EMERGENCY_TEMP_FAILURE);
+ mCsrController.stopTimer();
+ assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ doReturn(SLOT_1).when(mEcds).getSlotId();
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ doReturn(false).when(mEmergencyNumberHelper).isEmergencyNumber(anyInt(), anyString());
+ mCsrController.sendEmptyMessage(MSG_CROSS_STACK_TIMEOUT);
+ processAllMessages();
+
+ verify(mEcds, times(0)).notifyCrossStackTimerExpired();
+ }
+
+ @Test
+ public void testDefaultSlot0TempThenSlot1TimeoutPinLocked() throws Exception {
+ createController();
+
+ boolean inService = true;
+ boolean inRoaming = false;
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ mCsrController.notifyCallFailure(EMERGENCY_TEMP_FAILURE);
+ mCsrController.stopTimer();
+ assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ doReturn(SLOT_1).when(mEcds).getSlotId();
+ mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+ TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+ assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+ doReturn(TelephonyManager.SIM_STATE_PIN_REQUIRED)
+ .when(mTelephonyManager).getSimState(anyInt());
+ mCsrController.sendEmptyMessage(MSG_CROSS_STACK_TIMEOUT);
+ processAllMessages();
+
+ verify(mEcds, times(0)).notifyCrossStackTimerExpired();
+ }
+
+ private void createController() throws Exception {
+ mCsrController = new CrossSimRedialingController(mContext,
+ mHandlerThread.getLooper(), mEmergencyNumberHelper);
+ }
+
+ private static PersistableBundle getDefaultPersistableBundle() {
+ return getPersistableBundle(0, 120, false);
+ }
+
+ private static PersistableBundle getPersistableBundle(
+ int quickTimer, int timer, boolean startQuickInService) {
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, quickTimer);
+ bundle.putInt(KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT, timer);
+ bundle.putBoolean(KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL,
+ startQuickInService);
+
+ return bundle;
+ }
+
+ private void processAllMessages() {
+ while (!mLooper.getLooper().getQueue().isIdle()) {
+ mLooper.processAllMessages();
+ }
+ }
+
+ private static void logd(String str) {
+ Log.d(TAG, str);
+ }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/DomainSelectorBaseTest.java b/tests/src/com/android/services/telephony/domainselection/DomainSelectorBaseTest.java
new file mode 100644
index 0000000..74c3311
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/DomainSelectorBaseTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.TransportSelectorCallback;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TestContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for DomainSelectorBase.
+ */
+@RunWith(AndroidJUnit4.class)
+public class DomainSelectorBaseTest {
+ public class TestDomainSelectorBase extends DomainSelectorBase {
+ public TestDomainSelectorBase(Context context, int slotId, int subId,
+ @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
+ @NonNull DomainSelectorBase.DestroyListener listener, String logTag) {
+ super(context, slotId, subId, looper, imsStateTracker, listener, logTag);
+ }
+
+ @Override
+ public void cancelSelection() {
+ // No operations.
+ }
+
+ @Override
+ public void reselectDomain(@NonNull SelectionAttributes attr) {
+ // No operations.
+ }
+
+ @Override
+ public void finishSelection() {
+ // No operations.
+ }
+
+ @Override
+ public void selectDomain(SelectionAttributes attr, TransportSelectorCallback callback) {
+ // No operations.
+ }
+ }
+
+ private static final String TAG = DomainSelectorBaseTest.class.getSimpleName();
+ private static final int SLOT_0 = 0;
+ private static final int SUB_1 = 1;
+
+ @Mock private DomainSelectorBase.DestroyListener mDomainSelectorDestroyListener;
+ @Mock private ImsStateTracker mImsStateTracker;
+
+ private Context mContext;
+ private Looper mLooper;
+ private TestDomainSelectorBase mDomainSelector;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = new TestContext();
+
+ HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ mLooper = handlerThread.getLooper();
+ mDomainSelector = new TestDomainSelectorBase(mContext, SLOT_0, SUB_1, mLooper,
+ mImsStateTracker, mDomainSelectorDestroyListener, TAG);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mDomainSelector != null) {
+ mDomainSelector.destroy();
+ mDomainSelector = null;
+ }
+
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+
+ mDomainSelectorDestroyListener = null;
+ mImsStateTracker = null;
+ mContext = null;
+ }
+
+ @Test
+ @SmallTest
+ public void testInit() {
+ assertEquals(SLOT_0, mDomainSelector.getSlotId());
+ assertEquals(SUB_1, mDomainSelector.getSubId());
+ }
+
+ @Test
+ @SmallTest
+ public void testDestroy() {
+ mDomainSelector.destroy();
+ verify(mDomainSelectorDestroyListener).onDomainSelectorDestroyed(eq(mDomainSelector));
+ mDomainSelector = null;
+ }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
new file mode 100644
index 0000000..9be85ed
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -0,0 +1,2117 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.GERAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.NGRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.UNKNOWN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.UTRAN;
+import static android.telephony.BarringInfo.BARRING_SERVICE_TYPE_EMERGENCY;
+import static android.telephony.BarringInfo.BarringServiceInfo.BARRING_TYPE_UNCONDITIONAL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_OVER_CS_ROAMING_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_OVER_IMS_ROAMING_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_SCAN_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE;
+import static android.telephony.CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE;
+import static android.telephony.CarrierConfigManager.ImsEmergency.SCAN_TYPE_NO_PREFERENCE;
+import static android.telephony.CarrierConfigManager.ImsEmergency.VOWIFI_REQUIRES_NONE;
+import static android.telephony.CarrierConfigManager.ImsEmergency.VOWIFI_REQUIRES_SETTING_ENABLED;
+import static android.telephony.CarrierConfigManager.ImsEmergency.VOWIFI_REQUIRES_VALID_EID;
+import static android.telephony.CarrierConfigManager.ImsWfc.KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL;
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
+
+import static com.android.services.telephony.domainselection.EmergencyCallDomainSelector.MSG_MAX_CELLULAR_TIMEOUT;
+import static com.android.services.telephony.domainselection.EmergencyCallDomainSelector.MSG_NETWORK_SCAN_TIMEOUT;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkRequest;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.os.PowerManager;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.BarringInfo;
+import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentityLte;
+import android.telephony.DisconnectCause;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.EmergencyRegResult;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PreciseDisconnectCause;
+import android.telephony.TelephonyManager;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.WwanSelectorCallback;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ProvisioningManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.TestContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Unit tests for EmergencyCallDomainSelector
+ */
+public class EmergencyCallDomainSelectorTest {
+ private static final String TAG = "EmergencyCallDomainSelectorTest";
+
+ private static final int SLOT_0 = 0;
+ private static final int SLOT_0_SUB_ID = 1;
+
+ @Mock private CarrierConfigManager mCarrierConfigManager;
+ @Mock private ConnectivityManager mConnectivityManager;
+ @Mock private TelephonyManager mTelephonyManager;
+ @Mock private WwanSelectorCallback mWwanSelectorCallback;
+ @Mock private TransportSelectorCallback mTransportSelectorCallback;
+ @Mock private ImsMmTelManager mMmTelManager;
+ @Mock private ImsStateTracker mImsStateTracker;
+ @Mock private DomainSelectorBase.DestroyListener mDestroyListener;
+ @Mock private ProvisioningManager mProvisioningManager;
+ @Mock private CrossSimRedialingController mCsrdCtrl;
+
+ private Context mContext;
+
+ private HandlerThread mHandlerThread;
+ private TestableLooper mLooper;
+ private EmergencyCallDomainSelector mDomainSelector;
+ private SelectionAttributes mSelectionAttributes;
+ private @AccessNetworkConstants.RadioAccessNetworkType List<Integer> mAccessNetwork;
+ private PowerManager mPowerManager;
+ private ConnectivityManager.NetworkCallback mNetworkCallback;
+ private Consumer<EmergencyRegResult> mResultConsumer;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = new TestContext() {
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ if (serviceClass == ImsManager.class) {
+ return Context.TELEPHONY_IMS_SERVICE;
+ } else if (serviceClass == TelephonyManager.class) {
+ return Context.TELEPHONY_SERVICE;
+ } else if (serviceClass == CarrierConfigManager.class) {
+ return Context.CARRIER_CONFIG_SERVICE;
+ } else if (serviceClass == PowerManager.class) {
+ return Context.POWER_SERVICE;
+ } else if (serviceClass == ConnectivityManager.class) {
+ return Context.CONNECTIVITY_SERVICE;
+ }
+ return super.getSystemServiceName(serviceClass);
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case (Context.POWER_SERVICE) : {
+ return mPowerManager;
+ }
+ case (Context.CONNECTIVITY_SERVICE) : {
+ return mConnectivityManager;
+ }
+ }
+ return super.getSystemService(name);
+ }
+
+ @Override
+ public String getOpPackageName() {
+ return "";
+ }
+ };
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mHandlerThread = new HandlerThread("EmergencyCallDomainSelectorTest");
+ mHandlerThread.start();
+
+ try {
+ mLooper = new TestableLooper(mHandlerThread.getLooper());
+ } catch (Exception e) {
+ logd("Unable to create looper from handler.");
+ }
+
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ when(mTelephonyManager.createForSubscriptionId(anyInt()))
+ .thenReturn(mTelephonyManager);
+ when(mTelephonyManager.getNetworkCountryIso()).thenReturn("");
+
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt()))
+ .thenReturn(getDefaultPersistableBundle());
+
+ mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ mNetworkCallback = (ConnectivityManager.NetworkCallback)
+ invocation.getArguments()[1];
+ return null;
+ }
+ }).when(mConnectivityManager).registerNetworkCallback(
+ any(NetworkRequest.class), any(ConnectivityManager.NetworkCallback.class));
+
+ IPowerManager powerManager = mock(IPowerManager.class);
+ mPowerManager = new PowerManager(mContext, powerManager, mock(IThermalService.class),
+ new Handler(mHandlerThread.getLooper()));
+
+ ImsManager imsManager = mContext.getSystemService(ImsManager.class);
+ when(imsManager.getImsMmTelManager(anyInt())).thenReturn(mMmTelManager);
+ when(mMmTelManager.isAdvancedCallingSettingEnabled()).thenReturn(true);
+ doReturn(mProvisioningManager).when(imsManager).getProvisioningManager(anyInt());
+ doReturn(null).when(mProvisioningManager).getProvisioningStringValue(anyInt());
+
+ when(mTransportSelectorCallback.onWwanSelected()).thenReturn(mWwanSelectorCallback);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Consumer<WwanSelectorCallback> consumer =
+ (Consumer<WwanSelectorCallback>) invocation.getArguments()[0];
+ consumer.accept(mWwanSelectorCallback);
+ return null;
+ }
+ }).when(mTransportSelectorCallback).onWwanSelected(any());
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ mAccessNetwork = (List<Integer>) invocation.getArguments()[0];
+ mResultConsumer = (Consumer<EmergencyRegResult>) invocation.getArguments()[3];
+ return null;
+ }
+ }).when(mWwanSelectorCallback).onRequestEmergencyNetworkScan(
+ any(), anyInt(), any(), any());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mDomainSelector != null) {
+ mDomainSelector.destroy();
+ mDomainSelector = null;
+ }
+
+ if (mLooper != null) {
+ mLooper.destroy();
+ mLooper = null;
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void testInit() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+
+ verify(mWwanSelectorCallback, times(0)).onRequestEmergencyNetworkScan(
+ any(), anyInt(), any(), any());
+ verify(mWwanSelectorCallback, times(0)).onDomainSelected(anyInt(), eq(true));
+ }
+
+ @Test
+ public void testDefaultCombinedImsRegisteredBarredSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsRegisteredSelectPs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyPsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsRegisteredSelectPsThenCsfb() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyPsDialed();
+
+ mDomainSelector.reselectDomain(attr);
+ processAllMessages();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsNotRegisteredSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testNoCsCombinedImsNotRegisteredSelectPs() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putIntArray(KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+ new int[0]);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyPsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsNotRegisteredBarredSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsRegisteredEmsOffBarredSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ true, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsRegisteredEmsOffSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ true, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsNotRegisteredEmsOffSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ true, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsNotRegisteredEmsOffBarredSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ true, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsRegisteredVopsOffBarredSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ false, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsRegisteredVopsOffSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ false, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsNotRegisteredVopsOffSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ false, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsNotRegisteredVopsOffBarredSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ false, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsRegisteredVopsOffEmsOffBarredSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsRegisteredVopsOffEmsOffSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsNotRegisteredVopsOffEmsOffSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCombinedImsNotRegisteredVopsOffEmsOffBarredSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+ false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultCsSelectCs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testDefaultEpsImsRegisteredBarredScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsRegisteredSelectPs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyPsDialed();
+ }
+
+ @Test
+ public void testDefaultEpsImsNotRegisteredSelectPs() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyPsDialed();
+ }
+
+ @Test
+ public void testDefaultEpsImsNotRegisteredBarredSelectScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsRegisteredEmsOffBarredScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsRegisteredEmsOffScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsNotRegisteredEmsOffScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsNotRegisteredEmsOffBarredScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsRegisteredVopsOffBarredScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ false, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsRegisteredVopsOffScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ false, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsNotRegisteredVopsOffScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ false, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsNotRegisteredVopsOffBarredScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ false, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsRegisteredVopsOffEmsOffBarredScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsRegisteredVopsOffEmsOffScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsNotRegisteredVopsOffEmsOffScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsNotRegisteredVopsOffEmsOffBarredScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testDefaultOutOfServiceScanPsPreferred() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(
+ UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testVoLteOnEpsImsNotRegisteredSelectPs() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ // Requires VoLTE enabled and VoLTE is enabled.
+ verifyPsDialed();
+ }
+
+ @Test
+ public void testVoLteOffEpsImsNotRegisteredScanCsPreferred() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ // Disable VoLTE.
+ when(mMmTelManager.isAdvancedCallingSettingEnabled()).thenReturn(false);
+
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ // Requires VoLTE enabled but VoLTE is'nt enabled.
+ verifyScanCsPreferred();
+ }
+
+ @Test
+ public void testRequiresRegEpsImsNotRegisteredScanCsPreferred() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putBoolean(KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyScanCsPreferred();
+ }
+
+ @Test
+ public void testDefaultEpsImsRegisteredBarredScanTimeoutWifi() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService(true);
+
+ verifyScanPsPreferred();
+
+ assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+
+ // Wi-Fi is not connected.
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // Wi-Fi is connected.
+ mNetworkCallback.onAvailable(null);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(true));
+ }
+
+ @Test
+ public void testVoWifiSosPdnRequiresSettingEnabled() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
+ bundle.putInt(KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT, VOWIFI_REQUIRES_SETTING_ENABLED);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ bindImsServiceUnregistered();
+ processAllMessages();
+
+ assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ // Wi-Fi is not connected.
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // Wi-Fi is connected. But Wi-Fi calling setting is disabled.
+ mNetworkCallback.onAvailable(null);
+ when(mMmTelManager.isVoWiFiRoamingSettingEnabled()).thenReturn(false);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // Wi-Fi is connected and Wi-Fi calling setting is enabled.
+ when(mMmTelManager.isVoWiFiSettingEnabled()).thenReturn(true);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(true));
+ }
+
+ @Test
+ public void testVoWifiSosPdnRequiresValidEid() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
+ bundle.putInt(KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT, VOWIFI_REQUIRES_VALID_EID);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ bindImsServiceUnregistered();
+ processAllMessages();
+
+ assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ // Wi-Fi is not connected.
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // Wi-Fi is connected. But Wi-Fi calling s not activated.
+ mNetworkCallback.onAvailable(null);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // Wi-Fi is connected and Wi-Fi calling is activated.
+ doReturn("1").when(mProvisioningManager).getProvisioningStringValue(anyInt());
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(true));
+ }
+
+ @Test
+ public void testVoWifiImsPdnRequiresNone() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ bindImsServiceUnregistered();
+ processAllMessages();
+
+ assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ // Wi-Fi is not connected.
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // Wi-Fi is connected but IMS is not registered over Wi-Fi.
+ mNetworkCallback.onAvailable(null);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // IMS is registered over Wi-Fi.
+ bindImsService(true);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(false));
+ }
+
+ @Test
+ public void testIgnoreDuplicatedCallbacks() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService(true);
+
+ verify(mTransportSelectorCallback, times(1)).onWwanSelected(any());
+
+ // duplicated event
+ unsolBarringInfoChanged(true);
+
+ // ignore duplicated callback, no change in interaction
+ verify(mTransportSelectorCallback, times(1)).onWwanSelected(any());
+
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(false));
+
+ // duplicated event
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ // ignore duplicated callback, no change in interaction
+ verify(mTransportSelectorCallback, times(1)).onWlanSelected(anyBoolean());
+ }
+
+ @Test
+ public void testDualSimInvalidSubscription() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
+ doReturn(TelephonyManager.SIM_STATE_PIN_REQUIRED)
+ .when(mTelephonyManager).getSimState(anyInt());
+ doReturn(true).when(mCsrdCtrl).isThereOtherSlot();
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+ 0, false, false, 0, 0, "", "", "jp");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+ processAllMessages();
+
+ verify(mTransportSelectorCallback, times(1))
+ .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_PERM_FAILURE));
+ }
+
+ @Test
+ public void testDualSimInvalidSubscriptionButNoOtherSlot() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
+ doReturn(TelephonyManager.SIM_STATE_PIN_REQUIRED)
+ .when(mTelephonyManager).getSimState(anyInt());
+ doReturn(false).when(mCsrdCtrl).isThereOtherSlot();
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+ 0, false, false, 0, 0, "", "", "jp");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+ processAllMessages();
+
+ verify(mTransportSelectorCallback, times(0))
+ .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_PERM_FAILURE));
+ verifyScanPsPreferred();
+ }
+
+ @Test
+ public void testEutranWithCsDomainOnly() throws Exception {
+ setupForHandleScanResult();
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ DOMAIN_CS, false, false, 0, 0, "", "");
+ mResultConsumer.accept(regResult);
+ processAllMessages();
+
+ verifyCsDialed();
+ }
+
+ @Test
+ public void testFullService() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_FULL_SERVICE);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ mResultConsumer = null;
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(UNKNOWN, REGISTRATION_STATE_UNKNOWN,
+ 0, false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+ processAllMessages();
+
+ verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+ any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), any(), any());
+ assertNotNull(mResultConsumer);
+
+ mResultConsumer.accept(regResult);
+ processAllMessages();
+
+ verify(mWwanSelectorCallback, times(2)).onRequestEmergencyNetworkScan(
+ any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), any(), any());
+ }
+
+ @Test
+ public void testFullServiceThenLimtedService() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT,
+ SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ mResultConsumer = null;
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(UNKNOWN, REGISTRATION_STATE_UNKNOWN,
+ 0, false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+ processAllMessages();
+
+ verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+ any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), any(), any());
+ assertNotNull(mResultConsumer);
+
+ mResultConsumer.accept(regResult);
+ processAllMessages();
+
+ verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+ any(), eq(DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE), any(), any());
+ }
+
+ @Test
+ public void testCsThenPsPreference() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+
+ setupForScanListTest(bundle);
+
+ verifyCsPreferredScanList(mDomainSelector.getNextPreferredNetworks(false, false, false));
+ }
+
+ @Test
+ public void testPsThenCsPreference() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+
+ setupForScanListTest(bundle);
+
+ verifyPsPreferredScanList(mDomainSelector.getNextPreferredNetworks(false, false, false));
+ }
+
+ @Test
+ public void testPsOnlyPreference() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+ bundle.putIntArray(KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+ new int[0]);
+
+ setupForScanListTest(bundle);
+
+ verifyPsOnlyScanList(mDomainSelector.getNextPreferredNetworks(false, false, false));
+ }
+
+ @Test
+ public void testCsOnlyPreference() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+ bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+ new int[0]);
+
+ setupForScanListTest(bundle);
+
+ verifyCsOnlyScanList(mDomainSelector.getNextPreferredNetworks(false, false, false));
+
+ }
+
+ @Test
+ public void testCsThenPsPreferenceCsPreferred() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+
+ setupForScanListTest(bundle);
+
+ verifyCsPreferredScanList(mDomainSelector.getNextPreferredNetworks(true, false, false));
+ }
+
+ @Test
+ public void testPsThenCsPreferenceCsPreferred() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+
+ setupForScanListTest(bundle);
+
+ verifyCsPreferredScanList(mDomainSelector.getNextPreferredNetworks(true, false, false));
+ }
+
+ @Test
+ public void testPsOnlyPreferenceCsPreferred() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+ bundle.putIntArray(KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+ new int[0]);
+
+ setupForScanListTest(bundle);
+
+ verifyPsOnlyScanList(mDomainSelector.getNextPreferredNetworks(true, false, false));
+ }
+
+ @Test
+ public void testCsOnlyPreferenceCsPreferred() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+ bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+ new int[0]);
+
+ setupForScanListTest(bundle);
+
+ verifyCsOnlyScanList(mDomainSelector.getNextPreferredNetworks(true, false, false));
+ }
+
+ @Test
+ public void testCsThenPsPreferencePsFail() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+
+ setupForScanListTest(bundle, true);
+
+ bindImsService();
+ processAllMessages();
+
+ verifyCsPreferredScanList(mDomainSelector.getNextPreferredNetworks(false, false, false));
+ }
+
+ @Test
+ public void testPsThenCsPreferencePsFail() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+
+ setupForScanListTest(bundle, true);
+
+ bindImsService();
+ processAllMessages();
+
+ verifyCsPreferredScanList(mDomainSelector.getNextPreferredNetworks(false, false, false));
+ }
+
+ @Test
+ public void testPsOnlyPreferencePsFail() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+ bundle.putIntArray(KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+ new int[0]);
+
+ setupForScanListTest(bundle, true);
+
+ bindImsService();
+ processAllMessages();
+
+ verifyPsOnlyScanList(mDomainSelector.getNextPreferredNetworks(false, false, false));
+ }
+
+ @Test
+ public void testCsThenPsPreferencePsFailCsPreferred() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+
+ setupForScanListTest(bundle, true);
+
+ bindImsService();
+ processAllMessages();
+
+ verifyCsPreferredScanList(mDomainSelector.getNextPreferredNetworks(true, false, false));
+ }
+
+ @Test
+ public void testPsThenCsPreferencePsFailCsPreferred() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+
+ setupForScanListTest(bundle, true);
+
+ bindImsService();
+ processAllMessages();
+
+ verifyCsPreferredScanList(mDomainSelector.getNextPreferredNetworks(true, false, false));
+ }
+
+ @Test
+ public void testPsOnlyPreferencePsFailCsPreferred() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+ bundle.putIntArray(KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+ new int[0]);
+
+ setupForScanListTest(bundle, true);
+
+ bindImsService();
+ processAllMessages();
+
+ verifyPsOnlyScanList(mDomainSelector.getNextPreferredNetworks(true, false, false));
+ }
+
+ @Test
+ public void testEpsFallbackThenCsPreference() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ };
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+ bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+ new int[] { NGRAN, EUTRAN });
+
+ setupForScanListTest(bundle);
+
+ List<Integer> networks = mDomainSelector.getNextPreferredNetworks(false, true, false);
+
+ assertFalse(networks.isEmpty());
+ assertTrue(networks.contains(EUTRAN));
+ assertTrue(networks.contains(NGRAN));
+ assertTrue(networks.contains(UTRAN));
+ assertTrue(networks.contains(GERAN));
+ assertTrue(networks.indexOf(EUTRAN) < networks.indexOf(UTRAN));
+ assertTrue(networks.indexOf(UTRAN) < networks.indexOf(GERAN));
+ assertTrue(networks.indexOf(GERAN) < networks.indexOf(NGRAN));
+ }
+
+ @Test
+ public void testStartCrossStackTimer() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(
+ UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ processAllMessages();
+ verify(mCsrdCtrl).startTimer(any(), eq(mDomainSelector), any(),
+ any(), anyBoolean(), anyBoolean(), anyInt());
+ }
+
+ @Test
+ public void testStopCrossStackTimerOnCancel() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ mDomainSelector.cancelSelection();
+
+ verify(mCsrdCtrl).stopTimer();
+ }
+
+ @Test
+ public void testStopCrossStackTimerOnFinish() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ mDomainSelector.finishSelection();
+
+ verify(mCsrdCtrl).stopTimer();
+ }
+
+ @Test
+ public void testCrossStackTimerTempFailure() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+
+ attr = new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+ .setEmergency(true)
+ .setEmergencyRegResult(regResult)
+ .setCsDisconnectCause(PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE)
+ .build();
+
+ mDomainSelector.reselectDomain(attr);
+ processAllMessages();
+
+ verify(mCsrdCtrl).notifyCallFailure(eq(PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE));
+ }
+
+ @Test
+ public void testCrossStackTimerPermFailure() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+
+ attr = new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+ .setEmergency(true)
+ .setEmergencyRegResult(regResult)
+ .setCsDisconnectCause(PreciseDisconnectCause.EMERGENCY_PERM_FAILURE)
+ .build();
+
+ mDomainSelector.reselectDomain(attr);
+ processAllMessages();
+
+ verify(mCsrdCtrl).notifyCallFailure(eq(PreciseDisconnectCause.EMERGENCY_PERM_FAILURE));
+ }
+
+ @Test
+ public void testCrossStackTimerExpired() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(
+ UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyScanPsPreferred();
+
+ mDomainSelector.notifyCrossStackTimerExpired();
+
+ verify(mTransportSelectorCallback)
+ .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
+ }
+
+ @Test
+ public void testCrossStackTimerExpiredAfterDomainSelected() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+
+ mDomainSelector.notifyCrossStackTimerExpired();
+
+ verify(mTransportSelectorCallback, times(0))
+ .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
+
+ mDomainSelector.reselectDomain(attr);
+ processAllMessages();
+
+ verify(mTransportSelectorCallback)
+ .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
+ }
+
+ @Test
+ public void testDefaultEpsImsRegisteredSelectPsEmergencyRegFailed() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsService();
+
+ verifyPsDialed();
+
+ attr = new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+ .setEmergency(true)
+ .setEmergencyRegResult(regResult)
+ .setPsDisconnectCause(
+ new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, 0, null))
+ .build();
+ mDomainSelector.reselectDomain(attr);
+ processAllMessages();
+
+ verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+ any(), anyInt(), any(), any());
+ assertFalse(mAccessNetwork.contains(EUTRAN));
+ }
+
+ @Test
+ public void testMaxCellularTimeout() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
+ bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 20);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ setupForHandleScanResult();
+
+ assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+ assertTrue(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // Wi-Fi is connected.
+ mNetworkCallback.onAvailable(null);
+
+ // Max cellular timer expired
+ mDomainSelector.removeMessages(MSG_MAX_CELLULAR_TIMEOUT);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_MAX_CELLULAR_TIMEOUT));
+
+ assertFalse(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+ verify(mTransportSelectorCallback, times(1)).onWlanSelected(anyBoolean());
+ }
+
+
+ @Test
+ public void testMaxCellularTimeoutScanTimeout() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
+ bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 20);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ setupForHandleScanResult();
+
+ assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+ assertTrue(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // Wi-Fi is connected.
+ mNetworkCallback.onAvailable(null);
+
+ // Scan timer expired
+ mDomainSelector.removeMessages(MSG_NETWORK_SCAN_TIMEOUT);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ assertFalse(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+ verify(mTransportSelectorCallback, times(1)).onWlanSelected(anyBoolean());
+ }
+
+ @Test
+ public void testMaxCellularTimeoutWhileDialingOnCellular() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
+ bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 5);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+
+ verifyCsDialed();
+
+ assertFalse(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+ assertFalse(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+ mDomainSelector.reselectDomain(attr);
+ processAllMessages();
+
+ assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+ assertTrue(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+ // Wi-Fi is connected.
+ mNetworkCallback.onAvailable(null);
+ processAllMessages();
+
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // Max cellular timer expired
+ mDomainSelector.removeMessages(MSG_MAX_CELLULAR_TIMEOUT);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_MAX_CELLULAR_TIMEOUT));
+ processAllMessages();
+
+ mDomainSelector.reselectDomain(attr);
+ processAllMessages();
+
+ assertFalse(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+ verify(mTransportSelectorCallback, times(1)).onWlanSelected(anyBoolean());
+ }
+
+ @Test
+ public void testMaxCellularTimeoutWileDialingOnWlan() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
+ bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 20);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ setupForHandleScanResult();
+
+ assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+ assertTrue(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // Wi-Fi is connected.
+ mNetworkCallback.onAvailable(null);
+
+ // Network scan timer expired
+ mDomainSelector.removeMessages(MSG_NETWORK_SCAN_TIMEOUT);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(1)).onWlanSelected(anyBoolean());
+ assertFalse(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+ }
+
+ @Test
+ public void testMaxCellularTimeoutWileDialingOnWlanAllowMultipleTries() throws Exception {
+ PersistableBundle bundle = getDefaultPersistableBundle();
+ bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
+ bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 20);
+ bundle.putInt(KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT, 2);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ setupForHandleScanResult();
+
+ assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+ assertTrue(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+ // Wi-Fi is connected.
+ mNetworkCallback.onAvailable(null);
+
+ // Network scan timer expired
+ mDomainSelector.removeMessages(MSG_NETWORK_SCAN_TIMEOUT);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+ verify(mTransportSelectorCallback, times(1)).onWlanSelected(anyBoolean());
+ assertFalse(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+ EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_CS,
+ true, true, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.reselectDomain(attr);
+ processAllMessages();
+
+ assertTrue(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+ // Max cellular timer expired
+ mDomainSelector.removeMessages(MSG_MAX_CELLULAR_TIMEOUT);
+ mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_MAX_CELLULAR_TIMEOUT));
+ processAllMessages();
+
+ verify(mTransportSelectorCallback, times(2)).onWlanSelected(anyBoolean());
+ }
+
+ private void setupForScanListTest(PersistableBundle bundle) throws Exception {
+ setupForScanListTest(bundle, false);
+ }
+
+ private void setupForScanListTest(PersistableBundle bundle, boolean psFailed) throws Exception {
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+ 0, false, false, 0, 0, "", "");
+ if (psFailed) {
+ regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.DOMAIN_PS, true, true, 0, 0, "", "");
+ }
+
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+ }
+
+ private void verifyCsPreferredScanList(List<Integer> networks) {
+ assertFalse(networks.isEmpty());
+ assertTrue(networks.contains(EUTRAN));
+ assertTrue(networks.contains(UTRAN));
+ assertTrue(networks.contains(GERAN));
+ assertTrue(networks.indexOf(UTRAN) < networks.indexOf(EUTRAN));
+ }
+
+ private void verifyPsPreferredScanList(List<Integer> networks) {
+ assertFalse(networks.isEmpty());
+ assertTrue(networks.contains(EUTRAN));
+ assertTrue(networks.contains(UTRAN));
+ assertTrue(networks.contains(GERAN));
+ assertTrue(networks.indexOf(EUTRAN) < networks.indexOf(UTRAN));
+ }
+
+ private void verifyPsOnlyScanList(List<Integer> networks) {
+ assertFalse(networks.isEmpty());
+ assertTrue(networks.contains(EUTRAN));
+ assertFalse(networks.contains(UTRAN));
+ assertFalse(networks.contains(GERAN));
+ }
+
+ private void verifyCsOnlyScanList(List<Integer> networks) {
+ assertFalse(networks.isEmpty());
+ assertFalse(networks.contains(EUTRAN));
+ assertTrue(networks.contains(UTRAN));
+ assertTrue(networks.contains(GERAN));
+ }
+
+ private void setupForHandleScanResult() throws Exception {
+ mResultConsumer = null;
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(true);
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+ 0, false, false, 0, 0, "", "");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+ processAllMessages();
+
+ verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+ any(), anyInt(), any(), any());
+ assertNotNull(mResultConsumer);
+ }
+
+ private void createSelector(int subId) throws Exception {
+ mDomainSelector = new EmergencyCallDomainSelector(
+ mContext, SLOT_0, subId, mHandlerThread.getLooper(),
+ mImsStateTracker, mDestroyListener, mCsrdCtrl);
+
+ replaceInstance(DomainSelectorBase.class,
+ "mWwanSelectorCallback", mDomainSelector, mWwanSelectorCallback);
+ }
+
+ private void verifyCsDialed() {
+ processAllMessages();
+ verify(mWwanSelectorCallback, times(1)).onDomainSelected(eq(DOMAIN_CS), eq(false));
+ }
+
+ private void verifyPsDialed() {
+ processAllMessages();
+ verify(mWwanSelectorCallback, times(1)).onDomainSelected(eq(DOMAIN_PS), eq(true));
+ }
+
+ private void verifyScanPsPreferred() {
+ verifyScanPreferred(DomainSelectionService.SCAN_TYPE_NO_PREFERENCE, EUTRAN);
+ }
+
+ private void verifyScanCsPreferred() {
+ verifyScanPreferred(DomainSelectionService.SCAN_TYPE_NO_PREFERENCE, UTRAN);
+ }
+
+ private void verifyScanPreferred(int scanType, int expectedPreferredAccessNetwork) {
+ processAllMessages();
+ verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+ any(), eq(scanType), any(), any());
+ assertEquals(expectedPreferredAccessNetwork, (int) mAccessNetwork.get(0));
+ }
+
+ private void unsolBarringInfoChanged(boolean barred) {
+ SparseArray<BarringInfo.BarringServiceInfo> serviceInfos = new SparseArray<>();
+ if (barred) {
+ serviceInfos.put(BARRING_SERVICE_TYPE_EMERGENCY,
+ new BarringInfo.BarringServiceInfo(BARRING_TYPE_UNCONDITIONAL, false, 0, 0));
+ }
+ mDomainSelector.onBarringInfoUpdated(new BarringInfo(new CellIdentityLte(), serviceInfos));
+ }
+
+ private void bindImsService() {
+ bindImsService(false);
+ }
+
+ private void bindImsService(boolean isWifi) {
+ doReturn(isWifi).when(mImsStateTracker).isImsRegisteredOverWlan();
+ doReturn(true).when(mImsStateTracker).isImsRegistered();
+ mDomainSelector.onImsRegistrationStateChanged();
+ doReturn(true).when(mImsStateTracker).isImsVoiceCapable();
+ mDomainSelector.onImsMmTelCapabilitiesChanged();
+ }
+
+ private void bindImsServiceUnregistered() {
+ doReturn(false).when(mImsStateTracker).isImsRegistered();
+ mDomainSelector.onImsRegistrationStateChanged();
+ doReturn(false).when(mImsStateTracker).isImsVoiceCapable();
+ mDomainSelector.onImsMmTelCapabilitiesChanged();
+ }
+
+ private static EmergencyRegResult getEmergencyRegResult(
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetwork,
+ @NetworkRegistrationInfo.RegistrationState int regState,
+ @NetworkRegistrationInfo.Domain int domain,
+ boolean isVopsSupported, boolean isEmcBearerSupported, int emc, int emf,
+ @NonNull String mcc, @NonNull String mnc) {
+ return getEmergencyRegResult(accessNetwork, regState, domain, isVopsSupported,
+ isEmcBearerSupported, emc, emf, mcc, mnc, "");
+ }
+
+ private static EmergencyRegResult getEmergencyRegResult(
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetwork,
+ @NetworkRegistrationInfo.RegistrationState int regState,
+ @NetworkRegistrationInfo.Domain int domain,
+ boolean isVopsSupported, boolean isEmcBearerSupported, int emc, int emf,
+ @NonNull String mcc, @NonNull String mnc, @NonNull String iso) {
+ return new EmergencyRegResult(accessNetwork, regState,
+ domain, isVopsSupported, isEmcBearerSupported,
+ emc, emf, mcc, mnc, iso);
+ }
+
+ private static PersistableBundle getDefaultPersistableBundle() {
+ int[] imsRats = new int[] { EUTRAN };
+ int[] csRats = new int[] { UTRAN, GERAN };
+ int[] imsRoamRats = new int[] { EUTRAN };
+ int[] csRoamRats = new int[] { UTRAN, GERAN };
+ int[] domainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_NON_3GPP
+ };
+ int[] roamDomainPreference = new int[] {
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+ CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+ CarrierConfigManager.ImsEmergency.DOMAIN_PS_NON_3GPP
+ };
+ boolean imsWhenVoiceOnCs = false;
+ int voWifiRequiresCondition = VOWIFI_REQUIRES_NONE;
+ int maxRetriesOverWiFi = 1;
+ int cellularScanTimerSec = 10;
+ int maxCellularTimerSec = 0;
+ boolean voWifiOverEmergencyPdn = false;
+ int scanType = SCAN_TYPE_NO_PREFERENCE;
+ boolean requiresImsRegistration = false;
+ boolean requiresVoLteEnabled = false;
+ boolean ltePreferredAfterNrFailed = false;
+ String[] cdmaPreferredNumbers = new String[] {};
+
+ return getPersistableBundle(imsRats, csRats, imsRoamRats, csRoamRats,
+ domainPreference, roamDomainPreference, imsWhenVoiceOnCs,
+ voWifiRequiresCondition, maxRetriesOverWiFi, cellularScanTimerSec,
+ maxCellularTimerSec, scanType, voWifiOverEmergencyPdn, requiresImsRegistration,
+ requiresVoLteEnabled, ltePreferredAfterNrFailed, cdmaPreferredNumbers);
+ }
+
+ private static PersistableBundle getPersistableBundle(
+ @Nullable int[] imsRats, @Nullable int[] csRats,
+ @Nullable int[] imsRoamRats, @Nullable int[] csRoamRats,
+ @Nullable int[] domainPreference, @Nullable int[] roamDomainPreference,
+ boolean imsWhenVoiceOnCs, int voWifiRequiresCondition,
+ int maxRetriesOverWiFi, int cellularScanTimerSec,
+ int maxCellularTimerSec, int scanType,
+ boolean voWifiOverEmergencyPdn, boolean requiresImsRegistration,
+ boolean requiresVoLteEnabled, boolean ltePreferredAfterNrFailed,
+ @Nullable String[] cdmaPreferredNumbers) {
+
+ PersistableBundle bundle = new PersistableBundle();
+ if (imsRats != null) {
+ bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+ imsRats);
+ }
+ if (imsRoamRats != null) {
+ bundle.putIntArray(
+ KEY_EMERGENCY_OVER_IMS_ROAMING_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+ imsRoamRats);
+ }
+ if (csRats != null) {
+ bundle.putIntArray(KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+ csRats);
+ }
+ if (csRoamRats != null) {
+ bundle.putIntArray(
+ KEY_EMERGENCY_OVER_CS_ROAMING_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+ csRoamRats);
+ }
+ if (domainPreference != null) {
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+ }
+ if (roamDomainPreference != null) {
+ bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY,
+ roamDomainPreference);
+ }
+ bundle.putBoolean(KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL, imsWhenVoiceOnCs);
+ bundle.putInt(KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT, voWifiRequiresCondition);
+ bundle.putInt(KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT, maxRetriesOverWiFi);
+ bundle.putInt(KEY_EMERGENCY_SCAN_TIMER_SEC_INT, cellularScanTimerSec);
+ bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, maxCellularTimerSec);
+ bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, voWifiOverEmergencyPdn);
+ bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, scanType);
+ bundle.putBoolean(KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL, requiresImsRegistration);
+ bundle.putBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL, requiresVoLteEnabled);
+ bundle.putInt(KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT, 0);
+ bundle.putBoolean(KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL,
+ ltePreferredAfterNrFailed);
+ bundle.putStringArray(KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY,
+ cdmaPreferredNumbers);
+
+ return bundle;
+ }
+
+ public static SelectionAttributes getSelectionAttributes(int slotId, int subId,
+ EmergencyRegResult regResult) {
+ SelectionAttributes.Builder builder =
+ new SelectionAttributes.Builder(slotId, subId, SELECTOR_TYPE_CALLING)
+ .setEmergency(true)
+ .setEmergencyRegResult(regResult);
+ return builder.build();
+ }
+
+ private static void replaceInstance(final Class c,
+ final String instanceName, final Object obj, final Object newValue) throws Exception {
+ Field field = c.getDeclaredField(instanceName);
+ field.setAccessible(true);
+ field.set(obj, newValue);
+ }
+
+ private void processAllMessages() {
+ while (!mLooper.getLooper().getQueue().isIdle()) {
+ mLooper.processAllMessages();
+ }
+ }
+
+ private static void logd(String str) {
+ Log.d(TAG, str);
+ }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java
new file mode 100644
index 0000000..ed064cb
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
+import android.telephony.BarringInfo;
+import android.telephony.CarrierConfigManager;
+import android.telephony.DataSpecificRegistrationInfo;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.VopsSupportInfo;
+import android.telephony.WwanSelectorCallback;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+import android.util.SparseArray;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TestContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Consumer;
+
+/**
+ * Unit tests for EmergencySmsDomainSelector.
+ */
+@RunWith(AndroidJUnit4.class)
+public class EmergencySmsDomainSelectorTest {
+ private static final int SLOT_0 = 0;
+ private static final int SUB_1 = 1;
+
+ @Mock private ServiceState mServiceState;
+ @Mock private TransportSelectorCallback mTransportSelectorCallback;
+ @Mock private WwanSelectorCallback mWwanSelectorCallback;
+ @Mock private VopsSupportInfo mVopsSupportInfo;
+ @Mock private ImsStateTracker mImsStateTracker;
+ @Mock private DomainSelectorBase.DestroyListener mDomainSelectorDestroyListener;
+
+ private final SelectionAttributes mSelectionAttributes =
+ new SelectionAttributes.Builder(SLOT_0, SUB_1, SELECTOR_TYPE_SMS)
+ .setEmergency(true)
+ .build();
+ private Context mContext;
+ private Looper mLooper;
+ private TestableLooper mTestableLooper;
+ private CarrierConfigManager mCarrierConfigManager;
+ private NetworkRegistrationInfo mNetworkRegistrationInfo;
+ private boolean mCarrierConfigManagerNullTest = false;
+ private BarringInfo mBarringInfo = new BarringInfo();
+ private ImsStateTracker.BarringInfoListener mBarringInfoListener;
+ private ImsStateTracker.ServiceStateListener mServiceStateListener;
+ private EmergencySmsDomainSelector mDomainSelector;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = new TestContext() {
+ @Override
+ public Object getSystemService(String name) {
+ if (name.equals(Context.CARRIER_CONFIG_SERVICE)) {
+ if (mCarrierConfigManagerNullTest) {
+ return null;
+ }
+ }
+
+ return super.getSystemService(name);
+ }
+ };
+
+ HandlerThread handlerThread = new HandlerThread(
+ EmergencySmsDomainSelectorTest.class.getSimpleName());
+ handlerThread.start();
+ mLooper = handlerThread.getLooper();
+ mTestableLooper = new TestableLooper(mLooper);
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+
+ mDomainSelector = new EmergencySmsDomainSelector(mContext, SLOT_0, SUB_1,
+ mLooper, mImsStateTracker, mDomainSelectorDestroyListener);
+
+ ArgumentCaptor<ImsStateTracker.ServiceStateListener> serviceStateListenerCaptor =
+ ArgumentCaptor.forClass(ImsStateTracker.ServiceStateListener.class);
+ verify(mImsStateTracker).addServiceStateListener(serviceStateListenerCaptor.capture());
+ mServiceStateListener = serviceStateListenerCaptor.getValue();
+ assertNotNull(mServiceStateListener);
+
+ ArgumentCaptor<ImsStateTracker.BarringInfoListener> barringInfoListenerCaptor =
+ ArgumentCaptor.forClass(ImsStateTracker.BarringInfoListener.class);
+ verify(mImsStateTracker).addBarringInfoListener(barringInfoListenerCaptor.capture());
+ mBarringInfoListener = barringInfoListenerCaptor.getValue();
+ assertNotNull(mBarringInfoListener);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mTestableLooper != null) {
+ mTestableLooper.destroy();
+ mTestableLooper = null;
+ }
+
+ if (mDomainSelector != null) {
+ mDomainSelector.destroy();
+ verify(mImsStateTracker).removeImsStateListener(eq(mDomainSelector));
+ verify(mImsStateTracker).removeBarringInfoListener(eq(mDomainSelector));
+ verify(mImsStateTracker).removeServiceStateListener(eq(mDomainSelector));
+ }
+
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+
+ mDomainSelector = null;
+ mNetworkRegistrationInfo = null;
+ mVopsSupportInfo = null;
+ mDomainSelectorDestroyListener = null;
+ mWwanSelectorCallback = null;
+ mTransportSelectorCallback = null;
+ mServiceState = null;
+ mCarrierConfigManager = null;
+ mCarrierConfigManagerNullTest = false;
+ }
+
+ @Test
+ @SmallTest
+ public void testFinishSelection() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(mBarringInfo);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+ assertTrue(mDomainSelector.isDomainSelectionReady());
+
+ mDomainSelector.finishSelection();
+
+ assertFalse(mDomainSelector.isDomainSelectionReady());
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsDomainSelectionReady() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+
+ assertFalse(mDomainSelector.isDomainSelectionReady());
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(mBarringInfo);
+
+ assertTrue(mDomainSelector.isDomainSelectionReady());
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsDomainSelectionReadyAndSelectDomain() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(mBarringInfo);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+ assertTrue(mDomainSelector.isDomainSelectionReady());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenImsRegistered() {
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+
+ assertTrue(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenImsRegisteredAndConfigEnabledAndLteAvailable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+
+ assertTrue(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenImsRegisteredAndConfigEnabledAndLteNotAvailable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, false, false, false);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenCarrierConfigManagerIsNull() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ mCarrierConfigManagerNullTest = true;
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenCarrierConfigIsNull() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(null);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenCarrierConfigNotEnabled() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(false);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenMmTelFeatureUnavailable() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN, false, false);
+ setUpCarrierConfig(true);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenServiceStateIsNull() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenNoLte() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(
+ anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+ .thenReturn(mNetworkRegistrationInfo);
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteNotRegisteredOrEmergencyNotEnabled() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(
+ anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+ .thenReturn(mNetworkRegistrationInfo);
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndNoDataSpecificRegistrationInfo() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(true, true, true, true, false);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndNoVopsSupportInfo() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, true, true, true, false);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndEmcBsNotSupported() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, false, true, false);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndEmcBsSupportedAndNoBarringInfo() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, true, false);
+
+ assertTrue(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndEmcBsSupportedAndBarred() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, true);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndEmcBsSupportedAndNotBarred() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+
+ assertTrue(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInLimitedService() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLimitedLteService(false, false, true, false, false);
+
+ assertTrue(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhilePreviousRequestInProgress() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(mBarringInfo);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ processAllMessages();
+
+ // onDomainSelected will be invoked only once
+ // even though the domain selection was requested twice.
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(true));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndConfigDisabled() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(false);
+ setUpLteInService(false, false, true, false, false);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndUmtsNetwork() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpNonLteService(TelephonyManager.NETWORK_TYPE_UMTS);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndUnknownNetwork() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpNonLteService(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLteInService() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: PS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(true));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLteEmcBsNotSupported() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, false, false, false);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLteEmergencyBarred() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, true);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLimitedLteService() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLimitedLteService(false, false, true, false, false);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: PS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(true));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLimitedLteEmcBsNotSupported() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLimitedLteService(false, false, false, false, false);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLimitedLteEmergencyBarred() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLimitedLteService(false, false, true, false, true);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnLte() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: PS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(true));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnLteAndEmcBsNotSupported() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, false, false, false);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnLteAndEmergencyBarred() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, true);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnIwlanAndConfigDisabled() {
+ setUpImsStateTracker(AccessNetworkType.IWLAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(false);
+ setUpLteInService(false, false, true, false, false);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: WLAN
+ verify(mTransportSelectorCallback).onWlanSelected(eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnIwlanAndLteNotAvailable() {
+ setUpImsStateTracker(AccessNetworkType.IWLAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, false, false, false);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network - even though IMS is successfully registered over Wi-Fi,
+ // if the emergency SMS messages over IMS is enabled in the carrier configuration and
+ // the PS network does not allow the emergency service, this MO SMS should be routed to
+ // CS domain.
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnIwlanAndLteAvailable() {
+ setUpImsStateTracker(AccessNetworkType.IWLAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: PS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(true));
+ }
+
+ private void setUpCarrierConfig(boolean supported) {
+ PersistableBundle b = new PersistableBundle();
+ b.putBoolean(CarrierConfigManager.KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL, supported);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(b);
+ }
+
+ private void setUpNonLteService(int networkType) {
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(networkType)
+ .setRegistrationState(networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN
+ ? NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN
+ : NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(
+ anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+ .thenReturn(mNetworkRegistrationInfo);
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(null);
+ }
+
+ private void setUpLteInService(boolean noDataSpecificRegistrationInfo,
+ boolean noVopsSupportInfo, boolean emcBsSupported,
+ boolean noBarringInfo, boolean barred) {
+ DataSpecificRegistrationInfo dsri = noDataSpecificRegistrationInfo
+ ? null : new DataSpecificRegistrationInfo(
+ 8, false, false, false, noVopsSupportInfo ? null : mVopsSupportInfo);
+
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setDataSpecificInfo(dsri)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(
+ anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+ .thenReturn(mNetworkRegistrationInfo);
+ when(mVopsSupportInfo.isEmergencyServiceSupported()).thenReturn(emcBsSupported);
+
+ BarringInfo barringInfo = null;
+
+ if (!noBarringInfo) {
+ SparseArray<BarringInfo.BarringServiceInfo> barringServiceInfos = new SparseArray<>();
+ barringServiceInfos.put(BarringInfo.BARRING_SERVICE_TYPE_EMERGENCY,
+ new BarringInfo.BarringServiceInfo(
+ barred ? BarringInfo.BarringServiceInfo.BARRING_TYPE_UNCONDITIONAL :
+ BarringInfo.BarringServiceInfo.BARRING_TYPE_NONE, false, 0, 0));
+ barringInfo = new BarringInfo(null, barringServiceInfos);
+ }
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(barringInfo);
+ }
+
+ private void setUpLimitedLteService(boolean noDataSpecificRegistrationInfo,
+ boolean noVopsSupportInfo, boolean emcBsSupported,
+ boolean noBarringInfo, boolean barred) {
+ DataSpecificRegistrationInfo dsri = noDataSpecificRegistrationInfo
+ ? null : new DataSpecificRegistrationInfo(
+ 8, false, false, false, noVopsSupportInfo ? null : mVopsSupportInfo);
+
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setEmergencyOnly(true)
+ .setDataSpecificInfo(dsri)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(
+ anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+ .thenReturn(mNetworkRegistrationInfo);
+ when(mVopsSupportInfo.isEmergencyServiceSupported()).thenReturn(emcBsSupported);
+
+ BarringInfo barringInfo = null;
+
+ if (!noBarringInfo) {
+ SparseArray<BarringInfo.BarringServiceInfo> barringServiceInfos = new SparseArray<>();
+ barringServiceInfos.put(BarringInfo.BARRING_SERVICE_TYPE_EMERGENCY,
+ new BarringInfo.BarringServiceInfo(
+ barred ? BarringInfo.BarringServiceInfo.BARRING_TYPE_UNCONDITIONAL :
+ BarringInfo.BarringServiceInfo.BARRING_TYPE_NONE, false, 0, 0));
+ barringInfo = new BarringInfo(null, barringServiceInfos);
+ }
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(barringInfo);
+ }
+
+ private void setUpImsStateTracker(@RadioAccessNetworkType int accessNetworkType) {
+ setUpImsStateTracker(accessNetworkType, true, true);
+ }
+
+ private void setUpImsStateTracker(@RadioAccessNetworkType int accessNetworkType,
+ boolean mmTelFeatureAvailable, boolean smsCapable) {
+ when(mImsStateTracker.isMmTelFeatureAvailable()).thenReturn(mmTelFeatureAvailable);
+ when(mImsStateTracker.isImsRegistered())
+ .thenReturn(accessNetworkType != AccessNetworkType.UNKNOWN);
+ when(mImsStateTracker.isImsRegisteredOverWlan())
+ .thenReturn(accessNetworkType == AccessNetworkType.IWLAN);
+ when(mImsStateTracker.getImsAccessNetworkType()).thenReturn(accessNetworkType);
+ when(mImsStateTracker.isImsSmsCapable()).thenReturn(smsCapable);
+ }
+
+ private void setUpWwanSelectorCallback() {
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final Consumer<WwanSelectorCallback> callback =
+ (Consumer<WwanSelectorCallback>) args[0];
+ callback.accept(mWwanSelectorCallback);
+ return null;
+ }).when(mTransportSelectorCallback).onWwanSelected(any(Consumer.class));
+ }
+
+ private void setUpImsStateListener(boolean notifyMmTelFeatureAvailable,
+ boolean notifyImsRegState, boolean notifyMmTelCapability) {
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final ImsStateTracker.ImsStateListener listener =
+ (ImsStateTracker.ImsStateListener) args[0];
+ mDomainSelector.post(() -> {
+ if (notifyMmTelFeatureAvailable) {
+ listener.onImsMmTelFeatureAvailableChanged();
+ }
+ if (notifyImsRegState) {
+ listener.onImsRegistrationStateChanged();
+ }
+ if (notifyMmTelCapability) {
+ listener.onImsMmTelCapabilitiesChanged();
+ }
+ });
+ return null;
+ }).when(mImsStateTracker).addImsStateListener(any(ImsStateTracker.ImsStateListener.class));
+ }
+
+ private void processAllMessages() {
+ while (!mTestableLooper.getLooper().getQueue().isIdle()) {
+ mTestableLooper.processAllMessages();
+ }
+ }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/ImsStateTrackerTest.java b/tests/src/com/android/services/telephony/domainselection/ImsStateTrackerTest.java
new file mode 100644
index 0000000..430adea
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/ImsStateTrackerTest.java
@@ -0,0 +1,774 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.BarringInfo;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.ImsStateCallback;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TestContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit tests for ImsStateTracker.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImsStateTrackerTest {
+ private static final int SLOT_0 = 0;
+ private static final int SUB_1 = 1;
+ private static final int SUB_2 = 2;
+ private static final long TIMEOUT_MS = 100;
+
+ @Mock private ImsMmTelManager mMmTelManager;
+ @Mock private ImsMmTelManager mMmTelManager2;
+ @Mock private ImsStateTracker.BarringInfoListener mBarringInfoListener;
+ @Mock private ImsStateTracker.ServiceStateListener mServiceStateListener;
+ @Mock private ImsStateTracker.ImsStateListener mImsStateListener;
+ @Mock private ServiceState mServiceState;
+
+ private Context mContext;
+ private Looper mLooper;
+ private BarringInfo mBarringInfo = new BarringInfo();
+ private ImsStateTracker mImsStateTracker;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = new TestContext() {
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ if (serviceClass == ImsManager.class) {
+ return Context.TELEPHONY_IMS_SERVICE;
+ }
+ return super.getSystemServiceName(serviceClass);
+ }
+ };
+
+ HandlerThread handlerThread = new HandlerThread(
+ ImsStateTrackerTest.class.getSimpleName());
+ handlerThread.start();
+ mLooper = handlerThread.getLooper();
+ mImsStateTracker = new ImsStateTracker(mContext, SLOT_0, mLooper);
+
+ ImsManager imsManager = mContext.getSystemService(ImsManager.class);
+ when(imsManager.getImsMmTelManager(eq(SUB_1))).thenReturn(mMmTelManager);
+ when(imsManager.getImsMmTelManager(eq(SUB_2))).thenReturn(mMmTelManager2);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mImsStateTracker.destroy();
+ mImsStateTracker = null;
+ mMmTelManager = null;
+
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testInit() {
+ assertEquals(SLOT_0, mImsStateTracker.getSlotId());
+ assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID, mImsStateTracker.getSubId());
+ }
+
+ @Test
+ @SmallTest
+ public void testStartWithInvalidSubId() {
+ mImsStateTracker.start(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID, mImsStateTracker.getSubId());
+ assertTrue(isImsStateUnavailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testStart() throws ImsException {
+ mImsStateTracker.start(SUB_1);
+
+ assertEquals(SUB_1, mImsStateTracker.getSubId());
+ assertTrue(isImsStateInit());
+ verify(mMmTelManager).registerImsStateCallback(
+ any(Executor.class), any(ImsStateCallback.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartWithDifferentSubId() throws ImsException {
+ mImsStateTracker.start(SUB_1);
+
+ assertEquals(SUB_1, mImsStateTracker.getSubId());
+ assertTrue(isImsStateInit());
+
+ mImsStateTracker.start(SUB_2);
+
+ assertEquals(SUB_2, mImsStateTracker.getSubId());
+ assertTrue(isImsStateInit());
+ verify(mMmTelManager).registerImsStateCallback(
+ any(Executor.class), any(ImsStateCallback.class));
+ verify(mMmTelManager).unregisterImsStateCallback(
+ any(ImsStateCallback.class));
+ verify(mMmTelManager2).registerImsStateCallback(
+ any(Executor.class), any(ImsStateCallback.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartWithSameSubId() throws ImsException {
+ mImsStateTracker.start(SUB_1);
+
+ assertEquals(SUB_1, mImsStateTracker.getSubId());
+ assertTrue(isImsStateInit());
+
+ mImsStateTracker.start(SUB_1);
+
+ assertEquals(SUB_1, mImsStateTracker.getSubId());
+ assertTrue(isImsStateInit());
+ verify(mMmTelManager).registerImsStateCallback(
+ any(Executor.class), any(ImsStateCallback.class));
+ verify(mMmTelManager, never()).unregisterImsStateCallback(
+ any(ImsStateCallback.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartWhenRegisteringCallbacksThrowException() throws ImsException {
+ doAnswer((invocation) -> {
+ throw new ImsException("Intended exception for ImsStateCallback.");
+ }).when(mMmTelManager).registerImsStateCallback(
+ any(Executor.class), any(ImsStateCallback.class));
+
+ mImsStateTracker.start(SUB_1);
+
+ assertEquals(SUB_1, mImsStateTracker.getSubId());
+
+ mImsStateTracker.start(SUB_2);
+
+ assertEquals(SUB_2, mImsStateTracker.getSubId());
+
+ verify(mMmTelManager, never()).unregisterImsStateCallback(
+ any(ImsStateCallback.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartAfterUnavailableWithReasonSubscriptionInactive() throws ImsException {
+ ImsStateCallback callback = setUpImsStateCallback();
+ callback.onUnavailable(ImsStateCallback.REASON_SUBSCRIPTION_INACTIVE);
+
+ mImsStateTracker.start(SUB_1);
+
+ assertTrue(isImsStateInit());
+ // One is invoked in setUpImsStateCallback and the other is invoked in start(int).
+ verify(mMmTelManager, times(2)).registerImsStateCallback(
+ any(Executor.class), any(ImsStateCallback.class));
+ // ImsStateCallback has already been set to null when onUnavailable is called.
+ verify(mMmTelManager, never()).unregisterImsStateCallback(
+ any(ImsStateCallback.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateServiceStateBeforeAddingListener() {
+ mImsStateTracker.updateServiceState(mServiceState);
+ mImsStateTracker.addServiceStateListener(mServiceStateListener);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verify(mServiceStateListener).onServiceStateUpdated(eq(mServiceState));
+
+ mImsStateTracker.removeServiceStateListener(mServiceStateListener);
+ ServiceState ss = Mockito.mock(ServiceState.class);
+ mImsStateTracker.updateServiceState(ss);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verifyNoMoreInteractions(mServiceStateListener);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateServiceStateAfterAddingListener() {
+ mImsStateTracker.addServiceStateListener(mServiceStateListener);
+ mImsStateTracker.updateServiceState(mServiceState);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verify(mServiceStateListener).onServiceStateUpdated(eq(mServiceState));
+
+ mImsStateTracker.removeServiceStateListener(mServiceStateListener);
+ ServiceState ss = Mockito.mock(ServiceState.class);
+ mImsStateTracker.updateServiceState(ss);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verifyNoMoreInteractions(mServiceStateListener);
+ }
+
+ @Test
+ @SmallTest
+ public void testAddAndRemoveServiceStateListener() {
+ mImsStateTracker.updateServiceState(mServiceState);
+ mImsStateTracker.addServiceStateListener(mServiceStateListener);
+ mImsStateTracker.removeServiceStateListener(mServiceStateListener);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verify(mServiceStateListener, never()).onServiceStateUpdated(eq(mServiceState));
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateBarringInfoBeforeAddingListener() {
+ mImsStateTracker.updateBarringInfo(mBarringInfo);
+ mImsStateTracker.addBarringInfoListener(mBarringInfoListener);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verify(mBarringInfoListener).onBarringInfoUpdated(eq(mBarringInfo));
+
+ mImsStateTracker.removeBarringInfoListener(mBarringInfoListener);
+ BarringInfo bi = new BarringInfo();
+ mImsStateTracker.updateBarringInfo(bi);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verifyNoMoreInteractions(mBarringInfoListener);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateBarringInfoAfterAddingListener() {
+ mImsStateTracker.addBarringInfoListener(mBarringInfoListener);
+ mImsStateTracker.updateBarringInfo(mBarringInfo);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verify(mBarringInfoListener).onBarringInfoUpdated(eq(mBarringInfo));
+
+ mImsStateTracker.removeBarringInfoListener(mBarringInfoListener);
+ BarringInfo bi = new BarringInfo();
+ mImsStateTracker.updateBarringInfo(bi);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verifyNoMoreInteractions(mBarringInfoListener);
+ }
+
+ @Test
+ @SmallTest
+ public void testAddAndRemoveBarringInfoListener() {
+ mImsStateTracker.updateBarringInfo(mBarringInfo);
+ mImsStateTracker.addBarringInfoListener(mBarringInfoListener);
+ mImsStateTracker.removeBarringInfoListener(mBarringInfoListener);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verify(mBarringInfoListener, never()).onBarringInfoUpdated(eq(mBarringInfo));
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifyImsStateCallbackOnAvailable() throws ImsException {
+ ImsStateCallback callback = setUpImsStateCallback();
+ callback.onAvailable();
+
+ assertTrue(mImsStateTracker.isMmTelFeatureAvailable());
+ assertFalse(mImsStateTracker.isImsStateReady());
+ verify(mMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class), any(RegistrationManager.RegistrationCallback.class));
+ verify(mMmTelManager).registerMmTelCapabilityCallback(
+ any(Executor.class), any(ImsMmTelManager.CapabilityCallback.class));
+ verify(mImsStateListener).onImsMmTelFeatureAvailableChanged();
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifyImsStateCallbackOnUnavailableWithReasonUnknownPermanentError()
+ throws ImsException {
+ ImsStateCallback callback = setUpImsStateCallback();
+ callback.onUnavailable(ImsStateCallback.REASON_UNKNOWN_PERMANENT_ERROR);
+
+ assertTrue(isImsStateUnavailable());
+ assertTrue(mImsStateTracker.isImsStateReady());
+ verify(mImsStateListener).onImsMmTelFeatureAvailableChanged();
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifyImsStateCallbackOnUnavailableWithReasonNoImsServiceConfigured()
+ throws ImsException {
+ ImsStateCallback callback = setUpImsStateCallback();
+ callback.onUnavailable(ImsStateCallback.REASON_NO_IMS_SERVICE_CONFIGURED);
+
+ assertTrue(isImsStateUnavailable());
+ assertTrue(mImsStateTracker.isImsStateReady());
+ verify(mImsStateListener).onImsMmTelFeatureAvailableChanged();
+ }
+
+ @Test
+ public void testNotifyImsStateCallbackOnUnavailableWithReasonUnknownTemporaryError()
+ throws ImsException {
+ ImsStateCallback callback = setUpImsStateCallback();
+ callback.onUnavailable(ImsStateCallback.REASON_UNKNOWN_TEMPORARY_ERROR);
+
+ assertFalse(mImsStateTracker.isMmTelFeatureAvailable());
+ assertFalse(isImsStateUnavailable());
+ assertFalse(mImsStateTracker.isImsStateReady());
+
+ waitForHandlerActionDelayed(mImsStateTracker.getHandler(),
+ ImsStateTracker.MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS,
+ ImsStateTracker.MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS + TIMEOUT_MS);
+
+ assertFalse(mImsStateTracker.isMmTelFeatureAvailable());
+ assertTrue(isImsStateUnavailable());
+ assertTrue(mImsStateTracker.isImsStateReady());
+ verify(mImsStateListener).onImsMmTelFeatureAvailableChanged();
+ }
+
+ @Test
+ public void testNotifyImsStateCallbackOnUnavailableWithReasonImsServiceNotReady()
+ throws ImsException {
+ ImsStateCallback callback = setUpImsStateCallback();
+ callback.onUnavailable(ImsStateCallback.REASON_IMS_SERVICE_NOT_READY);
+
+ assertFalse(mImsStateTracker.isMmTelFeatureAvailable());
+ assertFalse(isImsStateUnavailable());
+ assertFalse(mImsStateTracker.isImsStateReady());
+
+ waitForHandlerActionDelayed(mImsStateTracker.getHandler(),
+ ImsStateTracker.MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS,
+ ImsStateTracker.MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS + TIMEOUT_MS);
+
+ assertFalse(mImsStateTracker.isMmTelFeatureAvailable());
+ assertTrue(isImsStateUnavailable());
+ assertTrue(mImsStateTracker.isImsStateReady());
+ verify(mImsStateListener).onImsMmTelFeatureAvailableChanged();
+ }
+
+ @Test
+ public void testNotifyImsStateCallbackOnUnavailableWithReasonImsServiceDisconnected()
+ throws ImsException {
+ ImsStateCallback callback = setUpImsStateCallback();
+ callback.onUnavailable(ImsStateCallback.REASON_IMS_SERVICE_DISCONNECTED);
+
+ assertFalse(mImsStateTracker.isMmTelFeatureAvailable());
+ assertFalse(isImsStateUnavailable());
+ assertFalse(mImsStateTracker.isImsStateReady());
+
+ waitForHandlerActionDelayed(mImsStateTracker.getHandler(),
+ ImsStateTracker.MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS,
+ ImsStateTracker.MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS + TIMEOUT_MS);
+
+ assertFalse(mImsStateTracker.isMmTelFeatureAvailable());
+ assertTrue(isImsStateUnavailable());
+ assertTrue(mImsStateTracker.isImsStateReady());
+ verify(mMmTelManager, never()).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mMmTelManager, never()).unregisterMmTelCapabilityCallback(
+ any(ImsMmTelManager.CapabilityCallback.class));
+ verify(mImsStateListener).onImsMmTelFeatureAvailableChanged();
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifyImsStateCallbackOnUnavailableWithReasonSubscriptionInactive()
+ throws ImsException {
+ ImsStateCallback callback = setUpImsStateCallback();
+ callback.onUnavailable(ImsStateCallback.REASON_SUBSCRIPTION_INACTIVE);
+
+ assertFalse(mImsStateTracker.isMmTelFeatureAvailable());
+ assertTrue(isImsStateUnavailable());
+ assertTrue(mImsStateTracker.isImsStateReady());
+ verify(mMmTelManager, never()).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mMmTelManager, never()).unregisterMmTelCapabilityCallback(
+ any(ImsMmTelManager.CapabilityCallback.class));
+ verify(mImsStateListener).onImsMmTelFeatureAvailableChanged();
+ }
+
+ @Test
+ public void testNotifyImsStateCallbackOnAvailableUnavailableWithReasonImsServiceDisconnected()
+ throws ImsException {
+ ImsStateCallback callback = setUpImsStateCallback();
+ callback.onAvailable();
+ callback.onUnavailable(ImsStateCallback.REASON_IMS_SERVICE_DISCONNECTED);
+
+ assertFalse(mImsStateTracker.isMmTelFeatureAvailable());
+ assertFalse(isImsStateUnavailable());
+ assertFalse(mImsStateTracker.isImsStateReady());
+
+ waitForHandlerActionDelayed(mImsStateTracker.getHandler(),
+ ImsStateTracker.MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS,
+ ImsStateTracker.MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS + TIMEOUT_MS);
+
+ assertFalse(mImsStateTracker.isMmTelFeatureAvailable());
+ assertTrue(isImsStateUnavailable());
+ assertTrue(mImsStateTracker.isImsStateReady());
+ verify(mMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class), any(RegistrationManager.RegistrationCallback.class));
+ verify(mMmTelManager).registerMmTelCapabilityCallback(
+ any(Executor.class), any(ImsMmTelManager.CapabilityCallback.class));
+ verify(mMmTelManager).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mMmTelManager).unregisterMmTelCapabilityCallback(
+ any(ImsMmTelManager.CapabilityCallback.class));
+ verify(mImsStateListener, times(2)).onImsMmTelFeatureAvailableChanged();
+ }
+
+ @Test
+ public void testNotifyImsStateCallbackOnUnavailableAvailableWithReasonImsServiceDisconnected()
+ throws ImsException {
+ ImsStateCallback callback = setUpImsStateCallback();
+ callback.onUnavailable(ImsStateCallback.REASON_IMS_SERVICE_DISCONNECTED);
+ callback.onAvailable();
+
+ assertTrue(mImsStateTracker.isMmTelFeatureAvailable());
+ assertFalse(isImsStateUnavailable());
+ assertFalse(mImsStateTracker.isImsStateReady());
+
+ waitForHandlerActionDelayed(mImsStateTracker.getHandler(),
+ ImsStateTracker.MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS,
+ ImsStateTracker.MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS + TIMEOUT_MS);
+
+ assertTrue(mImsStateTracker.isMmTelFeatureAvailable());
+ assertFalse(isImsStateUnavailable());
+ assertFalse(mImsStateTracker.isImsStateReady());
+ verify(mMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class), any(RegistrationManager.RegistrationCallback.class));
+ verify(mMmTelManager).registerMmTelCapabilityCallback(
+ any(Executor.class), any(ImsMmTelManager.CapabilityCallback.class));
+ verify(mMmTelManager, never()).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mMmTelManager, never()).unregisterMmTelCapabilityCallback(
+ any(ImsMmTelManager.CapabilityCallback.class));
+ verify(mImsStateListener).onImsMmTelFeatureAvailableChanged();
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifyImsStateCallbackOnAvailableUnavailableWithReasonSubscriptionInactive()
+ throws ImsException {
+ ImsStateCallback callback = setUpImsStateCallback();
+ callback.onAvailable();
+ callback.onUnavailable(ImsStateCallback.REASON_SUBSCRIPTION_INACTIVE);
+
+ assertFalse(mImsStateTracker.isMmTelFeatureAvailable());
+ assertTrue(isImsStateUnavailable());
+ assertTrue(mImsStateTracker.isImsStateReady());
+ verify(mMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class), any(RegistrationManager.RegistrationCallback.class));
+ verify(mMmTelManager).registerMmTelCapabilityCallback(
+ any(Executor.class), any(ImsMmTelManager.CapabilityCallback.class));
+ verify(mMmTelManager).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mMmTelManager).unregisterMmTelCapabilityCallback(
+ any(ImsMmTelManager.CapabilityCallback.class));
+ verify(mImsStateListener, times(2)).onImsMmTelFeatureAvailableChanged();
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifyImsRegistrationCallbackOnRegistered() throws ImsException {
+ RegistrationManager.RegistrationCallback callback = setUpImsRegistrationCallback();
+ callback.onRegistered(new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE).build());
+
+ // It's false because the MMTEL capabilities are not updated yet.
+ assertFalse(mImsStateTracker.isImsStateReady());
+ assertTrue(mImsStateTracker.isImsRegistered());
+ assertFalse(mImsStateTracker.isImsRegisteredOverWlan());
+ assertFalse(mImsStateTracker.isImsRegisteredOverCrossSim());
+ assertEquals(AccessNetworkType.EUTRAN, mImsStateTracker.getImsAccessNetworkType());
+
+ callback.onRegistered(new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_NR).build());
+
+ assertFalse(mImsStateTracker.isImsStateReady());
+ assertTrue(mImsStateTracker.isImsRegistered());
+ assertFalse(mImsStateTracker.isImsRegisteredOverWlan());
+ assertFalse(mImsStateTracker.isImsRegisteredOverCrossSim());
+ assertEquals(AccessNetworkType.NGRAN, mImsStateTracker.getImsAccessNetworkType());
+
+ callback.onRegistered(new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN).build());
+
+ assertFalse(mImsStateTracker.isImsStateReady());
+ assertTrue(mImsStateTracker.isImsRegistered());
+ assertTrue(mImsStateTracker.isImsRegisteredOverWlan());
+ assertFalse(mImsStateTracker.isImsRegisteredOverCrossSim());
+ assertEquals(AccessNetworkType.IWLAN, mImsStateTracker.getImsAccessNetworkType());
+
+ callback.onRegistered(new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM).build());
+
+ assertFalse(mImsStateTracker.isImsStateReady());
+ assertTrue(mImsStateTracker.isImsRegistered());
+ assertTrue(mImsStateTracker.isImsRegisteredOverWlan());
+ assertTrue(mImsStateTracker.isImsRegisteredOverCrossSim());
+ assertEquals(AccessNetworkType.IWLAN, mImsStateTracker.getImsAccessNetworkType());
+
+ callback.onRegistered(new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_NONE).build());
+
+ assertFalse(mImsStateTracker.isImsStateReady());
+ assertTrue(mImsStateTracker.isImsRegistered());
+ assertFalse(mImsStateTracker.isImsRegisteredOverWlan());
+ assertFalse(mImsStateTracker.isImsRegisteredOverCrossSim());
+ assertEquals(AccessNetworkType.UNKNOWN, mImsStateTracker.getImsAccessNetworkType());
+
+ verify(mImsStateListener, times(5)).onImsRegistrationStateChanged();
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifyImsRegistrationCallbackOnUnregistered() throws ImsException {
+ RegistrationManager.RegistrationCallback callback = setUpImsRegistrationCallback();
+ callback.onRegistered(new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE).build());
+
+ // It's false because the MMTEL capabilities are not updated yet.
+ assertFalse(mImsStateTracker.isImsStateReady());
+ assertTrue(mImsStateTracker.isImsRegistered());
+ assertFalse(mImsStateTracker.isImsRegisteredOverWlan());
+ assertEquals(AccessNetworkType.EUTRAN, mImsStateTracker.getImsAccessNetworkType());
+
+ callback.onUnregistered(new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 0, null));
+
+ // When IMS is unregistered, the MMTEL capability is also reset.
+ assertTrue(mImsStateTracker.isImsStateReady());
+ assertFalse(mImsStateTracker.isImsRegistered());
+ assertFalse(mImsStateTracker.isImsRegisteredOverWlan());
+ assertEquals(AccessNetworkType.UNKNOWN, mImsStateTracker.getImsAccessNetworkType());
+
+ verify(mImsStateListener, times(2)).onImsRegistrationStateChanged();
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifyMmTelCapabilityCallbackOnCapabilitiesStatusChanged() throws ImsException {
+ ImsMmTelManager.CapabilityCallback callback = setUpMmTelCapabilityCallback();
+
+ assertFalse(mImsStateTracker.isImsVoiceCapable());
+ assertFalse(mImsStateTracker.isImsVideoCapable());
+ assertFalse(mImsStateTracker.isImsSmsCapable());
+ assertFalse(mImsStateTracker.isImsUtCapable());
+
+ MmTelCapabilities capabilities = new MmTelCapabilities(
+ MmTelCapabilities.CAPABILITY_TYPE_VOICE
+ | MmTelCapabilities.CAPABILITY_TYPE_VIDEO
+ | MmTelCapabilities.CAPABILITY_TYPE_SMS
+ | MmTelCapabilities.CAPABILITY_TYPE_UT
+ );
+ callback.onCapabilitiesStatusChanged(capabilities);
+
+ assertTrue(mImsStateTracker.isImsStateReady());
+ assertTrue(mImsStateTracker.isImsVoiceCapable());
+ assertTrue(mImsStateTracker.isImsVideoCapable());
+ assertTrue(mImsStateTracker.isImsSmsCapable());
+ assertTrue(mImsStateTracker.isImsUtCapable());
+
+ capabilities = new MmTelCapabilities();
+ callback.onCapabilitiesStatusChanged(capabilities);
+
+ assertTrue(mImsStateTracker.isImsStateReady());
+ assertFalse(mImsStateTracker.isImsVoiceCapable());
+ assertFalse(mImsStateTracker.isImsVideoCapable());
+ assertFalse(mImsStateTracker.isImsSmsCapable());
+ assertFalse(mImsStateTracker.isImsUtCapable());
+
+ verify(mImsStateListener, times(2)).onImsMmTelCapabilitiesChanged();
+ }
+
+ @Test
+ @SmallTest
+ public void testAddImsStateListenerWhenImsStateReady() throws ImsException {
+ ImsMmTelManager.CapabilityCallback callback = setUpMmTelCapabilityCallback();
+
+ MmTelCapabilities capabilities = new MmTelCapabilities(
+ MmTelCapabilities.CAPABILITY_TYPE_VOICE
+ | MmTelCapabilities.CAPABILITY_TYPE_VIDEO
+ | MmTelCapabilities.CAPABILITY_TYPE_SMS
+ | MmTelCapabilities.CAPABILITY_TYPE_UT
+ );
+ callback.onCapabilitiesStatusChanged(capabilities);
+
+ ImsStateTracker.ImsStateListener listener =
+ Mockito.mock(ImsStateTracker.ImsStateListener.class);
+ mImsStateTracker.addImsStateListener(listener);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verify(listener).onImsMmTelFeatureAvailableChanged();
+ verify(listener).onImsRegistrationStateChanged();
+ verify(listener).onImsMmTelCapabilitiesChanged();
+ }
+
+ @Test
+ @SmallTest
+ public void testAddAndRemoveImsStateListenerWhenImsStateReady() throws ImsException {
+ ImsMmTelManager.CapabilityCallback callback = setUpMmTelCapabilityCallback();
+
+ MmTelCapabilities capabilities = new MmTelCapabilities(
+ MmTelCapabilities.CAPABILITY_TYPE_VOICE
+ | MmTelCapabilities.CAPABILITY_TYPE_VIDEO
+ | MmTelCapabilities.CAPABILITY_TYPE_SMS
+ | MmTelCapabilities.CAPABILITY_TYPE_UT
+ );
+ callback.onCapabilitiesStatusChanged(capabilities);
+
+ Handler handler = new Handler(mLooper);
+ ImsStateTracker.ImsStateListener listener =
+ Mockito.mock(ImsStateTracker.ImsStateListener.class);
+ handler.post(() -> {
+ mImsStateTracker.addImsStateListener(listener);
+ mImsStateTracker.removeImsStateListener(listener);
+ });
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ verify(listener, never()).onImsMmTelFeatureAvailableChanged();
+ verify(listener, never()).onImsRegistrationStateChanged();
+ verify(listener, never()).onImsMmTelCapabilitiesChanged();
+ }
+
+ private ImsStateCallback setUpImsStateCallback() throws ImsException {
+ mImsStateTracker.start(SUB_1);
+ mImsStateTracker.addImsStateListener(mImsStateListener);
+ waitForHandlerAction(mImsStateTracker.getHandler(), TIMEOUT_MS);
+
+ assertEquals(SUB_1, mImsStateTracker.getSubId());
+ assertFalse(mImsStateTracker.isMmTelFeatureAvailable());
+ ArgumentCaptor<ImsStateCallback> callbackCaptor =
+ ArgumentCaptor.forClass(ImsStateCallback.class);
+ verify(mMmTelManager).registerImsStateCallback(
+ any(Executor.class), callbackCaptor.capture());
+
+ ImsStateCallback imsStateCallback = callbackCaptor.getValue();
+ assertNotNull(imsStateCallback);
+ return imsStateCallback;
+ }
+
+ private RegistrationManager.RegistrationCallback setUpImsRegistrationCallback()
+ throws ImsException {
+ ImsStateCallback imsStateCallback = setUpImsStateCallback();
+ imsStateCallback.onAvailable();
+
+ assertTrue(mImsStateTracker.isMmTelFeatureAvailable());
+ ArgumentCaptor<RegistrationManager.RegistrationCallback> callbackCaptor =
+ ArgumentCaptor.forClass(RegistrationManager.RegistrationCallback.class);
+ verify(mMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class), callbackCaptor.capture());
+
+ RegistrationManager.RegistrationCallback registrationCallback = callbackCaptor.getValue();
+ assertNotNull(registrationCallback);
+ return registrationCallback;
+ }
+
+ private ImsMmTelManager.CapabilityCallback setUpMmTelCapabilityCallback()
+ throws ImsException {
+ RegistrationManager.RegistrationCallback registrationCallback =
+ setUpImsRegistrationCallback();
+ registrationCallback.onRegistered(new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE).build());
+
+ assertTrue(mImsStateTracker.isMmTelFeatureAvailable());
+ // It's false because the MMTEL capabilities are not updated.
+ assertFalse(mImsStateTracker.isImsStateReady());
+ assertTrue(mImsStateTracker.isImsRegistered());
+ assertFalse(mImsStateTracker.isImsRegisteredOverWlan());
+ assertEquals(AccessNetworkType.EUTRAN, mImsStateTracker.getImsAccessNetworkType());
+ ArgumentCaptor<ImsMmTelManager.CapabilityCallback> callbackCaptor =
+ ArgumentCaptor.forClass(ImsMmTelManager.CapabilityCallback.class);
+ verify(mMmTelManager).registerMmTelCapabilityCallback(
+ any(Executor.class), callbackCaptor.capture());
+
+ ImsMmTelManager.CapabilityCallback capabilityCallback = callbackCaptor.getValue();
+ assertNotNull(capabilityCallback);
+ return capabilityCallback;
+ }
+
+ private boolean isImsStateUnavailable() {
+ return mImsStateTracker.isImsStateReady()
+ && !mImsStateTracker.isImsRegistered()
+ && !mImsStateTracker.isMmTelFeatureAvailable()
+ && !mImsStateTracker.isImsVoiceCapable()
+ && !mImsStateTracker.isImsVideoCapable()
+ && !mImsStateTracker.isImsSmsCapable()
+ && !mImsStateTracker.isImsUtCapable()
+ && (AccessNetworkType.UNKNOWN == mImsStateTracker.getImsAccessNetworkType());
+ }
+
+ private boolean isImsStateInit() {
+ return !mImsStateTracker.isImsStateReady()
+ && !mImsStateTracker.isImsRegistered()
+ && !mImsStateTracker.isMmTelFeatureAvailable()
+ && !mImsStateTracker.isImsVoiceCapable()
+ && !mImsStateTracker.isImsVideoCapable()
+ && !mImsStateTracker.isImsSmsCapable()
+ && !mImsStateTracker.isImsUtCapable()
+ && (AccessNetworkType.UNKNOWN == mImsStateTracker.getImsAccessNetworkType());
+ }
+
+ private void waitForHandlerAction(Handler h, long timeoutMillis) {
+ waitForHandlerActionDelayed(h, 0, timeoutMillis);
+ }
+
+ private void waitForHandlerActionDelayed(Handler h, long delayMillis, long timeoutMillis) {
+ final CountDownLatch lock = new CountDownLatch(1);
+ h.postDelayed(lock::countDown, delayMillis);
+ while (lock.getCount() > 0) {
+ try {
+ lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
new file mode 100644
index 0000000..4dd1f3c
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_UT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.CancellationSignal;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
+import android.telephony.DisconnectCause;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelector;
+import android.telephony.EmergencyRegResult;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.WwanSelectorCallback;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.util.Log;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+
+/**
+ * Unit tests for DomainSelectorBase.
+ */
+@RunWith(AndroidJUnit4.class)
+public class NormalCallDomainSelectorTest {
+ private static final String TAG = "NormalCallDomainSelectorTest";
+
+ private static final int SLOT_ID = 0;
+ private static final int SUB_ID_1 = 1;
+ private static final int SUB_ID_2 = 2;
+ private static final String TEST_CALLID = "01234";
+
+ private HandlerThread mHandlerThread;
+ private NormalCallDomainSelector mNormalCallDomainSelector;
+
+ @Mock private Context mMockContext;
+ @Mock private CarrierConfigManager mMockCarrierConfigMgr;
+ @Mock private ImsManager mMockImsManager;
+ @Mock private ImsMmTelManager mMockMmTelManager;
+ @Mock private ImsStateTracker mMockImsStateTracker;
+ @Mock private DomainSelectorBase.DestroyListener mMockDestroyListener;
+ @Mock private TelecomManager mMockTelecomManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ doReturn(Context.TELEPHONY_IMS_SERVICE).when(mMockContext)
+ .getSystemServiceName(ImsManager.class);
+ doReturn(mMockImsManager).when(mMockContext)
+ .getSystemService(Context.TELEPHONY_IMS_SERVICE);
+
+ doReturn(Context.CARRIER_CONFIG_SERVICE).when(mMockContext)
+ .getSystemServiceName(CarrierConfigManager.class);
+ doReturn(mMockCarrierConfigMgr).when(mMockContext)
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+
+ doReturn(Context.TELECOM_SERVICE).when(mMockContext)
+ .getSystemServiceName(TelecomManager.class);
+ doReturn(mMockTelecomManager).when(mMockContext)
+ .getSystemService(Context.TELECOM_SERVICE);
+
+ doReturn(mMockMmTelManager).when(mMockImsManager).getImsMmTelManager(SUB_ID_1);
+ doReturn(mMockMmTelManager).when(mMockImsManager).getImsMmTelManager(SUB_ID_2);
+ doNothing().when(mMockImsStateTracker).removeServiceStateListener(any());
+ doNothing().when(mMockImsStateTracker).removeImsStateListener(any());
+ doReturn(true).when(mMockImsStateTracker).isMmTelFeatureAvailable();
+
+ // Set up the looper if it does not exist on the test thread.
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mHandlerThread = new HandlerThread(
+ NormalCallDomainSelectorTest.class.getSimpleName());
+ mHandlerThread.start();
+
+ mNormalCallDomainSelector = new NormalCallDomainSelector(mMockContext, SLOT_ID, SUB_ID_1,
+ mHandlerThread.getLooper(), mMockImsStateTracker, mMockDestroyListener);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mHandlerThread != null) {
+ mHandlerThread.quit();
+ }
+ }
+
+ private void initialize(ServiceState serviceState, boolean isImsRegistered,
+ boolean isImsRegisteredOverWlan, boolean isImsVoiceCapable,
+ boolean isImsVideoCapable) {
+ if (serviceState != null) mNormalCallDomainSelector.onServiceStateUpdated(serviceState);
+ doReturn(isImsRegistered).when(mMockImsStateTracker).isImsStateReady();
+ doReturn(isImsRegistered).when(mMockImsStateTracker).isImsRegistered();
+ doReturn(isImsVoiceCapable).when(mMockImsStateTracker).isImsVoiceCapable();
+ doReturn(isImsVideoCapable).when(mMockImsStateTracker).isImsVideoCapable();
+ doReturn(isImsRegisteredOverWlan).when(mMockImsStateTracker).isImsRegisteredOverWlan();
+ mNormalCallDomainSelector.onImsRegistrationStateChanged();
+ mNormalCallDomainSelector.onImsMmTelCapabilitiesChanged();
+ }
+
+ @Test
+ public void testInit() {
+ assertEquals(SLOT_ID, mNormalCallDomainSelector.getSlotId());
+ assertEquals(SUB_ID_1, mNormalCallDomainSelector.getSubId());
+ }
+
+ @Test
+ public void testSelectDomainInputParams() {
+ MockTransportSelectorCallback transportSelectorCallback =
+ new MockTransportSelectorCallback();
+
+ DomainSelectionService.SelectionAttributes attributes =
+ new DomainSelectionService.SelectionAttributes.Builder(
+ SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
+ .setCallId(TEST_CALLID)
+ .setEmergency(false)
+ .setVideoCall(true)
+ .setExitedFromAirplaneMode(false)
+ .build();
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
+
+ // Case 1: null inputs
+ try {
+ mNormalCallDomainSelector.selectDomain(null, null);
+ } catch (Exception e) {
+ fail("Invalid input params not handled." + e.getMessage());
+ }
+
+ // Case 2: null TransportSelectorCallback
+ try {
+ mNormalCallDomainSelector.selectDomain(attributes, null);
+ } catch (Exception e) {
+ fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
+ }
+
+ // Case 3: null SelectionAttributes
+ transportSelectorCallback.mSelectionTerminated = false;
+ try {
+ mNormalCallDomainSelector.selectDomain(null, transportSelectorCallback);
+ } catch (Exception e) {
+ fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
+ }
+
+ assertTrue(transportSelectorCallback
+ .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE));
+
+ // Case 4: Invalid Subscription-id
+ attributes = new DomainSelectionService.SelectionAttributes.Builder(
+ SLOT_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID, SELECTOR_TYPE_CALLING)
+ .setCallId(TEST_CALLID)
+ .setEmergency(false)
+ .setVideoCall(true)
+ .setExitedFromAirplaneMode(false)
+ .build();
+ try {
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ } catch (Exception e) {
+ fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
+ }
+
+ assertTrue(transportSelectorCallback
+ .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE));
+
+ // Case 5: Invalid SELECTOR_TYPE
+ attributes =
+ new DomainSelectionService.SelectionAttributes.Builder(
+ SLOT_ID, SUB_ID_1, SELECTOR_TYPE_UT)
+ .setCallId(TEST_CALLID)
+ .setEmergency(false)
+ .setVideoCall(true)
+ .setExitedFromAirplaneMode(false)
+ .build();
+ try {
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ } catch (Exception e) {
+ fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
+ }
+
+ assertTrue(transportSelectorCallback
+ .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE));
+
+ // Case 6: Emergency Call
+ attributes = new DomainSelectionService.SelectionAttributes.Builder(
+ SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
+ .setCallId(TEST_CALLID)
+ .setEmergency(true)
+ .setVideoCall(true)
+ .setExitedFromAirplaneMode(false)
+ .build();
+ try {
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ } catch (Exception e) {
+ fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
+ }
+
+ assertTrue(transportSelectorCallback
+ .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE));
+ }
+
+ @Test
+ public void testOutOfService() {
+ MockTransportSelectorCallback transportSelectorCallback =
+ new MockTransportSelectorCallback();
+ DomainSelectionService.SelectionAttributes attributes =
+ new DomainSelectionService.SelectionAttributes.Builder(
+ SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
+ .setCallId(TEST_CALLID)
+ .setEmergency(false)
+ .setVideoCall(true)
+ .setExitedFromAirplaneMode(false)
+ .build();
+ ServiceState serviceState = new ServiceState();
+ serviceState.setStateOutOfService();
+ initialize(serviceState, false, false, false, false);
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ assertTrue(transportSelectorCallback
+ .verifyOnSelectionTerminated(DisconnectCause.OUT_OF_SERVICE));
+ }
+
+ @Test
+ public void testDomainSelection() {
+ MockTransportSelectorCallback transportSelectorCallback =
+ new MockTransportSelectorCallback();
+ DomainSelectionService.SelectionAttributes attributes =
+ new DomainSelectionService.SelectionAttributes.Builder(
+ SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
+ .setCallId(TEST_CALLID)
+ .setEmergency(false)
+ .setVideoCall(false)
+ .setExitedFromAirplaneMode(false)
+ .build();
+
+ // Case 1: WLAN
+ ServiceState serviceState = new ServiceState();
+ serviceState.setState(ServiceState.STATE_IN_SERVICE);
+ initialize(serviceState, true, true, true, true);
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ assertTrue(transportSelectorCallback.verifyOnWlanSelected());
+
+ // Case 2: 5G
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ initialize(serviceState, true, false, true, true);
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+ assertTrue(transportSelectorCallback
+ .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS));
+
+ // Case 3: PS -> CS redial
+ ImsReasonInfo imsReasonInfo = new ImsReasonInfo();
+ imsReasonInfo.mCode = ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED;
+ attributes = new DomainSelectionService.SelectionAttributes.Builder(
+ SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
+ .setCallId(TEST_CALLID)
+ .setEmergency(false)
+ .setVideoCall(false)
+ .setExitedFromAirplaneMode(false)
+ .setPsDisconnectCause(imsReasonInfo)
+ .build();
+ mNormalCallDomainSelector.reselectDomain(attributes);
+ assertTrue(transportSelectorCallback
+ .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS));
+
+ // Case 4: CS call
+ NetworkRegistrationInfo nwRegistrationInfo = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
+ AccessNetworkConstants.AccessNetworkType.UTRAN, 0, false,
+ null, null, null, false, 0, 0, 0);
+ serviceState.addNetworkRegistrationInfo(nwRegistrationInfo);
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ initialize(serviceState, false, false, false, false);
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+ assertTrue(transportSelectorCallback
+ .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS));
+
+ //Case 5: Backup calling
+ serviceState.setStateOutOfService();
+ initialize(serviceState, true, true, true, true);
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ assertTrue(transportSelectorCallback.verifyOnWlanSelected());
+ }
+
+ @Test
+ public void testWPSCallDomainSelection() {
+ MockTransportSelectorCallback transportSelectorCallback =
+ new MockTransportSelectorCallback();
+ DomainSelectionService.SelectionAttributes attributes =
+ new DomainSelectionService.SelectionAttributes.Builder(
+ SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
+ .setNumber("*272121")
+ .setCallId(TEST_CALLID)
+ .setEmergency(false)
+ .setVideoCall(false)
+ .setExitedFromAirplaneMode(false)
+ .build();
+
+ //Case 1: WPS not supported by IMS
+ PersistableBundle config = new PersistableBundle();
+ config.putBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, false);
+ doReturn(config).when(mMockCarrierConfigMgr).getConfigForSubId(SUB_ID_1);
+ ServiceState serviceState = new ServiceState();
+ serviceState.setState(ServiceState.STATE_IN_SERVICE);
+ initialize(serviceState, true, true, true, true);
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+ assertTrue(transportSelectorCallback
+ .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS));
+
+ //Case 2: WPS supported by IMS and WLAN registered
+ config.putBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
+ serviceState.setState(ServiceState.STATE_IN_SERVICE);
+ initialize(serviceState, true, true, true, true);
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ assertTrue(transportSelectorCallback.verifyOnWlanSelected());
+
+ //Case 2: WPS supported by IMS and LTE registered
+ config.putBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
+ serviceState.setState(ServiceState.STATE_IN_SERVICE);
+ initialize(serviceState, true, false, true, true);
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+ assertTrue(transportSelectorCallback
+ .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS));
+ }
+
+ @Test
+ public void testTtyCallDomainSelection() {
+ MockTransportSelectorCallback transportSelectorCallback =
+ new MockTransportSelectorCallback();
+ DomainSelectionService.SelectionAttributes attributes =
+ new DomainSelectionService.SelectionAttributes.Builder(
+ SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
+ .setCallId(TEST_CALLID)
+ .setEmergency(false)
+ .setVideoCall(false)
+ .setExitedFromAirplaneMode(false)
+ .build();
+
+ //Case 1: TTY not supported by IMS and TTY enabled
+ doReturn(TelecomManager.TTY_MODE_FULL).when(mMockTelecomManager).getCurrentTtyMode();
+ PersistableBundle config = new PersistableBundle();
+ config.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, false);
+ doReturn(config).when(mMockCarrierConfigMgr).getConfigForSubId(SUB_ID_1);
+ ServiceState serviceState = new ServiceState();
+ serviceState.setState(ServiceState.STATE_IN_SERVICE);
+ initialize(serviceState, true, false, true, true);
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+ assertTrue(transportSelectorCallback
+ .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS));
+
+ //Case 2: TTY supported by IMS and TTY enabled
+ config.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+ assertTrue(transportSelectorCallback
+ .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS));
+
+ //Case 3: TTY supported by IMS and TTY disabled
+ doReturn(TelecomManager.TTY_MODE_OFF).when(mMockTelecomManager).getCurrentTtyMode();
+ mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+ assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+ assertTrue(transportSelectorCallback
+ .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS));
+ }
+
+ static class MockTransportSelectorCallback implements TransportSelectorCallback,
+ WwanSelectorCallback {
+ public boolean mCreated;
+ public boolean mWlanSelected;
+ public boolean mWwanSelected;
+ public boolean mSelectionTerminated;
+ public boolean mDomainSelected;
+ int mCauseCode;
+ int mSelectedDomain;
+
+ @Override
+ public synchronized void onCreated(DomainSelector selector) {
+ Log.d(TAG, "onCreated");
+ mCreated = true;
+ notifyAll();
+ }
+
+ public boolean verifyOnCreated() {
+ mCreated = false;
+ Log.d(TAG, "verifyOnCreated");
+ waitForCallback(mCreated);
+ return mCreated;
+ }
+
+ @Override
+ public synchronized void onWlanSelected(boolean useEmergencyPdn) {
+ Log.d(TAG, "onWlanSelected");
+ mWlanSelected = true;
+ notifyAll();
+ }
+
+ public boolean verifyOnWlanSelected() {
+ Log.d(TAG, "verifyOnWlanSelected");
+ waitForCallback(mWlanSelected);
+ return mWlanSelected;
+ }
+
+ @Override
+ public synchronized WwanSelectorCallback onWwanSelected() {
+ mWwanSelected = true;
+ notifyAll();
+ return (WwanSelectorCallback) this;
+ }
+
+ @Override
+ public void onWwanSelected(final Consumer<WwanSelectorCallback> consumer) {
+ mWwanSelected = true;
+ Executors.newSingleThreadExecutor().execute(() -> {
+ consumer.accept(this);
+ });
+ }
+
+ public boolean verifyOnWwanSelected() {
+ waitForCallback(mWwanSelected);
+ return mWwanSelected;
+ }
+
+ @Override
+ public synchronized void onSelectionTerminated(int cause) {
+ Log.i(TAG, "onSelectionTerminated - called");
+ mCauseCode = cause;
+ mSelectionTerminated = true;
+ notifyAll();
+ }
+
+ public boolean verifyOnSelectionTerminated(int cause) {
+ Log.i(TAG, "verifyOnSelectionTerminated - called");
+ waitForCallback(mSelectionTerminated);
+ return (mSelectionTerminated && cause == mCauseCode);
+ }
+
+ private synchronized void waitForCallback(boolean condition) {
+ long now = System.currentTimeMillis();
+ long deadline = now + 1000;
+ try {
+ while (!condition && now < deadline) {
+ wait(deadline - now);
+ now = System.currentTimeMillis();
+ }
+ } catch (Exception e) {
+ Log.i(TAG, e.getMessage());
+ }
+ }
+
+ @Override
+ public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
+ int scanType,
+ @NonNull CancellationSignal signal,
+ @NonNull Consumer<EmergencyRegResult> consumer) {
+ Log.i(TAG, "onRequestEmergencyNetworkScan - called");
+
+ }
+
+ public synchronized void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
+ boolean useEmergencyPdn) {
+ Log.i(TAG, "onDomainSelected - called");
+ mSelectedDomain = domain;
+ mDomainSelected = true;
+ notifyAll();
+ }
+
+ public boolean verifyOnDomainSelected(int domain) {
+ Log.i(TAG, "verifyOnDomainSelected - called");
+ mDomainSelected = false;
+ waitForCallback(mDomainSelected);
+ return (domain == mSelectedDomain);
+ }
+ }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java
new file mode 100644
index 0000000..8f78a58
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.WwanSelectorCallback;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TestContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Consumer;
+
+/**
+ * Unit tests for SmsDomainSelector.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SmsDomainSelectorTest {
+ private static final int SLOT_0 = 0;
+ private static final int SUB_1 = 1;
+
+ @Mock private TransportSelectorCallback mTransportSelectorCallback;
+ @Mock private WwanSelectorCallback mWwanSelectorCallback;
+ @Mock private ImsStateTracker mImsStateTracker;
+ @Mock private DomainSelectorBase.DestroyListener mDomainSelectorDestroyListener;
+
+ private final SelectionAttributes mSelectionAttributes =
+ new SelectionAttributes.Builder(SLOT_0, SUB_1, SELECTOR_TYPE_SMS).build();
+ private Context mContext;
+ private Looper mLooper;
+ private TestableLooper mTestableLooper;
+ private SmsDomainSelector mDomainSelector;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = new TestContext();
+ HandlerThread handlerThread = new HandlerThread(
+ SmsDomainSelectorTest.class.getSimpleName());
+ handlerThread.start();
+ mLooper = handlerThread.getLooper();
+ mTestableLooper = new TestableLooper(mLooper);
+ mDomainSelector = new SmsDomainSelector(mContext, SLOT_0, SUB_1,
+ mLooper, mImsStateTracker, mDomainSelectorDestroyListener);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mTestableLooper != null) {
+ mTestableLooper.destroy();
+ mTestableLooper = null;
+ }
+
+ if (mDomainSelector != null) {
+ mDomainSelector.destroy();
+ verify(mImsStateTracker).removeImsStateListener(eq(mDomainSelector));
+ }
+
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+
+ mDomainSelector = null;
+ mWwanSelectorCallback = null;
+ mTransportSelectorCallback = null;
+ mImsStateTracker = null;
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnEutran() {
+ selectDomain(AccessNetworkType.EUTRAN);
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnNgran() {
+ selectDomain(AccessNetworkType.NGRAN);
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnIwlan() {
+ selectDomain(AccessNetworkType.IWLAN);
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegistered() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpImsStateListener(true, false, false);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenWwanSelectorCallbackNull() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final Consumer<WwanSelectorCallback> callback =
+ (Consumer<WwanSelectorCallback>) args[0];
+ callback.accept(null);
+ return null;
+ }).when(mTransportSelectorCallback).onWwanSelected(any(Consumer.class));
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mTransportSelectorCallback).onSelectionTerminated(anyInt());
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhilePreviousRequestInProgress() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ processAllMessages();
+
+ // onDomainSelected will be invoked only once
+ // even though the domain selection was requested twice.
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testCancelSelection() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.cancelSelection();
+
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ verify(mDomainSelectorDestroyListener).onDomainSelectorDestroyed(eq(mDomainSelector));
+ }
+
+ @Test
+ @SmallTest
+ public void testFinishSelection() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.finishSelection();
+
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ verify(mDomainSelectorDestroyListener).onDomainSelectorDestroyed(eq(mDomainSelector));
+ }
+
+ @Test
+ @SmallTest
+ public void testReselectDomain() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, AccessNetworkType.IWLAN);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.reselectDomain(mSelectionAttributes);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mTransportSelectorCallback).onWlanSelected(eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testReselectDomainWhilePreviousRequestInProgress() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, AccessNetworkType.IWLAN);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.reselectDomain(mSelectionAttributes);
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ verify(mTransportSelectorCallback, never()).onWlanSelected(eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnImsRegistrationStateChangedWhenNotRegistered() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpImsStateListener(false, true, false);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnImsRegistrationStateChangedWhenRegisteredAndSmsCapable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, true);
+ setUpImsStateListener(false, true, false);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnImsRegistrationStateChangedWhenRegisteredAndSmsIncapable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, false);
+ setUpImsStateListener(false, true, false);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnImsMmTelCapabilitiesChangedWhenSmsCapable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, true);
+ setUpImsStateListener(false, false, true);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnImsMmTelCapabilitiesChangedWhenSmsIncapable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, false);
+ setUpImsStateListener(false, false, true);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+ eq(false));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ private void selectDomain(@RadioAccessNetworkType int accessNetworkType) {
+ setUpImsStateTracker(accessNetworkType);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ if (accessNetworkType == AccessNetworkType.IWLAN) {
+ verify(mTransportSelectorCallback).onWlanSelected(eq(false));
+ } else {
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(false));
+ }
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ private void setUpImsStateTracker(@RadioAccessNetworkType int accessNetworkType) {
+ setUpImsStateTracker(accessNetworkType, true);
+ }
+
+ private void setUpImsStateTracker(@RadioAccessNetworkType int accessNetworkType,
+ boolean smsCapable) {
+ when(mImsStateTracker.isMmTelFeatureAvailable()).thenReturn(true);
+ when(mImsStateTracker.isImsRegistered())
+ .thenReturn(accessNetworkType != AccessNetworkType.UNKNOWN);
+ when(mImsStateTracker.isImsRegisteredOverWlan())
+ .thenReturn(accessNetworkType == AccessNetworkType.IWLAN);
+ when(mImsStateTracker.getImsAccessNetworkType()).thenReturn(accessNetworkType);
+ when(mImsStateTracker.isImsSmsCapable()).thenReturn(smsCapable);
+ }
+
+ private void setUpImsStateTracker(@RadioAccessNetworkType int firstAccessNetworkType,
+ @RadioAccessNetworkType int secondAccessNetworkType) {
+ when(mImsStateTracker.isMmTelFeatureAvailable()).thenReturn(true);
+ when(mImsStateTracker.isImsRegistered()).thenReturn(
+ firstAccessNetworkType != AccessNetworkType.UNKNOWN,
+ secondAccessNetworkType != AccessNetworkType.UNKNOWN);
+ when(mImsStateTracker.isImsRegisteredOverWlan()).thenReturn(
+ firstAccessNetworkType == AccessNetworkType.IWLAN,
+ secondAccessNetworkType == AccessNetworkType.IWLAN);
+ when(mImsStateTracker.getImsAccessNetworkType()).thenReturn(
+ firstAccessNetworkType,
+ secondAccessNetworkType);
+ when(mImsStateTracker.isImsSmsCapable()).thenReturn(true);
+ }
+
+ private void setUpWwanSelectorCallback() {
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final Consumer<WwanSelectorCallback> callback =
+ (Consumer<WwanSelectorCallback>) args[0];
+ callback.accept(mWwanSelectorCallback);
+ return null;
+ }).when(mTransportSelectorCallback).onWwanSelected(any(Consumer.class));
+ }
+
+ private void setUpImsStateListener(boolean notifyMmTelFeatureAvailable,
+ boolean notifyImsRegState, boolean notifyMmTelCapability) {
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final ImsStateTracker.ImsStateListener listener =
+ (ImsStateTracker.ImsStateListener) args[0];
+ mDomainSelector.post(() -> {
+ if (notifyMmTelFeatureAvailable) {
+ listener.onImsMmTelFeatureAvailableChanged();
+ }
+ if (notifyImsRegState) {
+ listener.onImsRegistrationStateChanged();
+ }
+ if (notifyMmTelCapability) {
+ listener.onImsMmTelCapabilitiesChanged();
+ }
+ });
+ return null;
+ }).when(mImsStateTracker).addImsStateListener(any(ImsStateTracker.ImsStateListener.class));
+ }
+
+ private void processAllMessages() {
+ while (!mTestableLooper.getLooper().getQueue().isIdle()) {
+ mTestableLooper.processAllMessages();
+ }
+ }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java b/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
new file mode 100644
index 0000000..f340e94
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.telephony.BarringInfo;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.DomainSelectionService.SelectorType;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TransportSelectorCallback;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TestContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests for TelephonyDomainSelectionService.
+ */
+@RunWith(AndroidJUnit4.class)
+public class TelephonyDomainSelectionServiceTest {
+ private TelephonyDomainSelectionService.ImsStateTrackerFactory mImsStateTrackerFactory =
+ new TelephonyDomainSelectionService.ImsStateTrackerFactory() {
+ @Override
+ public ImsStateTracker create(Context context, int slotId,
+ @NonNull Looper looper) {
+ return mImsStateTracker;
+ }
+ };
+ private TelephonyDomainSelectionService.DomainSelectorFactory mDomainSelectorFactory =
+ new TelephonyDomainSelectionService.DomainSelectorFactory() {
+ @Override
+ public DomainSelectorBase create(Context context, int slotId, int subId,
+ @SelectorType int selectorType, boolean isEmergency,
+ @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
+ @NonNull DomainSelectorBase.DestroyListener listener,
+ @NonNull CrossSimRedialingController crossSimRedialingController) {
+ switch (selectorType) {
+ case DomainSelectionService.SELECTOR_TYPE_CALLING: // fallthrough
+ case DomainSelectionService.SELECTOR_TYPE_SMS: // fallthrough
+ case DomainSelectionService.SELECTOR_TYPE_UT:
+ mDomainSelectorDestroyListener = listener;
+ if (subId == SUB_1) {
+ return mDomainSelectorBase1;
+ } else {
+ return mDomainSelectorBase2;
+ }
+ default:
+ return null;
+ }
+ }
+ };
+ private static final int SLOT_0 = 0;
+ private static final int SUB_1 = 1;
+ private static final int SUB_2 = 2;
+ private static final String CALL_ID = "Call_1";
+ private static final @SelectorType int TEST_SELECTOR_TYPE =
+ DomainSelectionService.SELECTOR_TYPE_CALLING;
+ private static final @SelectorType int INVALID_SELECTOR_TYPE = -1;
+
+ @Mock private DomainSelectorBase mDomainSelectorBase1;
+ @Mock private DomainSelectorBase mDomainSelectorBase2;
+ @Mock private TransportSelectorCallback mSelectorCallback1;
+ @Mock private TransportSelectorCallback mSelectorCallback2;
+ @Mock private ImsStateTracker mImsStateTracker;
+
+ private final ServiceState mServiceState = new ServiceState();
+ private final BarringInfo mBarringInfo = new BarringInfo();
+ private Context mContext;
+ private Handler mServiceHandler;
+ private TestableLooper mTestableLooper;
+ private SubscriptionManager mSubscriptionManager;
+ private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
+ private DomainSelectorBase.DestroyListener mDomainSelectorDestroyListener;
+ private TelephonyDomainSelectionService mDomainSelectionService;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mContext = new TestContext();
+ mDomainSelectionService = new TelephonyDomainSelectionService(mContext,
+ mImsStateTrackerFactory, mDomainSelectorFactory);
+ mServiceHandler = new Handler(mDomainSelectionService.getLooper());
+ mTestableLooper = new TestableLooper(mDomainSelectionService.getLooper());
+
+ mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+ ArgumentCaptor<OnSubscriptionsChangedListener> listenerCaptor =
+ ArgumentCaptor.forClass(OnSubscriptionsChangedListener.class);
+ verify(mSubscriptionManager).addOnSubscriptionsChangedListener(
+ any(Executor.class), listenerCaptor.capture());
+ mOnSubscriptionsChangedListener = listenerCaptor.getValue();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mTestableLooper != null) {
+ mTestableLooper.destroy();
+ mTestableLooper = null;
+ }
+ mServiceHandler = null;
+
+ if (mDomainSelectionService != null) {
+ mDomainSelectionService.onDestroy();
+ mDomainSelectionService = null;
+ }
+
+ mDomainSelectorBase1 = null;
+ mDomainSelectorBase2 = null;
+ mSelectorCallback1 = null;
+ mSelectorCallback2 = null;
+ mImsStateTracker = null;
+ mSubscriptionManager = null;
+ mOnSubscriptionsChangedListener = null;
+ mDomainSelectorDestroyListener = null;
+ }
+
+ @Test
+ @SmallTest
+ public void testGetExecutor() {
+ assertNotNull(mDomainSelectionService.getExecutor());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnDomainSelection() {
+ SelectionAttributes attr1 = new SelectionAttributes.Builder(
+ SLOT_0, SUB_1, TEST_SELECTOR_TYPE)
+ .setCallId(CALL_ID)
+ .setEmergency(true)
+ .build();
+ mServiceHandler.post(() -> {
+ mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
+ });
+ processAllMessages();
+
+ verify(mImsStateTracker).start(eq(SUB_1));
+ verify(mSelectorCallback1).onCreated(eq(mDomainSelectorBase1));
+ verifyNoMoreInteractions(mSelectorCallback1);
+ verify(mDomainSelectorBase1).selectDomain(eq(attr1), eq(mSelectorCallback1));
+ }
+
+ @Test
+ @SmallTest
+ public void testOnDomainSelectionWithInvalidSelectorType() {
+ SelectionAttributes attr1 = new SelectionAttributes.Builder(
+ SLOT_0, SUB_1, INVALID_SELECTOR_TYPE)
+ .setCallId(CALL_ID)
+ .setEmergency(true)
+ .build();
+ mServiceHandler.post(() -> {
+ mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
+ });
+ processAllMessages();
+
+ verify(mImsStateTracker, never()).start(anyInt());
+ verify(mSelectorCallback1).onSelectionTerminated(anyInt());
+ verifyNoMoreInteractions(mSelectorCallback1);
+ verify(mDomainSelectorBase1, never()).selectDomain(eq(attr1), eq(mSelectorCallback1));
+ }
+
+ @Test
+ @SmallTest
+ public void testOnDomainSelectionTwiceWithDestroy() {
+ SelectionAttributes attr1 = new SelectionAttributes.Builder(
+ SLOT_0, SUB_1, TEST_SELECTOR_TYPE)
+ .setCallId(CALL_ID)
+ .setEmergency(true)
+ .build();
+ mServiceHandler.post(() -> {
+ mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
+ });
+ processAllMessages();
+
+ verify(mImsStateTracker).start(eq(SUB_1));
+ verify(mSelectorCallback1).onCreated(eq(mDomainSelectorBase1));
+ verifyNoMoreInteractions(mSelectorCallback1);
+ verify(mDomainSelectorBase1).selectDomain(eq(attr1), eq(mSelectorCallback1));
+
+ // Notify the domain selection service that this domain selector is destroyed.
+ mDomainSelectorDestroyListener.onDomainSelectorDestroyed(mDomainSelectorBase1);
+
+ SelectionAttributes attr2 = new SelectionAttributes.Builder(
+ SLOT_0, SUB_2, TEST_SELECTOR_TYPE)
+ .setCallId(CALL_ID)
+ .setEmergency(true)
+ .build();
+ mServiceHandler.post(() -> {
+ mDomainSelectionService.onDomainSelection(attr2, mSelectorCallback2);
+ });
+ processAllMessages();
+
+ verify(mImsStateTracker).start(eq(SUB_2));
+ verify(mSelectorCallback2).onCreated(eq(mDomainSelectorBase2));
+ verifyNoMoreInteractions(mSelectorCallback2);
+ verify(mDomainSelectorBase2).selectDomain(eq(attr2), eq(mSelectorCallback2));
+ }
+
+ @Test
+ @SmallTest
+ public void testOnDomainSelectionTwiceWithoutDestroy() {
+ SelectionAttributes attr1 = new SelectionAttributes.Builder(
+ SLOT_0, SUB_1, TEST_SELECTOR_TYPE)
+ .setCallId(CALL_ID)
+ .setEmergency(true)
+ .build();
+ mServiceHandler.post(() -> {
+ mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
+ });
+ processAllMessages();
+
+ verify(mImsStateTracker).start(eq(SUB_1));
+ verify(mSelectorCallback1).onCreated(eq(mDomainSelectorBase1));
+ verifyNoMoreInteractions(mSelectorCallback1);
+ verify(mDomainSelectorBase1).selectDomain(eq(attr1), eq(mSelectorCallback1));
+
+ SelectionAttributes attr2 = new SelectionAttributes.Builder(
+ SLOT_0, SUB_2, TEST_SELECTOR_TYPE)
+ .setCallId(CALL_ID)
+ .setEmergency(true)
+ .build();
+ mServiceHandler.post(() -> {
+ mDomainSelectionService.onDomainSelection(attr2, mSelectorCallback2);
+ });
+ processAllMessages();
+
+ verify(mImsStateTracker).start(eq(SUB_2));
+ verify(mSelectorCallback2).onCreated(eq(mDomainSelectorBase2));
+ verifyNoMoreInteractions(mSelectorCallback2);
+ verify(mDomainSelectorBase2).selectDomain(eq(attr2), eq(mSelectorCallback2));
+ }
+
+ @Test
+ @SmallTest
+ public void testOnServiceStateUpdated() {
+ mDomainSelectionService.onServiceStateUpdated(SLOT_0, SUB_1, mServiceState);
+
+ verify(mImsStateTracker).updateServiceState(eq(mServiceState));
+ }
+
+ @Test
+ @SmallTest
+ public void testOnBarringInfoUpdated() {
+ mDomainSelectionService.onBarringInfoUpdated(SLOT_0, SUB_1, mBarringInfo);
+
+ verify(mImsStateTracker).updateBarringInfo(eq(mBarringInfo));
+ }
+
+ @Test
+ @SmallTest
+ public void testOnDestroy() {
+ SelectionAttributes attr1 = new SelectionAttributes.Builder(
+ SLOT_0, SUB_1, TEST_SELECTOR_TYPE)
+ .setCallId(CALL_ID)
+ .setEmergency(true)
+ .build();
+ mServiceHandler.post(() -> {
+ mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
+ });
+ processAllMessages();
+
+ mDomainSelectionService.onDestroy();
+
+ verify(mImsStateTracker).destroy();
+ verify(mDomainSelectorBase1).destroy();
+ verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(any());
+ }
+
+ @Test
+ @SmallTest
+ public void testHandleSubscriptionsChangedWithEmptySubscriptionInfo() {
+ when(mSubscriptionManager.getActiveSubscriptionInfoList())
+ .thenReturn(null, new ArrayList<SubscriptionInfo>());
+
+ mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+ mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+
+ verify(mImsStateTracker, never()).start(anyInt());
+ }
+
+ @Test
+ @SmallTest
+ public void testHandleSubscriptionsChangedWithActiveSubscriptionInfoAndInvalidSlotIndex() {
+ SubscriptionInfo subsInfo = Mockito.mock(SubscriptionInfo.class);
+ List<SubscriptionInfo> subsInfoList = new ArrayList<>();
+ subsInfoList.add(subsInfo);
+ when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(subsInfoList);
+ when(subsInfo.getSimSlotIndex()).thenReturn(SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+
+ mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+
+ verify(mImsStateTracker, never()).start(anyInt());
+ }
+
+ @Test
+ @SmallTest
+ public void testHandleSubscriptionsChangedWithActiveSubscriptionInfo() {
+ SubscriptionInfo subsInfo = Mockito.mock(SubscriptionInfo.class);
+ List<SubscriptionInfo> subsInfoList = new ArrayList<>();
+ subsInfoList.add(subsInfo);
+ when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(subsInfoList);
+ when(subsInfo.getSubscriptionId())
+ .thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID, SUB_1);
+ when(subsInfo.getSimSlotIndex()).thenReturn(SLOT_0);
+
+ mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+ mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+
+ verify(mImsStateTracker).start(eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+ verify(mImsStateTracker).start(eq(SUB_1));
+ }
+
+ private void processAllMessages() {
+ while (!mTestableLooper.getLooper().getQueue().isIdle()) {
+ mTestableLooper.processAllMessages();
+ }
+ }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
index 2a30e1a..07c9fd0 100644
--- a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -171,12 +172,13 @@
verify(mFeatureManager).registerImsRegistrationCallback(captor.capture());
assertNotNull(captor.getValue());
- captor.getValue().onDeregistered(REASON_DISCONNECTED);
+ captor.getValue().onDeregistered(REASON_DISCONNECTED, 0, 0);
controller.getRegistrationState(result -> {
assertNotNull(result);
assertEquals(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED, result.intValue());
});
- verify(mRegistrationCallback).handleImsUnregistered(REASON_DISCONNECTED);
+ verify(mRegistrationCallback).handleImsUnregistered(eq(REASON_DISCONNECTED),
+ anyInt(), anyInt());
ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
ImsRegistrationImplBase.REGISTRATION_TECH_LTE).build();
@@ -193,8 +195,7 @@
assertNotNull(result);
assertEquals(RegistrationManager.REGISTRATION_STATE_REGISTERED, result.intValue());
});
- verify(mRegistrationCallback).handleImsRegistered(
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ verify(mRegistrationCallback).handleImsRegistered(attr);
}
@Test
diff --git a/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java b/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java
index 37abb83..3874321 100644
--- a/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java
@@ -22,26 +22,44 @@
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.BinderCacheManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipDialogState;
+import android.telephony.ims.SipDialogStateCallback;
import android.telephony.ims.SipMessage;
+import android.telephony.ims.aidl.IImsRcsController;
+import android.util.ArraySet;
import android.util.Base64;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.internal.telephony.ISipDialogStateCallback;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.metrics.RcsStats;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -103,14 +121,29 @@
private static final String TEST_INVITE_SIP_METHOD = "INVITE";
private static final int TEST_SIP_RESPONSE_CODE = 200;
private static final int TEST_SIP_CLOSE_RESPONSE_CODE = 0;
- @Mock
- private RcsStats mRcsStats;
+
+ @Mock private RcsStats mRcsStats;
+ private boolean mUpdatedState = false;
+ private SipDialogStateCallback mCallback;
+ private SipDelegateManager mSipManager;
+ private ISipDialogStateCallback mCbBinder;
+ IImsRcsController mMockImsRcsInterface;
+ BinderCacheManager<ITelephony> mBinderCache;
+ BinderCacheManager<IImsRcsController> mRcsBinderCache;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
mStringEntryCounter = 0;
MockitoAnnotations.initMocks(this);
mTrackerUT = new SipSessionTracker(TEST_SUB_ID, mRcsStats);
+ mMockImsRcsInterface = mock(IImsRcsController.class);
+ mBinderCache = mock(BinderCacheManager.class);
+ mRcsBinderCache = mock(BinderCacheManager.class);
+ doReturn(mMockImsRcsInterface).when(mRcsBinderCache)
+ .listenOnBinder(any(), any(Runnable.class));
+ doReturn(mMockImsRcsInterface).when(mRcsBinderCache)
+ .removeRunnable(any(SipDialogStateCallback.class));
+ doReturn(mMockImsRcsInterface).when(mRcsBinderCache).getBinder();
}
@Test
@@ -425,6 +458,143 @@
verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr);
}
+ @Test
+ public void testActiveDialogsChanged() throws ImsException {
+ sipDialogStateCallback();
+
+ // first dialog
+ DialogAttributes attr1 = new DialogAttributes();
+ createConfirmedDialog(attr1);
+ assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+ verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr1);
+
+ verifyConfirmedStates(true);
+
+ // add a second dialog
+ DialogAttributes attr2 = new DialogAttributes();
+ createConfirmedDialog(attr2);
+ assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+ verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr1, attr2);
+ verifyConfirmedStates(true);
+
+ // Send BYE request on first dialog
+ SipMessage byeRequest = generateSipRequest(SipMessageUtils.BYE_SIP_METHOD, attr1);
+ filterMessage(byeRequest, attr1);
+ assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+ verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr2);
+ verifyContainsCallIds(mTrackerUT.getClosedDialogs(), attr1);
+ mTrackerUT.cleanupSession(attr1.callId);
+ verifyConfirmedStates(true);
+
+ // Send BYE request on second dialog
+ byeRequest = generateSipRequest(SipMessageUtils.BYE_SIP_METHOD, attr2);
+ filterMessage(byeRequest, attr2);
+ assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+ assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+ verifyContainsCallIds(mTrackerUT.getClosedDialogs(), attr2);
+ mTrackerUT.cleanupSession(attr2.callId);
+ assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+ assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+ assertTrue(mTrackerUT.getClosedDialogs().isEmpty());
+ verifyConfirmedStates(false);
+ unRegisterCallback();
+ }
+
+ @Test
+ public void testActiveSipDialogsChangedClearAll() throws ImsException {
+ sipDialogStateCallback();
+
+ // first dialog
+ DialogAttributes attr1 = new DialogAttributes();
+ createConfirmedDialog(attr1);
+ assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+ verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr1);
+ verifyConfirmedStates(true);
+
+ // add a second dialog
+ DialogAttributes attr2 = new DialogAttributes();
+ createConfirmedDialog(attr2);
+ assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+ verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr1, attr2);
+ verifyConfirmedStates(true);
+
+ // cleanAllSessions
+ mTrackerUT.clearAllSessions();
+ assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+ assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+ assertTrue(mTrackerUT.getClosedDialogs().isEmpty());
+ verifyConfirmedStates(false);
+ unRegisterCallback();
+ }
+
+ private void sipDialogStateCallback() throws ImsException {
+ mCallback = new SipDialogStateCallback() {
+ @Override
+ public void onActiveSipDialogsChanged(List<SipDialogState> dialogs) {
+ mUpdatedState = isSipDialogActiveState(dialogs);
+ }
+
+ @Override
+ public void onError() { }
+ };
+ registerCallback();
+ }
+
+ private void verifyConfirmedStates(boolean currentState) {
+ List<SipDialogState> dialogStates = new ArrayList<>();
+ for (SipDialog d : (ArraySet<SipDialog>) mTrackerUT.getTrackedDialogs()) {
+ SipDialogState dialog = new SipDialogState.Builder(d.getState()).build();
+ dialogStates.add(dialog);
+ }
+ try {
+ mCbBinder.onActiveSipDialogsChanged(dialogStates);
+ } catch (RemoteException e) {
+ //onActiveSipDialogsChanged error
+ }
+ assertEquals(currentState, mUpdatedState);
+ }
+
+ private void registerCallback() throws ImsException {
+ // Capture the Runnable that was registered.
+ ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ // Capture the ISipDialogStateCallback that was registered.
+ ArgumentCaptor<ISipDialogStateCallback> callbackCaptor =
+ ArgumentCaptor.forClass(ISipDialogStateCallback.class);
+
+ Context context = PhoneFactory.getDefaultPhone().getContext();
+ mSipManager = new SipDelegateManager(context,
+ TEST_SUB_ID, mRcsBinderCache, mBinderCache);
+
+ mSipManager.registerSipDialogStateCallback(Runnable::run, mCallback);
+
+ verify(mRcsBinderCache).listenOnBinder(any(), runnableCaptor.capture());
+ try {
+ verify(mMockImsRcsInterface).registerSipDialogStateCallback(
+ eq(TEST_SUB_ID), callbackCaptor.capture());
+ } catch (RemoteException e) {
+ //registerSipDialogStateCallback error
+ }
+ mCbBinder = callbackCaptor.getValue();
+ }
+
+ private void unRegisterCallback() {
+ try {
+ mSipManager.unregisterSipDialogStateCallback(mCallback);
+ } catch (ImsException e) {
+ //unregisterSipDialogStateCallback error
+ }
+ }
+
+ private boolean isSipDialogActiveState(List<SipDialogState> dialogs) {
+ int confirmedSize = dialogs.stream().filter(
+ d -> d.getState() == SipDialogState.STATE_CONFIRMED)
+ .collect(Collectors.toSet()).size();
+ if (confirmedSize > 0) {
+ return true;
+ }
+ return false;
+ }
+
private void filterMessage(SipMessage m, DialogAttributes attr) {
mTrackerUT.filterSipMessage(
SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, m);
diff --git a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
index 39469b6..d575d77 100644
--- a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
+++ b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
@@ -25,17 +25,20 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.PropertyInvalidatedCache;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import androidx.test.runner.AndroidJUnit4;
import com.android.TelephonyTestBase;
import com.android.ims.FeatureConnector;
import com.android.ims.RcsFeatureManager;
+import com.android.internal.telephony.ISub;
import org.junit.After;
import org.junit.Before;
@@ -59,12 +62,26 @@
@Mock RcsFeatureController.FeatureConnectorFactory<RcsFeatureManager> mFeatureConnectorFactory;
@Mock FeatureConnector<RcsFeatureManager> mFeatureConnector;
+ @Mock
+ private ISub mISub;
+
+ @Mock
+ private TelephonyManager mTelephonyManager;
+
private RcsFeatureController mFeatureControllerSlot0;
private RcsFeatureController mFeatureControllerSlot1;
@Before
public void setUp() throws Exception {
super.setUp();
+ TelephonyManager.setupISubForTest(mISub);
+ TelephonyManager.enableServiceHandleCaching();
+ PropertyInvalidatedCache.disableForTestMode();
+
+ //set up default slot-> sub ID mappings.
+ setSlotToSubIdMapping(0 /*slotId*/, 1/*subId*/);
+ setSlotToSubIdMapping(1 /*slotId*/, 2/*subId*/);
+
doReturn(mFeatureConnector).when(mFeatureConnectorFactory).create(any(), anyInt(),
any(), any(), any());
mFeatureControllerSlot0 = createFeatureController(0 /*slotId*/, 1 /*subId*/);
@@ -82,9 +99,9 @@
doReturn(mMockSipTransportSlot1).when(mFeatureFactory).createSipTransportController(any(),
eq(1), anyInt());
doReturn(true).when(mResourceProxy).getDeviceUceEnabled(any());
- //set up default slot-> sub ID mappings.
- setSlotToSubIdMapping(0 /*slotId*/, 1/*subId*/);
- setSlotToSubIdMapping(1 /*slotId*/, 2/*subId*/);
+
+ replaceInstance(TelephonyManager.class, "sInstance", null, mTelephonyManager);
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
}
@After
@@ -341,11 +358,8 @@
bundle.putBoolean(key, value);
}
- private void setSlotToSubIdMapping(int slotId, int loadedSubId) {
- SubscriptionManager m = mContext.getSystemService(SubscriptionManager.class);
- int [] subIds = new int[1];
- subIds[0] = loadedSubId;
- doReturn(subIds).when(m).getSubscriptionIds(eq(slotId));
+ private void setSlotToSubIdMapping(int slotId, int loadedSubId) throws Exception {
+ doReturn(loadedSubId).when(mISub).getSubId(slotId);
}
private TelephonyRcsService createRcsService(int numSlots) {