DO NOT MERGE - Skip PPRL.190305.001 into master
Bug: 127812889
Change-Id: Iddb346fb6984252a84cf5020b97a54972178c87e
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..eabd567
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,9 @@
+android_app {
+ name: "TelephonyProvider",
+ privileged: true,
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+ libs: ["telephony-common"],
+ static_libs: ["android-common"],
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 3ff05c9..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-
-LOCAL_PACKAGE_NAME := TelephonyProvider
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-LOCAL_JAVA_LIBRARIES += telephony-common
-LOCAL_STATIC_JAVA_LIBRARIES += android-common
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 31e5364..9ac0815 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -112,6 +112,12 @@
android:singleUser="true"
android:multiprocess="false" />
+ <provider android:name="RcsProvider"
+ android:authorities="rcs"
+ android:multiprocess="false"
+ android:exported="true"
+ android:singleUser="true" />
+
<service
android:name=".TelephonyBackupAgent$DeferredSmsMmsRestoreService"
android:exported="false" />
diff --git a/OWNERS b/OWNERS
index 0716d33..92458db 100644
--- a/OWNERS
+++ b/OWNERS
@@ -6,4 +6,7 @@
mpq@google.com
jminjie@google.com
shuoq@google.com
-
+hallliu@google.com
+tgunn@google.com
+breadley@google.com
+nazaninb@google.com
diff --git a/assets/carrier_list.pb b/assets/carrier_list.pb
index 856c5bc..18b18e3 100644
--- a/assets/carrier_list.pb
+++ b/assets/carrier_list.pb
Binary files differ
diff --git a/assets/carrier_list.textpb b/assets/carrier_list.textpb
index 23196a5..07fdc8d 100644
--- a/assets/carrier_list.textpb
+++ b/assets/carrier_list.textpb
@@ -185,6 +185,7 @@
mccmnc_tuple: "23201"
mccmnc_tuple: "23211"
mccmnc_tuple: "23212"
+ spn: ""
}
}
carrier_id {
@@ -243,6 +244,7 @@
carrier_name: "Vodafone"
carrier_attribute {
mccmnc_tuple: "53001"
+ spn: "vodafone NZ"
}
}
carrier_id {
@@ -333,6 +335,8 @@
spn: "vodafone"
spn: "vodafone ES"
spn: "Vodafone"
+ spn: "Personal"
+ spn: "Profesional"
}
}
carrier_id {
@@ -340,6 +344,7 @@
carrier_name: "SFR"
carrier_attribute {
mccmnc_tuple: "20810"
+ mccmnc_tuple: "20809"
}
}
carrier_id {
@@ -712,6 +717,10 @@
gid1: "2c"
gid1: "4d"
}
+ carrier_attribute {
+ mccmnc_tuple: "302660"
+ spn: "MTS"
+ }
}
carrier_id {
canonical_id: 579
@@ -729,6 +738,10 @@
mccmnc_tuple: "302680"
mccmnc_tuple: "302780"
}
+ carrier_attribute {
+ mccmnc_tuple: "20404"
+ gid1: "5a"
+ }
}
carrier_id {
canonical_id: 581
@@ -747,7 +760,7 @@
}
carrier_id {
canonical_id: 625
- carrier_name: "Telefónica Móviles Colombia S.A."
+ carrier_name: "Movistar"
carrier_attribute {
mccmnc_tuple: "732123"
}
@@ -761,9 +774,10 @@
}
carrier_id {
canonical_id: 627
- carrier_name: "Instituto Costarricense de Electricidad - ICE"
+ carrier_name: "KOLBI ICE"
carrier_attribute {
mccmnc_tuple: "71201"
+ mccmnc_tuple: "71202"
}
}
carrier_id {
@@ -863,6 +877,7 @@
carrier_name: "Vodafone"
carrier_attribute {
mccmnc_tuple: "60202"
+ spn: "vodafone"
}
}
carrier_id {
@@ -870,6 +885,7 @@
carrier_name: "Orange"
carrier_attribute {
mccmnc_tuple: "21403"
+ spn: "Orange"
}
}
carrier_id {
@@ -898,6 +914,7 @@
carrier_name: "Virgin"
carrier_attribute {
mccmnc_tuple: "23430"
+ mccmnc_tuple: "23433"
mccmnc_tuple: "23438"
gid1: "2800000000000000"
}
@@ -911,28 +928,8 @@
carrier_name: "EE"
carrier_attribute {
mccmnc_tuple: "23430"
- imsi_prefix_xpattern: "234300"
- imsi_prefix_xpattern: "234301"
- imsi_prefix_xpattern: "234302"
- imsi_prefix_xpattern: "234303"
- imsi_prefix_xpattern: "2343040"
- imsi_prefix_xpattern: "2343042"
- imsi_prefix_xpattern: "2343043"
- imsi_prefix_xpattern: "2343044"
- imsi_prefix_xpattern: "2343045"
- imsi_prefix_xpattern: "2343046"
- imsi_prefix_xpattern: "2343047"
- imsi_prefix_xpattern: "2343048"
- imsi_prefix_xpattern: "2343049"
- imsi_prefix_xpattern: "234305"
- imsi_prefix_xpattern: "234306"
- imsi_prefix_xpattern: "234307"
- imsi_prefix_xpattern: "234308"
- imsi_prefix_xpattern: "234309"
- }
- carrier_attribute {
mccmnc_tuple: "23433"
- imsi_prefix_xpattern: "x"
+ mccmnc_tuple: "23486"
}
}
carrier_id {
@@ -1073,6 +1070,8 @@
carrier_name: "SmarTone HK"
carrier_attribute {
mccmnc_tuple: "45406"
+ mccmnc_tuple: "45417"
+ mccmnc_tuple: "45415"
}
}
carrier_id {
@@ -1119,13 +1118,6 @@
}
}
carrier_id {
- canonical_id: 768
- carrier_name: "3G Radio System/SMT3G"
- carrier_attribute {
- mccmnc_tuple: "45415"
- }
-}
-carrier_id {
canonical_id: 769
carrier_name: "PCCW"
carrier_attribute {
@@ -1228,6 +1220,7 @@
carrier_name: "Partner Communications Co. Ltd."
carrier_attribute {
mccmnc_tuple: "42501"
+ mccmnc_tuple: "42510"
}
}
carrier_id {
@@ -1266,11 +1259,17 @@
mccmnc_tuple: "405799"
mccmnc_tuple: "405845"
mccmnc_tuple: "405846"
+ mccmnc_tuple: "405847"
mccmnc_tuple: "405848"
mccmnc_tuple: "405849"
mccmnc_tuple: "405850"
+ mccmnc_tuple: "405851"
mccmnc_tuple: "405852"
mccmnc_tuple: "405853"
+ mccmnc_tuple: "405908"
+ mccmnc_tuple: "405909"
+ mccmnc_tuple: "405910"
+ mccmnc_tuple: "405911"
}
}
carrier_id {
@@ -1278,6 +1277,7 @@
carrier_name: "Orange Jordan"
carrier_attribute {
mccmnc_tuple: "41677"
+ mccmnc_tuple: "416770"
}
}
carrier_id {
@@ -1285,6 +1285,12 @@
carrier_name: "NTT DOCOMO"
carrier_attribute {
mccmnc_tuple: "44010"
+ gid1: "00FFFF"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "44010"
+ preferred_apn: "spmode.ne.jp"
+ preferred_apn: "mopera.net"
}
}
carrier_id {
@@ -1292,6 +1298,7 @@
carrier_name: "Safaricom"
carrier_attribute {
mccmnc_tuple: "63902"
+ spn: "Safaricom"
}
}
carrier_id {
@@ -1303,7 +1310,7 @@
}
carrier_id {
canonical_id: 867
- carrier_name: "Bitel GSM"
+ carrier_name: "Beeline"
carrier_attribute {
mccmnc_tuple: "43701"
}
@@ -1424,7 +1431,7 @@
}
carrier_id {
canonical_id: 903
- carrier_name: "Ittissalat Al Maghrid"
+ carrier_name: "Maroc"
carrier_attribute {
mccmnc_tuple: "60401"
}
@@ -1487,7 +1494,7 @@
}
carrier_id {
canonical_id: 959
- carrier_name: "Netcom GSM AS"
+ carrier_name: "Telia"
carrier_attribute {
mccmnc_tuple: "24202"
}
@@ -1580,7 +1587,7 @@
}
carrier_id {
canonical_id: 974
- carrier_name: "movistar (Telefónica Moviles Panama S.A.)"
+ carrier_name: "Movistar"
carrier_attribute {
mccmnc_tuple: "71402"
mccmnc_tuple: "714020"
@@ -1847,6 +1854,7 @@
carrier_name: "CAT CDMA"
carrier_attribute {
mccmnc_tuple: "52000"
+ mccmnc_tuple: "52002"
}
}
carrier_id {
@@ -2420,7 +2428,7 @@
}
carrier_id {
canonical_id: 1337
- carrier_name: "Telefonica Comunicaciones Personales S.A."
+ carrier_name: "Movistar"
carrier_attribute {
mccmnc_tuple: "722070"
mccmnc_tuple: "72207"
@@ -2431,6 +2439,7 @@
carrier_name: "Claro AR"
carrier_attribute {
mccmnc_tuple: "722310"
+ mccmnc_tuple: "72231"
}
}
carrier_id {
@@ -2652,7 +2661,7 @@
}
carrier_id {
canonical_id: 1370
- carrier_name: "Mtel"
+ carrier_name: "A1"
carrier_attribute {
mccmnc_tuple: "28401"
}
@@ -2779,6 +2788,7 @@
carrier_attribute {
mccmnc_tuple: "72430"
mccmnc_tuple: "72431"
+ mccmnc_tuple: "72424"
}
}
carrier_id {
@@ -2883,6 +2893,7 @@
mccmnc_tuple: "302221"
mccmnc_tuple: "30222"
mccmnc_tuple: "30286"
+ gid1: "5455"
}
}
carrier_id {
@@ -3050,7 +3061,7 @@
}
carrier_id {
canonical_id: 1428
- carrier_name: "Telefónica Móvil"
+ carrier_name: "Movistar"
carrier_attribute {
mccmnc_tuple: "73002"
mccmnc_tuple: "73007"
@@ -3065,9 +3076,10 @@
}
carrier_id {
canonical_id: 1430
- carrier_name: "Centennial Cayman Corp. Chile S.A."
+ carrier_name: "WOM"
carrier_attribute {
mccmnc_tuple: "73004"
+ mccmnc_tuple: "73009"
}
}
carrier_id {
@@ -3098,6 +3110,7 @@
mccmnc_tuple: "46000"
mccmnc_tuple: "46002"
mccmnc_tuple: "46007"
+ mccmnc_tuple: "46008"
}
}
carrier_id {
@@ -3105,7 +3118,7 @@
carrier_name: "China Unicom"
carrier_attribute {
mccmnc_tuple: "46001"
- mccmnc_tuple: "46003"
+ mccmnc_tuple: "46009"
}
}
carrier_id {
@@ -3333,6 +3346,7 @@
carrier_name: "Claro RD"
carrier_attribute {
mccmnc_tuple: "37002"
+ mccmnc_tuple: "370020"
}
}
carrier_id {
@@ -3365,7 +3379,7 @@
}
carrier_id {
canonical_id: 1472
- carrier_name: "Movistar (Otecel S.A.)"
+ carrier_name: "Movistar"
carrier_attribute {
mccmnc_tuple: "74000"
}
@@ -3375,6 +3389,7 @@
carrier_name: "Claro EC"
carrier_attribute {
mccmnc_tuple: "74001"
+ mccmnc_tuple: "740010"
}
}
carrier_id {
@@ -3386,9 +3401,10 @@
}
carrier_id {
canonical_id: 1475
- carrier_name: "Elisa Matkapuhelinpalvelut Ltd."
+ carrier_name: "Elisa"
carrier_attribute {
mccmnc_tuple: "24405"
+ mccmnc_tuple: "24421"
}
}
carrier_id {
@@ -3413,13 +3429,6 @@
}
}
carrier_id {
- canonical_id: 1479
- carrier_name: "Saunalahti Group Ltd."
- carrier_attribute {
- mccmnc_tuple: "24421"
- }
-}
-carrier_id {
canonical_id: 1480
carrier_name: "Telia"
carrier_attribute {
@@ -3593,6 +3602,8 @@
carrier_name: "Three Mobile"
carrier_attribute {
mccmnc_tuple: "23420"
+ mccmnc_tuple: "23494"
+ mccmnc_tuple: "23594"
}
}
carrier_id {
@@ -3675,13 +3686,7 @@
carrier_name: "Vodafone"
carrier_attribute {
mccmnc_tuple: "62002"
- }
-}
-carrier_id {
- canonical_id: 1517
- carrier_name: "Mobitel"
- carrier_attribute {
- mccmnc_tuple: "62003"
+ spn: "Vodafone GH"
}
}
carrier_id {
@@ -3714,7 +3719,7 @@
}
carrier_id {
canonical_id: 1522
- carrier_name: "Telefónica Centroamérica Guatemala S.A."
+ carrier_name: "Movistar"
carrier_attribute {
mccmnc_tuple: "70403"
mccmnc_tuple: "704030"
@@ -3774,7 +3779,7 @@
}
carrier_id {
canonical_id: 1530
- carrier_name: "Vip/Tomato"
+ carrier_name: "A1/Tomato"
carrier_attribute {
mccmnc_tuple: "21910"
}
@@ -3815,6 +3820,10 @@
spn: ""
spn: "vodafone HU"
}
+ carrier_attribute {
+ mccmnc_tuple: "21670"
+ imsi_prefix_xpattern: "21670xx2"
+ }
}
carrier_id {
canonical_id: 1536
@@ -3841,6 +3850,27 @@
mccmnc_tuple: "40452"
mccmnc_tuple: "40467"
mccmnc_tuple: "40485"
+ mccmnc_tuple: "40501"
+ mccmnc_tuple: "40503"
+ mccmnc_tuple: "40504"
+ mccmnc_tuple: "40505"
+ mccmnc_tuple: "40506"
+ mccmnc_tuple: "40507"
+ mccmnc_tuple: "40508"
+ mccmnc_tuple: "40509"
+ mccmnc_tuple: "40510"
+ mccmnc_tuple: "40511"
+ mccmnc_tuple: "40512"
+ mccmnc_tuple: "40513"
+ mccmnc_tuple: "40514"
+ mccmnc_tuple: "40515"
+ mccmnc_tuple: "40517"
+ mccmnc_tuple: "40518"
+ mccmnc_tuple: "40519"
+ mccmnc_tuple: "40520"
+ mccmnc_tuple: "40521"
+ mccmnc_tuple: "40522"
+ mccmnc_tuple: "40523"
}
}
carrier_id {
@@ -3873,6 +3903,7 @@
mccmnc_tuple: "40475"
mccmnc_tuple: "40476"
mccmnc_tuple: "40477"
+ mccmnc_tuple: "40479"
mccmnc_tuple: "40480"
mccmnc_tuple: "40481"
}
@@ -3889,6 +3920,24 @@
carrier_name: "Aircel Ltd."
carrier_attribute {
mccmnc_tuple: "40442"
+ mccmnc_tuple: "40425"
+ mccmnc_tuple: "40428"
+ mccmnc_tuple: "40429"
+ mccmnc_tuple: "40437"
+ mccmnc_tuple: "40491"
+ mccmnc_tuple: "405800"
+ mccmnc_tuple: "405801"
+ mccmnc_tuple: "405802"
+ mccmnc_tuple: "405803"
+ mccmnc_tuple: "405804"
+ mccmnc_tuple: "405805"
+ mccmnc_tuple: "405806"
+ mccmnc_tuple: "405807"
+ mccmnc_tuple: "405808"
+ mccmnc_tuple: "405809"
+ mccmnc_tuple: "405810"
+ mccmnc_tuple: "405811"
+ mccmnc_tuple: "405812"
}
}
carrier_id {
@@ -4009,6 +4058,7 @@
carrier_name: "Mossel (Jamaica) Ltd."
carrier_attribute {
mccmnc_tuple: "338050"
+ mccmnc_tuple: "33805"
}
}
carrier_id {
@@ -4171,6 +4221,7 @@
carrier_name: "Vodacom Lesotho (pty) Ltd."
carrier_attribute {
mccmnc_tuple: "65101"
+ spn: "Vodacom"
}
}
carrier_id {
@@ -4255,6 +4306,7 @@
carrier_name: "CTM GSM"
carrier_attribute {
mccmnc_tuple: "45501"
+ mccmnc_tuple: "45504"
}
}
carrier_id {
@@ -4290,6 +4342,7 @@
carrier_name: "Vodafone"
carrier_attribute {
mccmnc_tuple: "27801"
+ spn: "vodafone MT"
}
}
carrier_id {
@@ -4360,6 +4413,8 @@
carrier_name: "Digi"
carrier_attribute {
mccmnc_tuple: "50210"
+ mccmnc_tuple: "502143"
+ mccmnc_tuple: "502146"
mccmnc_tuple: "50216"
}
}
@@ -4397,6 +4452,7 @@
carrier_name: "Vodacom"
carrier_attribute {
mccmnc_tuple: "64304"
+ spn: "Vodacom"
}
}
carrier_id {
@@ -4446,6 +4502,7 @@
carrier_name: "Servicios de Comunicaciones, S.A. (SERCOM)"
carrier_attribute {
mccmnc_tuple: "71073"
+ mccmnc_tuple: "710730"
}
}
carrier_id {
@@ -4486,15 +4543,9 @@
}
carrier_id {
canonical_id: 1649
- carrier_name: "Bmobile"
+ carrier_name: "Vodafone"
carrier_attribute {
mccmnc_tuple: "53701"
- }
-}
-carrier_id {
- canonical_id: 1650
- carrier_name: "Greencom"
- carrier_attribute {
mccmnc_tuple: "53702"
}
}
@@ -4530,6 +4581,10 @@
canonical_id: 1655
carrier_name: "Sun"
carrier_attribute {
+ mccmnc_tuple: "51503"
+ imsi_prefix_xpattern: "5150363"
+ }
+ carrier_attribute {
mccmnc_tuple: "51505"
}
}
@@ -4538,6 +4593,7 @@
carrier_name: "Jazz"
carrier_attribute {
mccmnc_tuple: "41001"
+ mccmnc_tuple: "41007"
}
}
carrier_id {
@@ -4619,7 +4675,7 @@
}
carrier_id {
canonical_id: 1668
- carrier_name: "Optimus - Telecomunicações, S.A."
+ carrier_name: "NOS"
carrier_attribute {
mccmnc_tuple: "26803"
}
@@ -4633,7 +4689,7 @@
}
carrier_id {
canonical_id: 1670
- carrier_name: "TMN - Telecomunicações Móveis Nacionais, S.A."
+ carrier_name: "MEO"
carrier_attribute {
mccmnc_tuple: "26806"
}
@@ -4778,6 +4834,10 @@
carrier_attribute {
mccmnc_tuple: "24001"
}
+ carrier_attribute {
+ mccmnc_tuple: "24005"
+ spn: "Telia"
+ }
}
carrier_id {
canonical_id: 1691
@@ -4817,10 +4877,14 @@
}
carrier_id {
canonical_id: 1696
- carrier_name: "Tele2/Comviq Sverige"
+ carrier_name: "Tele2/Comviq Sverige/Com Hem"
carrier_attribute {
mccmnc_tuple: "24007"
}
+ carrier_attribute {
+ mccmnc_tuple: "24024"
+ spn: "Tele2"
+ }
}
carrier_id {
canonical_id: 1697
@@ -5115,6 +5179,14 @@
carrier_name: "TSTT Mobile"
carrier_attribute {
mccmnc_tuple: "37412"
+ mccmnc_tuple: "374122"
+ mccmnc_tuple: "374123"
+ mccmnc_tuple: "374124"
+ mccmnc_tuple: "374125"
+ mccmnc_tuple: "374126"
+ mccmnc_tuple: "374127"
+ mccmnc_tuple: "374128"
+ mccmnc_tuple: "374129"
}
}
carrier_id {
@@ -5122,6 +5194,7 @@
carrier_name: "Digicel Trinidad and Tobago Ltd."
carrier_attribute {
mccmnc_tuple: "374130"
+ mccmnc_tuple: "37413"
}
}
carrier_id {
@@ -5150,6 +5223,7 @@
carrier_name: "Vodacom (T) Ltd."
carrier_attribute {
mccmnc_tuple: "64004"
+ spn: "VodaCom Tanzania"
}
}
carrier_id {
@@ -5675,7 +5749,7 @@
}
carrier_id {
canonical_id: 1827
- carrier_name: "Taylor Telecommunications Ltd."
+ carrier_name: "Mid-Rivers"
carrier_attribute {
mccmnc_tuple: "310900"
}
@@ -5689,7 +5763,7 @@
}
carrier_id {
canonical_id: 1829
- carrier_name: "Get Mobile"
+ carrier_name: "James Valley"
carrier_attribute {
mccmnc_tuple: "310920"
}
@@ -5731,11 +5805,15 @@
}
carrier_id {
canonical_id: 1836
- carrier_name: "Cellular South Inc."
+ carrier_name: "C Spire"
carrier_attribute {
mccmnc_tuple: "311230"
mccmnc_tuple: "310023"
}
+ carrier_attribute {
+ mccmnc_tuple: "20404"
+ spn: "C Spire"
+ }
}
carrier_id {
canonical_id: 1837
@@ -5759,7 +5837,6 @@
gid1: "BAE0000000000000"
}
carrier_attribute {
- mccmnc_tuple: "310004"
mccmnc_tuple: "310012"
mccmnc_tuple: "311280"
mccmnc_tuple: "311281"
@@ -5834,16 +5911,17 @@
}
carrier_id {
canonical_id: 1847
- carrier_name: "Northwest Missouri Cellular Limited Partnership"
+ carrier_name: "northwestcell"
carrier_attribute {
mccmnc_tuple: "311420"
}
}
carrier_id {
canonical_id: 1848
- carrier_name: "RSA 1 Limited Partnership dba Cellular 29 Plus"
+ carrier_name: "Chat Mobility"
carrier_attribute {
mccmnc_tuple: "311430"
+ mccmnc_tuple: "312160"
}
}
carrier_id {
@@ -5855,7 +5933,7 @@
}
carrier_id {
canonical_id: 1850
- carrier_name: "Panhandle Telecommunication Systems Inc."
+ carrier_name: "PTCI"
carrier_attribute {
mccmnc_tuple: "311450"
}
@@ -5880,6 +5958,11 @@
carrier_attribute {
mccmnc_tuple: "311490"
}
+ carrier_attribute {
+ mccmnc_tuple: "310120"
+ mccmnc_tuple: "312530"
+ gid1: "000003"
+ }
}
carrier_id {
canonical_id: 1854
@@ -6046,7 +6129,7 @@
}
carrier_id {
canonical_id: 1877
- carrier_name: "Yemen Mobile Phone Company"
+ carrier_name: "SabaFon"
carrier_attribute {
mccmnc_tuple: "42101"
}
@@ -6077,7 +6160,7 @@
carrier_name: "遠傳電信 Far EasTone Telecom"
carrier_attribute {
mccmnc_tuple: "46601"
- mccmnc_tuple: "46688"
+ mccmnc_tuple: "46603"
}
}
carrier_id {
@@ -6164,7 +6247,7 @@
}
carrier_id {
canonical_id: 1894
- carrier_name: "SoftBank・Y!mobile"
+ carrier_name: "SoftBank"
carrier_attribute {
mccmnc_tuple: "44020"
}
@@ -6175,6 +6258,11 @@
carrier_attribute {
mccmnc_tuple: "302490"
}
+ carrier_attribute {
+ mccmnc_tuple: "22201"
+ mccmnc_tuple: "22288"
+ gid1: "ffffff00"
+ }
}
carrier_id {
canonical_id: 1896
@@ -6276,6 +6364,10 @@
mccmnc_tuple: "21406"
mccmnc_tuple: "21408"
}
+ carrier_attribute {
+ mccmnc_tuple: "21403"
+ imsi_prefix_xpattern: "2140359"
+ }
}
carrier_id {
canonical_id: 1910
@@ -6299,6 +6391,11 @@
mccmnc_tuple: "334090"
mccmnc_tuple: "33409"
}
+ carrier_attribute {
+ mccmnc_tuple: "22201"
+ spn: "IUSACELL"
+ spn: "UNEFON"
+ }
}
carrier_id {
canonical_id: 1913
@@ -6398,6 +6495,7 @@
carrier_name: "Digicel"
carrier_attribute {
mccmnc_tuple: "71404"
+ mccmnc_tuple: "714040"
}
}
carrier_id {
@@ -6416,14 +6514,14 @@
}
carrier_id {
canonical_id: 1929
- carrier_name: "Telefonica Moviles Peru"
+ carrier_name: "Movistar"
carrier_attribute {
mccmnc_tuple: "71606"
}
}
carrier_id {
canonical_id: 1930
- carrier_name: "Nextel"
+ carrier_name: "Entel"
carrier_attribute {
mccmnc_tuple: "71617"
}
@@ -6559,6 +6657,22 @@
carrier_name: "MetroPCS"
carrier_attribute {
mccmnc_tuple: "310260"
+ mccmnc_tuple: "310800"
+ mccmnc_tuple: "310660"
+ mccmnc_tuple: "310640"
+ mccmnc_tuple: "310590"
+ mccmnc_tuple: "310530"
+ mccmnc_tuple: "310490"
+ mccmnc_tuple: "310310"
+ mccmnc_tuple: "310300"
+ mccmnc_tuple: "310270"
+ mccmnc_tuple: "310250"
+ mccmnc_tuple: "310240"
+ mccmnc_tuple: "310230"
+ mccmnc_tuple: "310220"
+ mccmnc_tuple: "310210"
+ mccmnc_tuple: "310200"
+ mccmnc_tuple: "310160"
gid1: "6D38"
}
}
@@ -6575,13 +6689,40 @@
carrier_attribute {
mccmnc_tuple: "311870"
}
+ carrier_attribute {
+ mccmnc_tuple: "310120"
+ mccmnc_tuple: "312530"
+ gid1: "000002"
+ }
}
carrier_id {
canonical_id: 1952
carrier_name: "U.S. Cellular"
carrier_attribute {
mccmnc_tuple: "311580"
+ mccmnc_tuple: "311581"
+ mccmnc_tuple: "311582"
+ mccmnc_tuple: "311583"
+ mccmnc_tuple: "311584"
+ mccmnc_tuple: "311585"
+ mccmnc_tuple: "311586"
+ mccmnc_tuple: "311587"
+ mccmnc_tuple: "311588"
+ mccmnc_tuple: "311589"
mccmnc_tuple: "311220"
+ mccmnc_tuple: "311221"
+ mccmnc_tuple: "311222"
+ mccmnc_tuple: "311223"
+ mccmnc_tuple: "311224"
+ mccmnc_tuple: "311225"
+ mccmnc_tuple: "311226"
+ mccmnc_tuple: "311227"
+ mccmnc_tuple: "311228"
+ mccmnc_tuple: "311229"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "20404"
+ spn: "U.S. Cellular"
}
}
carrier_id {
@@ -6710,6 +6851,7 @@
mccmnc_tuple: "405754"
mccmnc_tuple: "405755"
mccmnc_tuple: "405756"
+ spn: "Vodafone IN"
}
}
carrier_id {
@@ -6789,6 +6931,12 @@
carrier_attribute {
mccmnc_tuple: "21421"
}
+ carrier_attribute {
+ mccmnc_tuple: "21403"
+ mccmnc_tuple: "21407"
+ spn: "JAZZTEL"
+ spn: "Jazztel"
+ }
}
carrier_id {
canonical_id: 1975
@@ -6834,13 +6982,6 @@
}
}
carrier_id {
- canonical_id: 1981
- carrier_name: "WaridTel"
- carrier_attribute {
- mccmnc_tuple: "41007"
- }
-}
-carrier_id {
canonical_id: 1982
carrier_name: "TATA DOCOMO"
carrier_attribute {
@@ -6859,6 +7000,7 @@
mccmnc_tuple: "405037"
mccmnc_tuple: "405038"
mccmnc_tuple: "405039"
+ mccmnc_tuple: "405040"
mccmnc_tuple: "405041"
mccmnc_tuple: "405042"
mccmnc_tuple: "405043"
@@ -6884,7 +7026,7 @@
}
carrier_id {
canonical_id: 1985
- carrier_name: "Wana"
+ carrier_name: "Inwi"
carrier_attribute {
mccmnc_tuple: "60402"
}
@@ -6912,7 +7054,7 @@
}
carrier_id {
canonical_id: 1989
- carrier_name: "Project Fi"
+ carrier_name: "Google Fi"
carrier_attribute {
mccmnc_tuple: "23420"
gid1: "0306"
@@ -6922,9 +7064,23 @@
gid1: "0206"
}
carrier_attribute {
+ mccmnc_tuple: "23210"
+ gid1: "0306"
+ }
+ carrier_attribute {
mccmnc_tuple: "310120"
spn: "Fi Network"
spn: "nova"
+ spn: "Google Fi"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "310120"
+ gid1: "A00184"
+ gid1: "A00184FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "310120"
+ privilege_access_rule: "4C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859"
}
carrier_attribute {
mccmnc_tuple: "310260"
@@ -6949,6 +7105,10 @@
mccmnc_tuple: "42507"
mccmnc_tuple: "42577"
}
+ carrier_attribute {
+ mccmnc_tuple: "20404"
+ spn: "HOT mobile"
+ }
}
carrier_id {
canonical_id: 1992
@@ -6997,6 +7157,7 @@
carrier_name: "Vodafone"
carrier_attribute {
mccmnc_tuple: "42702"
+ spn: "Vodafone Qatar"
}
}
carrier_id {
@@ -7029,7 +7190,7 @@
}
carrier_id {
canonical_id: 2003
- carrier_name: "movistar"
+ carrier_name: "Movistar"
carrier_attribute {
mccmnc_tuple: "71204"
}
@@ -7047,6 +7208,11 @@
carrier_attribute {
mccmnc_tuple: "20826"
}
+ carrier_attribute {
+ mccmnc_tuple: "20801"
+ mccmnc_tuple: "20810"
+ gid1: "4E"
+ }
}
carrier_id {
canonical_id: 2006
@@ -7073,7 +7239,7 @@
}
carrier_id {
canonical_id: 2009
- carrier_name: "Movistar (Telefonica Moviles El Salvador)"
+ carrier_name: "Movistar"
carrier_attribute {
mccmnc_tuple: "70604"
mccmnc_tuple: "706040"
@@ -7151,7 +7317,7 @@
mccmnc_tuple: "302221"
mccmnc_tuple: "30222"
mccmnc_tuple: "30286"
- gid1: "4b4f"
+ gid1: "4B4F"
}
}
carrier_id {
@@ -7323,6 +7489,7 @@
}
carrier_attribute {
mccmnc_tuple: "302220"
+ mccmnc_tuple: "302221"
gid1: "5043"
}
}
@@ -7341,9 +7508,16 @@
canonical_id: 2055
carrier_name: "Chatr Mobile"
carrier_attribute {
+ mccmnc_tuple: "302320"
+ }
+ carrier_attribute {
mccmnc_tuple: "302720"
imsi_prefix_xpattern: "302720x94"
- gid1: "ff"
+ imsi_prefix_xpattern: "302720x84"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "302720"
+ gid1: "d2"
}
}
carrier_id {
@@ -7352,7 +7526,10 @@
carrier_attribute {
mccmnc_tuple: "302720"
imsi_prefix_xpattern: "302720x98"
- gid1: "ff"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "302720"
+ gid1: "d4"
}
}
carrier_id {
@@ -7363,6 +7540,10 @@
imsi_prefix_xpattern: "302720x97"
gid1: "cc"
}
+ carrier_attribute {
+ mccmnc_tuple: "302720"
+ spn: "CITYFONE"
+ }
}
carrier_id {
canonical_id: 2060
@@ -7565,7 +7746,8 @@
carrier_name: "Public Mobile"
carrier_attribute {
mccmnc_tuple: "302220"
- gid1: "4d4f"
+ mccmnc_tuple: "302221"
+ gid1: "4D4F"
}
}
carrier_id {
@@ -7700,6 +7882,10 @@
mccmnc_tuple: "44010"
preferred_apn: "iijmio.jp"
}
+ carrier_attribute {
+ mccmnc_tuple: "20601"
+ spn: "IIJ"
+ }
}
carrier_id {
canonical_id: 2107
@@ -7710,8 +7896,9 @@
}
carrier_id {
canonical_id: 2108
- carrier_name: "Airtel"
+ carrier_name: "AirtelTigo"
carrier_attribute {
+ mccmnc_tuple: "62003"
mccmnc_tuple: "62006"
}
}
@@ -7723,11 +7910,16 @@
preferred_apn: "rmobile.co"
preferred_apn: "rmobile.jp"
}
+ carrier_attribute {
+ mccmnc_tuple: "44051"
+ preferred_apn: "a.rmobile.jp"
+ }
}
carrier_id {
canonical_id: 2110
carrier_name: "UQ mobile"
carrier_attribute {
+ mccmnc_tuple: "44050"
mccmnc_tuple: "44051"
preferred_apn: "uqmobile.jp"
}
@@ -7745,12 +7937,25 @@
canonical_id: 2112
carrier_name: "Telecable"
carrier_attribute {
- mccmnc_tuple: "21405"
mccmnc_tuple: "21416"
spn: "telecable"
spn: "Telecable"
spn: "TeleCable"
}
+ carrier_attribute {
+ mccmnc_tuple: "21405"
+ imsi_prefix_xpattern: "214050111"
+ spn: "telecable"
+ spn: "Telecable"
+ spn: "TeleCable"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "21406"
+ imsi_prefix_xpattern: "2140613"
+ spn: "telecable"
+ spn: "Telecable"
+ spn: "TeleCable"
+ }
}
carrier_id {
canonical_id: 2113
@@ -7802,5 +8007,2241 @@
mccmnc_tuple: "23410"
gid1: "508"
}
+ carrier_attribute {
+ mccmnc_tuple: "23410"
+ spn: "giffgaff"
+ }
}
-version: 4
+carrier_id {
+ canonical_id: 2119
+ carrier_name: "FirstNet"
+ carrier_attribute {
+ mccmnc_tuple: "313100"
+ }
+}
+carrier_id {
+ canonical_id: 2120
+ carrier_name: "FirstNet (Lab)"
+ carrier_attribute {
+ mccmnc_tuple: "312670"
+ }
+}
+carrier_id {
+ canonical_id: 2121
+ carrier_name: "mineo"
+ carrier_attribute {
+ mccmnc_tuple: "44010"
+ preferred_apn: "mineo-d.jp"
+ }
+}
+carrier_id {
+ canonical_id: 2122
+ carrier_name: "Lucky Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "30264"
+ mccmnc_tuple: "302610"
+ mccmnc_tuple: "302630"
+ mccmnc_tuple: "302640"
+ gid1: "42"
+ }
+}
+carrier_id {
+ canonical_id: 2123
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "54002"
+ }
+}
+carrier_id {
+ canonical_id: 2124
+ carrier_name: "Iliad"
+ carrier_attribute {
+ mccmnc_tuple: "22250"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "20815"
+ gid1: "F003"
+ }
+}
+carrier_id {
+ canonical_id: 2125
+ carrier_name: "Simyo"
+ carrier_attribute {
+ mccmnc_tuple: "21419"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "21403"
+ spn: "simyo"
+ }
+}
+carrier_id {
+ canonical_id: 2126
+ carrier_name: "Spectrum Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "311480"
+ gid1: "BA01490000000000"
+ }
+}
+carrier_id {
+ canonical_id: 2127
+ carrier_name: "Free RE"
+ carrier_attribute {
+ mccmnc_tuple: "64703"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "20815"
+ gid1: "F2330002"
+ }
+}
+carrier_id {
+ canonical_id: 2128
+ carrier_name: "Sprint Wholesale"
+ carrier_attribute {
+ mccmnc_tuple: "310120"
+ gid1: "A00022FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00305FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00209FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00250FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00207FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00105FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00181FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00263FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00271FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00151FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00058FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00301FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00143FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00247FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00274FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00042FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00092FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00312FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00050FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ gid1: "A00034FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ }
+}
+carrier_id {
+ canonical_id: 2129
+ carrier_name: "ALDI mobile"
+ carrier_attribute {
+ mccmnc_tuple: "50501"
+ spn: "aldimobile"
+ }
+}
+carrier_id {
+ canonical_id: 2130
+ carrier_name: "ALIV BS"
+ carrier_attribute {
+ mccmnc_tuple: "20809"
+ imsi_prefix_xpattern: "208090021"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "36449"
+ }
+}
+carrier_id {
+ canonical_id: 2131
+ carrier_name: "Altecom"
+ carrier_attribute {
+ mccmnc_tuple: "21402"
+ }
+}
+carrier_id {
+ canonical_id: 2132
+ carrier_name: "Bics"
+ carrier_attribute {
+ mccmnc_tuple: "20628"
+ mccmnc_tuple: "90158"
+ }
+}
+carrier_id {
+ canonical_id: 2133
+ carrier_name: "Carrefour"
+ carrier_attribute {
+ mccmnc_tuple: "21403"
+ imsi_prefix_xpattern: "2140352"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "20801"
+ gid1: "33"
+ }
+}
+carrier_id {
+ canonical_id: 2134
+ carrier_name: "com4"
+ carrier_attribute {
+ mccmnc_tuple: "24209"
+ }
+}
+carrier_id {
+ canonical_id: 2135
+ carrier_name: "Coriolis"
+ carrier_attribute {
+ mccmnc_tuple: "20827"
+ spn: "Coriolis"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "20810"
+ mccmnc_tuple: "64710"
+ gid1: "12"
+ }
+}
+carrier_id {
+ canonical_id: 2136
+ carrier_name: "Fibracat"
+ carrier_attribute {
+ mccmnc_tuple: "21402"
+ spn: "fibracat"
+ }
+}
+carrier_id {
+ canonical_id: 2137
+ carrier_name: "Gamma"
+ carrier_attribute {
+ mccmnc_tuple: "23439"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "24007"
+ imsi_prefix_xpattern: "24007561"
+ }
+}
+carrier_id {
+ canonical_id: 2138
+ carrier_name: "Jump"
+ carrier_attribute {
+ mccmnc_tuple: "23410"
+ mccmnc_tuple: "23439"
+ mccmnc_tuple: "24007"
+ spn: "jump"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "20404"
+ spn: "Jump"
+ }
+}
+carrier_id {
+ canonical_id: 2139
+ carrier_name: "Sky"
+ carrier_attribute {
+ mccmnc_tuple: "23457"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "24007"
+ gid1: "0c"
+ }
+}
+carrier_id {
+ canonical_id: 2140
+ carrier_name: "Smarty"
+ carrier_attribute {
+ mccmnc_tuple: "23420"
+ gid1: "0309"
+ }
+}
+carrier_id {
+ canonical_id: 2141
+ carrier_name: "Superdrug"
+ carrier_attribute {
+ mccmnc_tuple: "23420"
+ gid1: "0310"
+ }
+}
+carrier_id {
+ canonical_id: 2142
+ carrier_name: "Tinkoff"
+ carrier_attribute {
+ mccmnc_tuple: "25062"
+ }
+}
+carrier_id {
+ canonical_id: 2143
+ carrier_name: "Truphone"
+ carrier_attribute {
+ mccmnc_tuple: "20433"
+ mccmnc_tuple: "21427"
+ mccmnc_tuple: "23425"
+ mccmnc_tuple: "26033"
+ mccmnc_tuple: "26242"
+ mccmnc_tuple: "31030"
+ mccmnc_tuple: "50538"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "20404"
+ imsi_prefix_xpattern: "204043914"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "23450"
+ imsi_prefix_xpattern: "234500008"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "50502"
+ imsi_prefix_xpattern: "50502100"
+ }
+}
+carrier_id {
+ canonical_id: 2144
+ carrier_name: "Unleashed"
+ carrier_attribute {
+ mccmnc_tuple: "20630"
+ }
+}
+carrier_id {
+ canonical_id: 2145
+ carrier_name: "Virgin"
+ carrier_attribute {
+ mccmnc_tuple: "50502"
+ imsi_prefix_xpattern: "505029"
+ }
+}
+carrier_id {
+ canonical_id: 2146
+ carrier_name: "Visible"
+ carrier_attribute {
+ mccmnc_tuple: "311480"
+ gid1: "bae1000000000000"
+ }
+}
+carrier_id {
+ canonical_id: 2147
+ carrier_name: "Vodafone Lab"
+ carrier_attribute {
+ mccmnc_tuple: "26209"
+ }
+}
+carrier_id {
+ canonical_id: 2148
+ carrier_name: "BIGLOBE"
+ carrier_attribute {
+ mccmnc_tuple: "44010"
+ preferred_apn: "biglobe.jp"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "44051"
+ preferred_apn: "biglobe.jp"
+ }
+}
+carrier_id {
+ canonical_id: 2149
+ carrier_name: "Voiceworks"
+ carrier_attribute {
+ mccmnc_tuple: "20403"
+ }
+}
+carrier_id {
+ canonical_id: 2150
+ carrier_name: "Telenet"
+ carrier_attribute {
+ mccmnc_tuple: "20605"
+ }
+}
+carrier_id {
+ canonical_id: 2151
+ carrier_name: "Legos"
+ carrier_attribute {
+ mccmnc_tuple: "20817"
+ }
+}
+carrier_id {
+ canonical_id: 2152
+ carrier_name: "Lycamobile"
+ carrier_attribute {
+ mccmnc_tuple: "23426"
+ spn: "Lycamobile"
+ }
+}
+carrier_id {
+ canonical_id: 2153
+ carrier_name: "Unité"
+ carrier_attribute {
+ mccmnc_tuple: "25905"
+ }
+}
+carrier_id {
+ canonical_id: 2154
+ carrier_name: "Tesco"
+ carrier_attribute {
+ mccmnc_tuple: "27211"
+ gid1: "0A"
+ }
+}
+carrier_id {
+ canonical_id: 2155
+ carrier_name: "Nova"
+ carrier_attribute {
+ mccmnc_tuple: "27411"
+ }
+}
+carrier_id {
+ canonical_id: 2156
+ carrier_name: "PrimeTel"
+ carrier_attribute {
+ mccmnc_tuple: "28020"
+ }
+}
+carrier_id {
+ canonical_id: 2157
+ carrier_name: "IT&E OverSeas"
+ carrier_attribute {
+ mccmnc_tuple: "310032"
+ spn: "CdmaNai"
+ }
+}
+carrier_id {
+ canonical_id: 2158
+ carrier_name: "North Dakota Network Company"
+ carrier_attribute {
+ mccmnc_tuple: "311610"
+ spn: "srtcomm"
+ }
+}
+carrier_id {
+ canonical_id: 2159
+ carrier_name: "United Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "311650"
+ spn: "unitedwireless"
+ }
+}
+carrier_id {
+ canonical_id: 2160
+ carrier_name: "Pine Belt"
+ carrier_attribute {
+ mccmnc_tuple: "311670"
+ spn: "pinebelt"
+ }
+}
+carrier_id {
+ canonical_id: 2161
+ carrier_name: "Missouri RSA No 5 Partnership"
+ carrier_attribute {
+ mccmnc_tuple: "311920"
+ spn: "CdmaNai"
+ }
+}
+carrier_id {
+ canonical_id: 2162
+ carrier_name: "Custer"
+ carrier_attribute {
+ mccmnc_tuple: "312040"
+ spn: "custer"
+ }
+}
+carrier_id {
+ canonical_id: 2163
+ carrier_name: "Digicel"
+ carrier_attribute {
+ mccmnc_tuple: "36302"
+ mccmnc_tuple: "363020"
+ }
+}
+carrier_id {
+ canonical_id: 2164
+ carrier_name: "ALTEL"
+ carrier_attribute {
+ mccmnc_tuple: "40107"
+ }
+}
+carrier_id {
+ canonical_id: 2165
+ carrier_name: "TashiCell"
+ carrier_attribute {
+ mccmnc_tuple: "40277"
+ }
+}
+carrier_id {
+ canonical_id: 2166
+ carrier_name: "SanaTel"
+ carrier_attribute {
+ mccmnc_tuple: "41808"
+ }
+}
+carrier_id {
+ canonical_id: 2167
+ carrier_name: "Omnnea"
+ carrier_attribute {
+ mccmnc_tuple: "41892"
+ }
+}
+carrier_id {
+ canonical_id: 2168
+ carrier_name: "HiTS-UNITEL"
+ carrier_attribute {
+ mccmnc_tuple: "42104"
+ }
+}
+carrier_id {
+ canonical_id: 2169
+ carrier_name: "Alon Cellular Ltd"
+ carrier_attribute {
+ mccmnc_tuple: "42514"
+ }
+}
+carrier_id {
+ canonical_id: 2170
+ carrier_name: "Home Cellular"
+ carrier_attribute {
+ mccmnc_tuple: "42515"
+ }
+}
+carrier_id {
+ canonical_id: 2171
+ carrier_name: "Rami Levy"
+ carrier_attribute {
+ mccmnc_tuple: "42516"
+ }
+}
+carrier_id {
+ canonical_id: 2172
+ carrier_name: "Skytel"
+ carrier_attribute {
+ mccmnc_tuple: "42891"
+ }
+}
+carrier_id {
+ canonical_id: 2173
+ carrier_name: "G.Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "42898"
+ }
+}
+carrier_id {
+ canonical_id: 2174
+ carrier_name: "Tcell"
+ carrier_attribute {
+ mccmnc_tuple: "43612"
+ }
+}
+carrier_id {
+ canonical_id: 2175
+ carrier_name: "AkTel LLC"
+ carrier_attribute {
+ mccmnc_tuple: "43703"
+ }
+}
+carrier_id {
+ canonical_id: 2176
+ carrier_name: "MegaCom"
+ carrier_attribute {
+ mccmnc_tuple: "43705"
+ }
+}
+carrier_id {
+ canonical_id: 2177
+ carrier_name: "O!"
+ carrier_attribute {
+ mccmnc_tuple: "43709"
+ }
+}
+carrier_id {
+ canonical_id: 2178
+ carrier_name: "Gmobile"
+ carrier_attribute {
+ mccmnc_tuple: "45207"
+ }
+}
+carrier_id {
+ canonical_id: 2179
+ carrier_name: "Viettel Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "45208"
+ }
+}
+carrier_id {
+ canonical_id: 2180
+ carrier_name: "qb"
+ carrier_attribute {
+ mccmnc_tuple: "45604"
+ }
+}
+carrier_id {
+ canonical_id: 2181
+ carrier_name: "Metfone"
+ carrier_attribute {
+ mccmnc_tuple: "45608"
+ }
+}
+carrier_id {
+ canonical_id: 2182
+ carrier_name: "Telkomcel"
+ carrier_attribute {
+ mccmnc_tuple: "51401"
+ }
+}
+carrier_id {
+ canonical_id: 2183
+ carrier_name: "Timor Telecom"
+ carrier_attribute {
+ mccmnc_tuple: "51402"
+ }
+}
+carrier_id {
+ canonical_id: 2184
+ carrier_name: "Redinternet"
+ carrier_attribute {
+ mccmnc_tuple: "51518"
+ }
+}
+carrier_id {
+ canonical_id: 2185
+ carrier_name: "BREEZE"
+ carrier_attribute {
+ mccmnc_tuple: "54001"
+ }
+}
+carrier_id {
+ canonical_id: 2186
+ carrier_name: "Digicel"
+ carrier_attribute {
+ mccmnc_tuple: "54202"
+ }
+}
+carrier_id {
+ canonical_id: 2187
+ carrier_name: "Kiribati Frigate"
+ carrier_attribute {
+ mccmnc_tuple: "54509"
+ }
+}
+carrier_id {
+ canonical_id: 2188
+ carrier_name: "Palau Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "55280"
+ }
+}
+carrier_id {
+ canonical_id: 2189
+ carrier_name: "Al-Jeel"
+ carrier_attribute {
+ mccmnc_tuple: "60602"
+ }
+}
+carrier_id {
+ canonical_id: 2190
+ carrier_name: "Hatef"
+ carrier_attribute {
+ mccmnc_tuple: "60606"
+ }
+}
+carrier_id {
+ canonical_id: 2191
+ carrier_name: "QCell"
+ carrier_attribute {
+ mccmnc_tuple: "60704"
+ }
+}
+carrier_id {
+ canonical_id: 2192
+ carrier_name: "Expresso"
+ carrier_attribute {
+ mccmnc_tuple: "60803"
+ }
+}
+carrier_id {
+ canonical_id: 2193
+ carrier_name: "Intercel"
+ carrier_attribute {
+ mccmnc_tuple: "61103"
+ }
+}
+carrier_id {
+ canonical_id: 2194
+ carrier_name: "Lonestar"
+ carrier_attribute {
+ mccmnc_tuple: "61801"
+ }
+}
+carrier_id {
+ canonical_id: 2195
+ carrier_name: "Libercell"
+ carrier_attribute {
+ mccmnc_tuple: "61802"
+ }
+}
+carrier_id {
+ canonical_id: 2196
+ carrier_name: "LIBTELCO"
+ carrier_attribute {
+ mccmnc_tuple: "61820"
+ }
+}
+carrier_id {
+ canonical_id: 2197
+ carrier_name: "Salam"
+ carrier_attribute {
+ mccmnc_tuple: "62204"
+ }
+}
+carrier_id {
+ canonical_id: 2198
+ carrier_name: "Nationlink"
+ carrier_attribute {
+ mccmnc_tuple: "62304"
+ }
+}
+carrier_id {
+ canonical_id: 2199
+ carrier_name: "Hits-GE"
+ carrier_attribute {
+ mccmnc_tuple: "62703"
+ }
+}
+carrier_id {
+ canonical_id: 2200
+ carrier_name: "Azur"
+ carrier_attribute {
+ mccmnc_tuple: "62804"
+ }
+}
+carrier_id {
+ canonical_id: 2201
+ carrier_name: "MOVICEL"
+ carrier_attribute {
+ mccmnc_tuple: "63104"
+ }
+}
+carrier_id {
+ canonical_id: 2202
+ carrier_name: "Sudani"
+ carrier_attribute {
+ mccmnc_tuple: "63407"
+ }
+}
+carrier_id {
+ canonical_id: 2203
+ carrier_name: "Tigo"
+ carrier_attribute {
+ mccmnc_tuple: "63513"
+ }
+}
+carrier_id {
+ canonical_id: 2204
+ carrier_name: "Airtel"
+ carrier_attribute {
+ mccmnc_tuple: "63514"
+ }
+}
+carrier_id {
+ canonical_id: 2205
+ carrier_name: "Telesom"
+ carrier_attribute {
+ mccmnc_tuple: "63701"
+ }
+}
+carrier_id {
+ canonical_id: 2206
+ carrier_name: "Nationlink"
+ carrier_attribute {
+ mccmnc_tuple: "63710"
+ }
+}
+carrier_id {
+ canonical_id: 2207
+ carrier_name: "Somtel"
+ carrier_attribute {
+ mccmnc_tuple: "63771"
+ }
+}
+carrier_id {
+ canonical_id: 2208
+ carrier_name: "Telcom"
+ carrier_attribute {
+ mccmnc_tuple: "63782"
+ }
+}
+carrier_id {
+ canonical_id: 2209
+ carrier_name: "Yu"
+ carrier_attribute {
+ mccmnc_tuple: "63905"
+ }
+}
+carrier_id {
+ canonical_id: 2210
+ carrier_name: "Telkom"
+ carrier_attribute {
+ mccmnc_tuple: "63907"
+ }
+}
+carrier_id {
+ canonical_id: 2211
+ carrier_name: "Lumitel/Viettel"
+ carrier_attribute {
+ mccmnc_tuple: "64208"
+ }
+}
+carrier_id {
+ canonical_id: 2212
+ carrier_name: "Leo"
+ carrier_attribute {
+ mccmnc_tuple: "64282"
+ }
+}
+carrier_id {
+ canonical_id: 2213
+ carrier_name: "Movitel"
+ carrier_attribute {
+ mccmnc_tuple: "64303"
+ }
+}
+carrier_id {
+ canonical_id: 2214
+ carrier_name: "switch"
+ carrier_attribute {
+ mccmnc_tuple: "64902"
+ }
+}
+carrier_id {
+ canonical_id: 2215
+ carrier_name: "Porto Seguro TeleComumicacoes"
+ carrier_attribute {
+ mccmnc_tuple: "72454"
+ }
+}
+carrier_id {
+ canonical_id: 2216
+ carrier_name: "VTR Banda Ancha SA"
+ carrier_attribute {
+ mccmnc_tuple: "73008"
+ }
+}
+carrier_id {
+ canonical_id: 2217
+ carrier_name: "GT&T Cellink Plus"
+ carrier_attribute {
+ mccmnc_tuple: "73802"
+ mccmnc_tuple: "738002"
+ }
+}
+carrier_id {
+ canonical_id: 2218
+ carrier_name: "ETB 4G"
+ carrier_attribute {
+ mccmnc_tuple: "732187"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "732103"
+ spn: "ETB MOVI"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "732111"
+ spn: "ETB MOVIL"
+ }
+}
+carrier_id {
+ canonical_id: 2219
+ carrier_name: "Digicel"
+ carrier_attribute {
+ mccmnc_tuple: "54105"
+ }
+}
+carrier_id {
+ canonical_id: 2220
+ carrier_name: "Digicel"
+ carrier_attribute {
+ mccmnc_tuple: "53988"
+ }
+}
+carrier_id {
+ canonical_id: 2221
+ carrier_name: "Etisalat"
+ carrier_attribute {
+ mccmnc_tuple: "41250"
+ }
+}
+carrier_id {
+ canonical_id: 2222
+ carrier_name: "Globacom"
+ carrier_attribute {
+ mccmnc_tuple: "62007"
+ }
+}
+carrier_id {
+ canonical_id: 2223
+ carrier_name: "Grenada:Lime"
+ carrier_attribute {
+ mccmnc_tuple: "352110"
+ }
+}
+carrier_id {
+ canonical_id: 2224
+ carrier_name: "Cable & Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "33818"
+ mccmnc_tuple: "338180"
+ }
+}
+carrier_id {
+ canonical_id: 2225
+ carrier_name: "MTN/Areeba"
+ carrier_attribute {
+ mccmnc_tuple: "61104"
+ }
+}
+carrier_id {
+ canonical_id: 2227
+ carrier_name: "Tigo/Milicom/Tchad Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "62203"
+ }
+}
+carrier_id {
+ canonical_id: 2229
+ carrier_name: "Rebtel"
+ carrier_attribute {
+ mccmnc_tuple: "26044"
+ }
+}
+carrier_id {
+ canonical_id: 2230
+ carrier_name: "bulsatcom"
+ carrier_attribute {
+ mccmnc_tuple: "28411"
+ }
+}
+carrier_id {
+ canonical_id: 2231
+ carrier_name: "MAX TELECOM"
+ carrier_attribute {
+ mccmnc_tuple: "28413"
+ }
+}
+carrier_id {
+ canonical_id: 2232
+ carrier_name: "BICS"
+ carrier_attribute {
+ mccmnc_tuple: "29509"
+ spn: "BICS"
+ }
+}
+carrier_id {
+ canonical_id: 2233
+ carrier_name: "EMnify"
+ carrier_attribute {
+ mccmnc_tuple: "29509"
+ }
+}
+carrier_id {
+ canonical_id: 2234
+ carrier_name: "Itisaluna"
+ carrier_attribute {
+ mccmnc_tuple: "41862"
+ }
+}
+carrier_id {
+ canonical_id: 2235
+ carrier_name: "Perfectum Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "43406"
+ }
+}
+carrier_id {
+ canonical_id: 2236
+ carrier_name: "SEATEL"
+ carrier_attribute {
+ mccmnc_tuple: "45611"
+ }
+}
+carrier_id {
+ canonical_id: 2237
+ carrier_name: "China Telecom"
+ carrier_attribute {
+ mccmnc_tuple: "46003"
+ mccmnc_tuple: "46005"
+ mccmnc_tuple: "46011"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "20404"
+ spn: "中国电信"
+ }
+}
+carrier_id {
+ canonical_id: 2238
+ carrier_name: "Sunsurf"
+ carrier_attribute {
+ mccmnc_tuple: "52504"
+ }
+}
+carrier_id {
+ canonical_id: 2239
+ carrier_name: "AIL"
+ carrier_attribute {
+ mccmnc_tuple: "54100"
+ }
+}
+carrier_id {
+ canonical_id: 2240
+ carrier_name: "Kiribati - TSKL"
+ carrier_attribute {
+ mccmnc_tuple: "54501"
+ }
+}
+carrier_id {
+ canonical_id: 2241
+ carrier_name: "Guinetel"
+ carrier_attribute {
+ mccmnc_tuple: "63207"
+ }
+}
+carrier_id {
+ canonical_id: 2242
+ carrier_name: "Privet"
+ carrier_attribute {
+ mccmnc_tuple: "63409"
+ }
+}
+carrier_id {
+ canonical_id: 2243
+ carrier_name: "Rwandatel"
+ carrier_attribute {
+ mccmnc_tuple: "63512"
+ }
+}
+carrier_id {
+ canonical_id: 2244
+ carrier_name: "Somafone"
+ carrier_attribute {
+ mccmnc_tuple: "63704"
+ }
+}
+carrier_id {
+ canonical_id: 2245
+ carrier_name: "Hormuud"
+ carrier_attribute {
+ mccmnc_tuple: "63725"
+ }
+}
+carrier_id {
+ canonical_id: 2246
+ carrier_name: "Unitel"
+ carrier_attribute {
+ mccmnc_tuple: "63757"
+ }
+}
+carrier_id {
+ canonical_id: 2247
+ carrier_name: "Eritel"
+ carrier_attribute {
+ mccmnc_tuple: "65701"
+ }
+}
+carrier_id {
+ canonical_id: 2248
+ carrier_name: "Digicel"
+ carrier_attribute {
+ mccmnc_tuple: "70804"
+ mccmnc_tuple: "708040"
+ }
+}
+carrier_id {
+ canonical_id: 2249
+ carrier_name: "Quam_WEB"
+ carrier_attribute {
+ mccmnc_tuple: "72201"
+ spn: "QUAM"
+ spn: "CELULAR"
+ }
+}
+carrier_id {
+ canonical_id: 2250
+ carrier_name: "Argentina:Nuestro"
+ carrier_attribute {
+ mccmnc_tuple: "72236"
+ }
+}
+carrier_id {
+ canonical_id: 2251
+ carrier_name: "Sberbank-Telecom"
+ carrier_attribute {
+ mccmnc_tuple: "250050"
+ }
+}
+carrier_id {
+ canonical_id: 2252
+ carrier_name: "EastLink"
+ carrier_attribute {
+ mccmnc_tuple: "302270"
+ }
+}
+carrier_id {
+ canonical_id: 2253
+ carrier_name: "ALU Test-SIM"
+ carrier_attribute {
+ mccmnc_tuple: "310028"
+ }
+}
+carrier_id {
+ canonical_id: 2254
+ carrier_name: "etex"
+ carrier_attribute {
+ mccmnc_tuple: "310035"
+ spn: "etex"
+ }
+}
+carrier_id {
+ canonical_id: 2255
+ carrier_name: "NexTech Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "311100"
+ }
+}
+carrier_id {
+ canonical_id: 2256
+ carrier_name: "TelAlaska Cellular"
+ carrier_attribute {
+ mccmnc_tuple: "311740"
+ }
+}
+carrier_id {
+ canonical_id: 2257
+ carrier_name: "Cleartalk"
+ carrier_attribute {
+ mccmnc_tuple: "311750"
+ }
+}
+carrier_id {
+ canonical_id: 2258
+ carrier_name: "MobileNation"
+ carrier_attribute {
+ mccmnc_tuple: "311910"
+ }
+}
+carrier_id {
+ canonical_id: 2259
+ carrier_name: "Syringa"
+ carrier_attribute {
+ mccmnc_tuple: "311930"
+ }
+}
+carrier_id {
+ canonical_id: 2260
+ carrier_name: "NexTech Ota"
+ carrier_attribute {
+ mccmnc_tuple: "312420"
+ }
+}
+carrier_id {
+ canonical_id: 2261
+ carrier_name: "Blue Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "312570"
+ }
+}
+carrier_id {
+ canonical_id: 2262
+ carrier_name: "Open Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "330000"
+ spn: "openmobile"
+ }
+}
+carrier_id {
+ canonical_id: 2263
+ carrier_name: "Claro"
+ carrier_attribute {
+ mccmnc_tuple: "338070"
+ }
+}
+carrier_id {
+ canonical_id: 2264
+ carrier_name: "Videocon"
+ carrier_attribute {
+ mccmnc_tuple: "405823"
+ mccmnc_tuple: "405824"
+ mccmnc_tuple: "405825"
+ mccmnc_tuple: "405826"
+ mccmnc_tuple: "405827"
+ mccmnc_tuple: "405828"
+ mccmnc_tuple: "405829"
+ mccmnc_tuple: "405830"
+ mccmnc_tuple: "405832"
+ mccmnc_tuple: "405833"
+ mccmnc_tuple: "405834"
+ mccmnc_tuple: "405835"
+ mccmnc_tuple: "405836"
+ mccmnc_tuple: "405837"
+ mccmnc_tuple: "405838"
+ mccmnc_tuple: "405841"
+ mccmnc_tuple: "405842"
+ mccmnc_tuple: "405843"
+ mccmnc_tuple: "405932"
+ }
+}
+carrier_id {
+ canonical_id: 2265
+ carrier_name: "STEL"
+ carrier_attribute {
+ mccmnc_tuple: "405881"
+ mccmnc_tuple: "405882"
+ mccmnc_tuple: "405883"
+ mccmnc_tuple: "405884"
+ mccmnc_tuple: "405885"
+ mccmnc_tuple: "405886"
+ }
+}
+carrier_id {
+ canonical_id: 2266
+ carrier_name: "Cheers"
+ carrier_attribute {
+ mccmnc_tuple: "405912"
+ mccmnc_tuple: "405913"
+ mccmnc_tuple: "405914"
+ mccmnc_tuple: "405915"
+ mccmnc_tuple: "405916"
+ mccmnc_tuple: "405917"
+ mccmnc_tuple: "405918"
+ mccmnc_tuple: "405919"
+ mccmnc_tuple: "405920"
+ mccmnc_tuple: "405921"
+ mccmnc_tuple: "405922"
+ mccmnc_tuple: "405923"
+ mccmnc_tuple: "405930"
+ }
+}
+carrier_id {
+ canonical_id: 2267
+ carrier_name: "Tuyo"
+ carrier_attribute {
+ mccmnc_tuple: "712019"
+ mccmnc_tuple: "712190"
+ }
+}
+carrier_id {
+ canonical_id: 2268
+ carrier_name: "Viettel"
+ carrier_attribute {
+ mccmnc_tuple: "51403"
+ }
+}
+carrier_id {
+ canonical_id: 2270
+ carrier_name: "Telenet"
+ carrier_attribute {
+ mccmnc_tuple: "20601"
+ imsi_prefix_xpattern: "20601889"
+ }
+}
+carrier_id {
+ canonical_id: 2271
+ carrier_name: "Transatel"
+ carrier_attribute {
+ mccmnc_tuple: "20610"
+ spn: "BE-Transatel"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "20610"
+ gid1: "BB00"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "22803"
+ spn: "CH-Transatel"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "22803"
+ gid1: "BB00"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "27077"
+ gid1: "BB00"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "27077"
+ spn: "LU-Transatel"
+ }
+}
+carrier_id {
+ canonical_id: 2272
+ carrier_name: "Auchan"
+ carrier_attribute {
+ mccmnc_tuple: "20810"
+ spn: "A MOBILE"
+ }
+}
+carrier_id {
+ canonical_id: 2273
+ carrier_name: "Reglo"
+ carrier_attribute {
+ mccmnc_tuple: "20810"
+ spn: "LeclercMobile"
+ spn: "RegloMobile"
+ }
+}
+carrier_id {
+ canonical_id: 2274
+ carrier_name: "La Poste"
+ carrier_attribute {
+ mccmnc_tuple: "20810"
+ gid1: "4C"
+ }
+}
+carrier_id {
+ canonical_id: 2275
+ carrier_name: "Darty"
+ carrier_attribute {
+ mccmnc_tuple: "20810"
+ gid1: "44"
+ }
+}
+carrier_id {
+ canonical_id: 2276
+ carrier_name: "Keyyo"
+ carrier_attribute {
+ mccmnc_tuple: "20810"
+ spn: "Keyyo Mobile"
+ }
+}
+carrier_id {
+ canonical_id: 2277
+ carrier_name: "Zero forfait"
+ carrier_attribute {
+ mccmnc_tuple: "20810"
+ spn: "ZERO FORFAIT"
+ }
+}
+carrier_id {
+ canonical_id: 2278
+ carrier_name: "R"
+ carrier_attribute {
+ mccmnc_tuple: "21406"
+ imsi_prefix_xpattern: "2140612"
+ }
+}
+carrier_id {
+ canonical_id: 2279
+ carrier_name: "eroskimovil"
+ carrier_attribute {
+ mccmnc_tuple: "21406"
+ imsi_prefix_xpattern: "2140606"
+ }
+}
+carrier_id {
+ canonical_id: 2280
+ carrier_name: "pepephone"
+ carrier_attribute {
+ mccmnc_tuple: "21406"
+ spn: "pepephone"
+ }
+}
+carrier_id {
+ canonical_id: 2281
+ carrier_name: "T-2"
+ carrier_attribute {
+ mccmnc_tuple: "21407"
+ imsi_prefix_xpattern: "2140759577"
+ imsi_prefix_xpattern: "2140796692"
+ }
+}
+carrier_id {
+ canonical_id: 2282
+ carrier_name: "Noverca"
+ carrier_attribute {
+ mccmnc_tuple: "22201"
+ spn: "Noverca"
+ }
+}
+carrier_id {
+ canonical_id: 2283
+ carrier_name: "LIFE"
+ carrier_attribute {
+ mccmnc_tuple: "23433"
+ spn: "LIFE"
+ }
+}
+carrier_id {
+ canonical_id: 2284
+ carrier_name: "Telmore"
+ carrier_attribute {
+ mccmnc_tuple: "23801"
+ spn: "TELMORE"
+ }
+}
+carrier_id {
+ canonical_id: 2285
+ carrier_name: "Call Me"
+ carrier_attribute {
+ mccmnc_tuple: "23820"
+ spn: "Call me"
+ }
+}
+carrier_id {
+ canonical_id: 2286
+ carrier_name: "DLG Tele"
+ carrier_attribute {
+ mccmnc_tuple: "23820"
+ spn: "DLG Tele"
+ }
+}
+carrier_id {
+ canonical_id: 2287
+ carrier_name: "Halebop"
+ carrier_attribute {
+ mccmnc_tuple: "24001"
+ imsi_prefix_xpattern: "240017"
+ }
+ carrier_attribute {
+ mccmnc_tuple: "24005"
+ spn: "Halebop"
+ }
+}
+carrier_id {
+ canonical_id: 2288
+ carrier_name: "GSC"
+ carrier_attribute {
+ mccmnc_tuple: "311590"
+ spn: "gsc"
+ }
+}
+carrier_id {
+ canonical_id: 2289
+ carrier_name: "Cable & Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "348170"
+ }
+}
+carrier_id {
+ canonical_id: 2290
+ carrier_name: "Cable & Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "366110"
+ }
+}
+carrier_id {
+ canonical_id: 2291
+ carrier_name: "Cable & Wireless West Indies Ltd (Turks & Caicos)"
+ carrier_attribute {
+ mccmnc_tuple: "376350"
+ }
+}
+carrier_id {
+ canonical_id: 2292
+ carrier_name: "Cable & Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "354860"
+ }
+}
+carrier_id {
+ canonical_id: 2293
+ carrier_name: "Cable & Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "358110"
+ }
+}
+carrier_id {
+ canonical_id: 2294
+ carrier_name: "Cable & Wireless (St. Vincent & the Grenadines) Ltd"
+ carrier_attribute {
+ mccmnc_tuple: "360110"
+ }
+}
+carrier_id {
+ canonical_id: 2295
+ carrier_name: "Cable & Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "365840"
+ }
+}
+carrier_id {
+ canonical_id: 2297
+ carrier_name: "Happy"
+ carrier_attribute {
+ mccmnc_tuple: "21403"
+ spn: "Happy"
+ }
+}
+carrier_id {
+ canonical_id: 2298
+ carrier_name: "RACC"
+ carrier_attribute {
+ mccmnc_tuple: "21403"
+ spn: "RACC"
+ }
+}
+carrier_id {
+ canonical_id: 2299
+ carrier_name: "CABLE movil"
+ carrier_attribute {
+ mccmnc_tuple: "21403"
+ spn: "CABLE movil"
+ }
+}
+carrier_id {
+ canonical_id: 2300
+ carrier_name: "MASMovil"
+ carrier_attribute {
+ mccmnc_tuple: "21403"
+ spn: "MASMovil"
+ }
+}
+carrier_id {
+ canonical_id: 2301
+ carrier_name: "Ibercom"
+ carrier_attribute {
+ mccmnc_tuple: "21403"
+ spn: "Ibercom"
+ }
+}
+carrier_id {
+ canonical_id: 2303
+ carrier_name: "PosteMobile"
+ carrier_attribute {
+ mccmnc_tuple: "22210"
+ spn: "PosteMobile"
+ }
+}
+carrier_id {
+ canonical_id: 2304
+ carrier_name: "ERG"
+ carrier_attribute {
+ mccmnc_tuple: "22210"
+ spn: "ERG"
+ }
+}
+carrier_id {
+ canonical_id: 2308
+ carrier_name: "Sainsbury's"
+ carrier_attribute {
+ mccmnc_tuple: "23415"
+ spn: "Sainsbury's"
+ }
+}
+carrier_id {
+ canonical_id: 2309
+ carrier_name: "Lebara"
+ carrier_attribute {
+ mccmnc_tuple: "23415"
+ spn: "Lebara"
+ }
+}
+carrier_id {
+ canonical_id: 2310
+ carrier_name: "Debitel"
+ carrier_attribute {
+ mccmnc_tuple: "26201"
+ spn: "debitel"
+ }
+}
+carrier_id {
+ canonical_id: 2315
+ carrier_name: "Norvado"
+ carrier_attribute {
+ mccmnc_tuple: "311500"
+ spn: "Norvado Wireless"
+ spn: "Norvado Wireless RPA"
+ spn: "Norvado Wireless RPT"
+ spn: "Norvado Wireless RPO"
+ }
+}
+carrier_id {
+ canonical_id: 2316
+ carrier_name: "Mosaic Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "311500"
+ spn: "Mosaic RPA"
+ spn: "Mosaic Mobile"
+ spn: "Mosaic RPT"
+ spn: "Mosaic RPO"
+ }
+}
+carrier_id {
+ canonical_id: 2317
+ carrier_name: "Nemont"
+ carrier_attribute {
+ mccmnc_tuple: "311350"
+ spn: "nemont"
+ }
+}
+carrier_id {
+ canonical_id: 2318
+ carrier_name: "Bravado wireless"
+ carrier_attribute {
+ mccmnc_tuple: "311140"
+ spn: "sprocket"
+ }
+}
+carrier_id {
+ canonical_id: 2319
+ carrier_name: "Element Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "311070"
+ spn: "elementmobile"
+ }
+}
+carrier_id {
+ canonical_id: 2320
+ carrier_name: "strata"
+ carrier_attribute {
+ mccmnc_tuple: "310960"
+ spn: "strata"
+ }
+}
+carrier_id {
+ canonical_id: 2321
+ carrier_name: "southcentral"
+ carrier_attribute {
+ mccmnc_tuple: "310960"
+ spn: "southcentral"
+ }
+}
+carrier_id {
+ canonical_id: 2322
+ carrier_name: "snakeriver"
+ carrier_attribute {
+ mccmnc_tuple: "310960"
+ spn: "snakeriver"
+ }
+}
+carrier_id {
+ canonical_id: 2323
+ carrier_name: "silverstar"
+ carrier_attribute {
+ mccmnc_tuple: "310960"
+ spn: "silverstar"
+ }
+}
+carrier_id {
+ canonical_id: 2324
+ carrier_name: "NNTC Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "310960"
+ spn: "nntcwire"
+ }
+}
+carrier_id {
+ canonical_id: 2325
+ carrier_name: "Clear Sky Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "310820"
+ spn: "nepa"
+ }
+}
+carrier_id {
+ canonical_id: 2326
+ carrier_name: "EMnify"
+ carrier_attribute {
+ mccmnc_tuple: "53703"
+ mccmnc_tuple: "29505"
+ spn: "EMnify"
+ }
+}
+carrier_id {
+ canonical_id: 2327
+ carrier_name: "Telemach"
+ carrier_attribute {
+ mccmnc_tuple: "29370"
+ imsi_prefix_xpattern: "29370029"
+ }
+}
+carrier_id {
+ canonical_id: 2328
+ carrier_name: "Fonic Prepaid"
+ carrier_attribute {
+ mccmnc_tuple: "26207"
+ imsi_prefix_xpattern: "26207515"
+ }
+}
+carrier_id {
+ canonical_id: 2329
+ carrier_name: "Lidl Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "26207"
+ imsi_prefix_xpattern: "26207520"
+ }
+}
+carrier_id {
+ canonical_id: 2330
+ carrier_name: "Tchibo"
+ carrier_attribute {
+ mccmnc_tuple: "26207"
+ imsi_prefix_xpattern: "26207500"
+ }
+}
+carrier_id {
+ canonical_id: 2331
+ carrier_name: "Djuice"
+ carrier_attribute {
+ mccmnc_tuple: "25503"
+ spn: "djuice"
+ }
+}
+carrier_id {
+ canonical_id: 2332
+ carrier_name: "Send"
+ carrier_attribute {
+ mccmnc_tuple: "24801"
+ imsi_prefix_xpattern: "248010x2"
+ imsi_prefix_xpattern: "248010x3"
+ }
+}
+carrier_id {
+ canonical_id: 2333
+ carrier_name: "Ventelo"
+ carrier_attribute {
+ mccmnc_tuple: "24201"
+ imsi_prefix_xpattern: "24201700"
+ }
+}
+carrier_id {
+ canonical_id: 2334
+ carrier_name: "Chess"
+ carrier_attribute {
+ mccmnc_tuple: "24202"
+ imsi_prefix_xpattern: "2420256"
+ }
+}
+carrier_id {
+ canonical_id: 2335
+ carrier_name: "Virgin"
+ carrier_attribute {
+ mccmnc_tuple: "65507"
+ imsi_prefix_xpattern: "6550710"
+ }
+}
+carrier_id {
+ canonical_id: 2336
+ carrier_name: "TrueMove H"
+ carrier_attribute {
+ mccmnc_tuple: "52000"
+ gid1: "01"
+ }
+}
+carrier_id {
+ canonical_id: 2337
+ carrier_name: "Movil exito"
+ carrier_attribute {
+ mccmnc_tuple: "732103"
+ mccmnc_tuple: "732111"
+ spn: "movil exito"
+ }
+}
+carrier_id {
+ canonical_id: 2338
+ carrier_name: "UNE"
+ carrier_attribute {
+ mccmnc_tuple: "732103"
+ mccmnc_tuple: "732111"
+ spn: "UNE"
+ }
+}
+carrier_id {
+ canonical_id: 2339
+ carrier_name: "Virgin Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "732123"
+ spn: "Virgin Mobile"
+ }
+}
+carrier_id {
+ canonical_id: 2340
+ carrier_name: "Google CBRS"
+ carrier_attribute {
+ mccmnc_tuple: "312580"
+ mccmnc_tuple: "315010"
+ }
+}
+carrier_id {
+ canonical_id: 2345
+ carrier_name: "Mobitel"
+ carrier_attribute {
+ mccmnc_tuple: "41845"
+ }
+}
+carrier_id {
+ canonical_id: 2346
+ carrier_name: "Unitel"
+ carrier_attribute {
+ mccmnc_tuple: "45703"
+ }
+}
+carrier_id {
+ canonical_id: 2347
+ carrier_name: "Unitel"
+ carrier_attribute {
+ mccmnc_tuple: "42888"
+ }
+}
+carrier_id {
+ canonical_id: 2348
+ carrier_name: "Libya Phone"
+ carrier_attribute {
+ mccmnc_tuple: "60603"
+ }
+}
+carrier_id {
+ canonical_id: 2349
+ carrier_name: "YemenMobile"
+ carrier_attribute {
+ mccmnc_tuple: "42103"
+ }
+}
+carrier_id {
+ canonical_id: 2350
+ carrier_name: "B-Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "52802"
+ }
+}
+carrier_id {
+ canonical_id: 2351
+ carrier_name: "AGMS"
+ carrier_attribute {
+ mccmnc_tuple: "20465"
+ }
+}
+carrier_id {
+ canonical_id: 2352
+ carrier_name: "MTCE"
+ carrier_attribute {
+ mccmnc_tuple: "43270"
+ }
+}
+carrier_id {
+ canonical_id: 2353
+ carrier_name: "SK Telink"
+ carrier_attribute {
+ mccmnc_tuple: "45011"
+ }
+}
+carrier_id {
+ canonical_id: 2354
+ carrier_name: "Smart"
+ carrier_attribute {
+ mccmnc_tuple: "70299"
+ }
+}
+carrier_id {
+ canonical_id: 2355
+ carrier_name: "Smart Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "64207"
+ }
+}
+carrier_id {
+ canonical_id: 2356
+ carrier_name: "Digicel"
+ carrier_attribute {
+ mccmnc_tuple: "34877"
+ mccmnc_tuple: "348770"
+ }
+}
+carrier_id {
+ canonical_id: 2357
+ carrier_name: "Tuenti"
+ carrier_attribute {
+ mccmnc_tuple: "21432"
+ spn: "Tuenti"
+ }
+}
+carrier_id {
+ canonical_id: 2358
+ carrier_name: "Farzanegan Pars"
+ carrier_attribute {
+ mccmnc_tuple: "43293"
+ }
+}
+carrier_id {
+ canonical_id: 2359
+ carrier_name: "Bitel"
+ carrier_attribute {
+ mccmnc_tuple: "71615"
+ }
+}
+carrier_id {
+ canonical_id: 2360
+ carrier_name: "Telefonica"
+ carrier_attribute {
+ mccmnc_tuple: "26203"
+ gid1: "010301"
+ gid1: "010901"
+ }
+}
+carrier_id {
+ canonical_id: 2361
+ carrier_name: "o2"
+ carrier_attribute {
+ mccmnc_tuple: "26207"
+ gid1: "010301"
+ gid1: "010901"
+ }
+}
+carrier_id {
+ canonical_id: 2362
+ carrier_name: "Vodacom"
+ carrier_attribute {
+ mccmnc_tuple: "64004"
+ }
+}
+carrier_id {
+ canonical_id: 2363
+ carrier_name: "Crnogorski Telekom"
+ carrier_attribute {
+ mccmnc_tuple: "29702"
+ }
+}
+carrier_id {
+ canonical_id: 2364
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "53001"
+ }
+}
+carrier_id {
+ canonical_id: 2365
+ carrier_name: "Hrvaski Telekom"
+ carrier_attribute {
+ mccmnc_tuple: "21901"
+ }
+}
+carrier_id {
+ canonical_id: 2366
+ carrier_name: "Swisscom"
+ carrier_attribute {
+ mccmnc_tuple: "22801"
+ }
+}
+carrier_id {
+ canonical_id: 2367
+ carrier_name: "T-Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "26002"
+ }
+}
+carrier_id {
+ canonical_id: 2368
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "27801"
+ }
+}
+carrier_id {
+ canonical_id: 2369
+ carrier_name: "Orange"
+ carrier_attribute {
+ mccmnc_tuple: "21403"
+ }
+}
+carrier_id {
+ canonical_id: 2376
+ carrier_name: "safaricom"
+ carrier_attribute {
+ mccmnc_tuple: "63902"
+ }
+}
+carrier_id {
+ canonical_id: 2378
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "40401"
+ mccmnc_tuple: "40405"
+ mccmnc_tuple: "40411"
+ mccmnc_tuple: "40413"
+ mccmnc_tuple: "40415"
+ mccmnc_tuple: "40420"
+ mccmnc_tuple: "40427"
+ mccmnc_tuple: "40430"
+ mccmnc_tuple: "40443"
+ mccmnc_tuple: "40446"
+ mccmnc_tuple: "40460"
+ mccmnc_tuple: "40484"
+ mccmnc_tuple: "40486"
+ mccmnc_tuple: "40488"
+ mccmnc_tuple: "40566"
+ mccmnc_tuple: "40567"
+ mccmnc_tuple: "405750"
+ mccmnc_tuple: "405751"
+ mccmnc_tuple: "405752"
+ mccmnc_tuple: "405753"
+ mccmnc_tuple: "405754"
+ mccmnc_tuple: "405755"
+ mccmnc_tuple: "405756"
+ }
+}
+carrier_id {
+ canonical_id: 2380
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "60202"
+ }
+}
+carrier_id {
+ canonical_id: 2381
+ carrier_name: "Vodacom"
+ carrier_attribute {
+ mccmnc_tuple: "64304"
+ }
+}
+carrier_id {
+ canonical_id: 2383
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "62002"
+ }
+}
+carrier_id {
+ canonical_id: 2385
+ carrier_name: "Slovak Telekom"
+ carrier_attribute {
+ mccmnc_tuple: "23102"
+ }
+}
+carrier_id {
+ canonical_id: 2386
+ carrier_name: "T-Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "20416"
+ mccmnc_tuple: "20420"
+ }
+}
+carrier_id {
+ canonical_id: 2387
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "27201"
+ }
+}
+carrier_id {
+ canonical_id: 2391
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "22601"
+ }
+}
+carrier_id {
+ canonical_id: 2392
+ carrier_name: "VCL"
+ carrier_attribute {
+ mccmnc_tuple: "65101"
+ }
+}
+carrier_id {
+ canonical_id: 2393
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "42702"
+ }
+}
+carrier_id {
+ canonical_id: 2394
+ carrier_name: "T-Mobile"
+ carrier_attribute {
+ mccmnc_tuple: "23001"
+ }
+}
+carrier_id {
+ canonical_id: 2395
+ carrier_name: "Telekom"
+ carrier_attribute {
+ mccmnc_tuple: "26201"
+ }
+}
+carrier_id {
+ canonical_id: 2396
+ carrier_name: "Mkedonski Telecom AD Skopje"
+ carrier_attribute {
+ mccmnc_tuple: "29401"
+ }
+}
+carrier_id {
+ canonical_id: 2397
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "26202"
+ }
+}
+carrier_id {
+ canonical_id: 2398
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "23003"
+ }
+}
+carrier_id {
+ canonical_id: 2399
+ carrier_name: "Vodafone"
+ carrier_attribute {
+ mccmnc_tuple: "20205"
+ }
+}
+carrier_id {
+ canonical_id: 2401
+ carrier_name: "Magyar Telekom"
+ carrier_attribute {
+ mccmnc_tuple: "21630"
+ }
+}
+carrier_id {
+ canonical_id: 2403
+ carrier_name: "Lycamobile"
+ carrier_attribute {
+ mccmnc_tuple: "26009"
+ spn: "Lycamobile"
+ }
+}
+carrier_id {
+ canonical_id: 2404
+ carrier_name: "Lycamobile"
+ carrier_attribute {
+ mccmnc_tuple: "23208"
+ spn: "Lycamobile"
+ }
+}
+carrier_id {
+ canonical_id: 2405
+ carrier_name: "Lycamobile"
+ carrier_attribute {
+ mccmnc_tuple: "24012"
+ spn: "Lycamobile"
+ }
+}
+carrier_id {
+ canonical_id: 2406
+ carrier_name: "Rabo Mobiel Int."
+ carrier_attribute {
+ mccmnc_tuple: "20408"
+ spn: "Rabo Mobiel"
+ }
+}
+carrier_id {
+ canonical_id: 2407
+ carrier_name: "OCN MOBILE ONE"
+ carrier_attribute {
+ mccmnc_tuple: "44010"
+ preferred_apn: "lte-d.ocn.ne.jp"
+ }
+}
+carrier_id {
+ canonical_id: 2408
+ carrier_name: "Vmax Telecom"
+ carrier_attribute {
+ mccmnc_tuple: "46609"
+ }
+}
+carrier_id {
+ canonical_id: 2409
+ carrier_name: "Dishnet Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "40448"
+ }
+}
+carrier_id {
+ canonical_id: 2411
+ carrier_name: "Wireless City Planning"
+ carrier_attribute {
+ mccmnc_tuple: "44100"
+ }
+}
+carrier_id {
+ canonical_id: 2412
+ carrier_name: "ACeS"
+ carrier_attribute {
+ mccmnc_tuple: "52020"
+ }
+}
+carrier_id {
+ canonical_id: 2413
+ carrier_name: "TOT"
+ carrier_attribute {
+ mccmnc_tuple: "52047"
+ }
+}
+carrier_id {
+ canonical_id: 2414
+ carrier_name: "Airtel Wireless"
+ carrier_attribute {
+ mccmnc_tuple: "302290"
+ }
+}
+carrier_id {
+ canonical_id: 2415
+ carrier_name: "Fonyou Telecom"
+ carrier_attribute {
+ mccmnc_tuple: "21420"
+ }
+}
+carrier_id {
+ canonical_id: 2416
+ carrier_name: "Fastweb SpA"
+ carrier_attribute {
+ mccmnc_tuple: "22208"
+ }
+}
+carrier_id {
+ canonical_id: 10000
+ carrier_name: "Tracfone-ATT"
+ carrier_attribute {
+ mccmnc_tuple: "310410"
+ gid1: "DDFF"
+ gid1: "DEFF"
+ }
+ parent_canonical_id: 2022
+}
+carrier_id {
+ canonical_id: 10001
+ carrier_name: "Tracfone-TMO"
+ carrier_attribute {
+ mccmnc_tuple: "310260"
+ gid1: "DDFF"
+ gid1: "DEFF"
+ }
+ parent_canonical_id: 2022
+}
+carrier_id {
+ canonical_id: 10002
+ carrier_name: "o2prepaid_gb"
+ carrier_attribute {
+ mccmnc_tuple: "23410"
+ gid1: "61"
+ gid1: "67"
+ gid1: "85"
+ gid1: "99"
+ }
+ parent_canonical_id: 1492
+}
+carrier_id {
+ canonical_id: 10003
+ carrier_name: "o2prepaid_de"
+ carrier_attribute {
+ mccmnc_tuple: "26207"
+ imsi_prefix_xpattern: "2620749"
+ }
+ parent_canonical_id: 1454
+}
+carrier_id {
+ canonical_id: 10004
+ carrier_name: "sprintprepaid_us"
+ carrier_attribute {
+ mccmnc_tuple: "310120"
+ gid1: "000004"
+ }
+ parent_canonical_id: 1788
+}
+carrier_id {
+ canonical_id: 10005
+ carrier_name: "pcmobile_prepaid_bell"
+ carrier_attribute {
+ mccmnc_tuple: "30264"
+ mccmnc_tuple: "302610"
+ mccmnc_tuple: "302630"
+ mccmnc_tuple: "302640"
+ gid1: "40"
+ }
+ parent_canonical_id: 2053
+}
+carrier_id {
+ canonical_id: 10006
+ carrier_name: "pcmobile_postpaid_telus"
+ carrier_attribute {
+ mccmnc_tuple: "302220"
+ mccmnc_tuple: "302221"
+ gid1: "5043"
+ }
+ parent_canonical_id: 2053
+}
+carrier_id {
+ canonical_id: 10007
+ carrier_name: "docomo_mvno"
+ carrier_attribute {
+ mccmnc_tuple: "44010"
+ }
+}
+version: 8
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 309a57b..1c53b15 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" product="tablet" msgid="9194799012395299737">"تهيئة شبكة الجوال"</string>
- <string name="app_label" product="default" msgid="8338087656149558019">"مساحة تخزين للهاتف والرسائل"</string>
+ <string name="app_label" product="default" msgid="8338087656149558019">"سعة تخزينية للهاتف والرسائل"</string>
</resources>
diff --git a/res/values-as/config.xml b/res/values-as/config.xml
deleted file mode 100644
index 99877a6..0000000
--- a/res/values-as/config.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?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-array name="persist_apns_for_plmn">
- <item msgid="6413072509259000954">"20404"</item>
- <item msgid="5639159280778239123">"310004"</item>
- <item msgid="3860605521380788028">"310120"</item>
- <item msgid="537693705785480198">"311480"</item>
- </string-array>
-</resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
deleted file mode 100644
index 5a39f18..0000000
--- a/res/values-as/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2008 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" product="tablet" msgid="9194799012395299737">"ম\'বাইল নেটৱৰ্ক কনফিগাৰেশ্বন"</string>
- <string name="app_label" product="default" msgid="8338087656149558019">"ফ\'ন আৰু বাৰ্তাৰ সঞ্চয়াগাৰ"</string>
-</resources>
diff --git a/res/values-or/config.xml b/res/values-or/config.xml
deleted file mode 100644
index 99877a6..0000000
--- a/res/values-or/config.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?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-array name="persist_apns_for_plmn">
- <item msgid="6413072509259000954">"20404"</item>
- <item msgid="5639159280778239123">"310004"</item>
- <item msgid="3860605521380788028">"310120"</item>
- <item msgid="537693705785480198">"311480"</item>
- </string-array>
-</resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
deleted file mode 100644
index b004c4f..0000000
--- a/res/values-or/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2008 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" product="tablet" msgid="9194799012395299737">"ମୋବାଇଲ୍ ନେଟ୍ୱର୍କ କନଫିଗରେଶନ୍"</string>
- <string name="app_label" product="default" msgid="8338087656149558019">"ଫୋନ୍ ଓ ମେସେଜିଙ୍ଗ ଷ୍ଟୋରେଜ୍"</string>
-</resources>
diff --git a/src/com/android/providers/telephony/CarrierDatabaseHelper.java b/src/com/android/providers/telephony/CarrierDatabaseHelper.java
index b654a77..0b97da1 100644
--- a/src/com/android/providers/telephony/CarrierDatabaseHelper.java
+++ b/src/com/android/providers/telephony/CarrierDatabaseHelper.java
@@ -40,6 +40,7 @@
*/
public CarrierDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ setWriteAheadLoggingEnabled(false);
}
public static final String KEY_TYPE = "key_type";
diff --git a/src/com/android/providers/telephony/CarrierIdProvider.java b/src/com/android/providers/telephony/CarrierIdProvider.java
index 1c2e5e5..a2c924c 100644
--- a/src/com/android/providers/telephony/CarrierIdProvider.java
+++ b/src/com/android/providers/telephony/CarrierIdProvider.java
@@ -25,11 +25,14 @@
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
+import android.os.Build;
import android.os.Environment;
+import android.os.SystemProperties;
import android.provider.Telephony.CarrierId;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
@@ -74,7 +77,7 @@
private static final String TAG = CarrierIdProvider.class.getSimpleName();
private static final String DATABASE_NAME = "carrierIdentification.db";
- private static final int DATABASE_VERSION = 3;
+ private static final int DATABASE_VERSION = 5;
private static final String ASSETS_PB_FILE = "carrier_list.pb";
private static final String VERSION_KEY = "version";
@@ -119,10 +122,15 @@
* index 7: {@link CarrierId.All#ICCID_PREFIX}
*/
private static final int ICCID_PREFIX_INDEX = 7;
+
+ /**
+ * index 8: {@link CarrierId.All#PRIVILEGE_ACCESS_RULE}
+ */
+ private static final int PRIVILEGE_ACCESS_RULE = 8;
/**
* ending index of carrier attribute list.
*/
- private static final int CARRIER_ATTR_END_IDX = ICCID_PREFIX_INDEX;
+ private static final int CARRIER_ATTR_END_IDX = PRIVILEGE_ACCESS_RULE;
/**
* The authority string for the CarrierIdProvider
*/
@@ -139,15 +147,18 @@
CarrierId.All.IMSI_PREFIX_XPATTERN,
CarrierId.All.SPN,
CarrierId.All.APN,
- CarrierId.All.ICCID_PREFIX));
+ CarrierId.All.ICCID_PREFIX,
+ CarrierId.All.PRIVILEGE_ACCESS_RULE,
+ CarrierId.PARENT_CARRIER_ID));
private CarrierIdDatabaseHelper mDbHelper;
/**
* Stores carrier id information for the current active subscriptions.
- * Key is the active subId and entryValue is a pair of carrier id(int) and Carrier Name(String).
+ * Key is the active subId and entryValue is carrier id(int), mno carrier id (int) and
+ * carrier name(String).
*/
- private final Map<Integer, Pair<Integer, String>> mCurrentSubscriptionMap =
+ private final Map<Integer, ContentValues> mCurrentSubscriptionMap =
new ConcurrentHashMap<>();
@VisibleForTesting
@@ -162,8 +173,10 @@
+ CarrierId.All.SPN + " TEXT,"
+ CarrierId.All.APN + " TEXT,"
+ CarrierId.All.ICCID_PREFIX + " TEXT,"
+ + CarrierId.All.PRIVILEGE_ACCESS_RULE + " TEXT,"
+ CarrierId.CARRIER_NAME + " TEXT,"
+ CarrierId.CARRIER_ID + " INTEGER DEFAULT -1,"
+ + CarrierId.PARENT_CARRIER_ID + " INTEGER DEFAULT -1,"
+ "UNIQUE (" + TextUtils.join(", ", CARRIERS_ID_UNIQUE_FIELDS) + "));";
}
@@ -317,6 +330,7 @@
*/
public CarrierIdDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ setWriteAheadLoggingEnabled(false);
}
@Override
@@ -341,6 +355,9 @@
if (oldVersion < DATABASE_VERSION) {
dropCarrierTable(db);
createCarrierTable(db);
+ // force rewrite carrier id db
+ setAppliedVersion(0);
+ updateDatabaseFromPb(db);
}
}
}
@@ -368,6 +385,11 @@
cv = new ContentValues();
cv.put(CarrierId.CARRIER_ID, id.canonicalId);
cv.put(CarrierId.CARRIER_NAME, id.carrierName);
+ // 0 is the default proto value. if parentCanonicalId is unset, apply default
+ // unknown carrier id -1.
+ if (id.parentCanonicalId > 0) {
+ cv.put(CarrierId.PARENT_CARRIER_ID, id.parentCanonicalId);
+ }
cvs = new ArrayList<>();
convertCarrierAttrToContentValues(cv, cvs, attr, 0);
for (ContentValues contentVal : cvs) {
@@ -403,7 +425,9 @@
private void convertCarrierAttrToContentValues(ContentValues cv, List<ContentValues> cvs,
CarrierIdProto.CarrierAttribute attr, int index) {
if (index > CARRIER_ATTR_END_IDX) {
- cvs.add(new ContentValues(cv));
+ ContentValues carrier = new ContentValues(cv);
+ if (!cvs.contains(carrier))
+ cvs.add(carrier);
return;
}
boolean found = false;
@@ -426,7 +450,7 @@
break;
case GID1_INDEX:
for (String str : attr.gid1) {
- cv.put(CarrierId.All.GID1, str);
+ cv.put(CarrierId.All.GID1, str.toLowerCase());
convertCarrierAttrToContentValues(cv, cvs, attr, index + 1);
cv.remove(CarrierId.All.GID1);
found = true;
@@ -434,7 +458,7 @@
break;
case GID2_INDEX:
for (String str : attr.gid2) {
- cv.put(CarrierId.All.GID2, str);
+ cv.put(CarrierId.All.GID2, str.toLowerCase());
convertCarrierAttrToContentValues(cv, cvs, attr, index + 1);
cv.remove(CarrierId.All.GID2);
found = true;
@@ -450,7 +474,7 @@
break;
case SPN_INDEX:
for (String str : attr.spn) {
- cv.put(CarrierId.All.SPN, str);
+ cv.put(CarrierId.All.SPN, str.toLowerCase());
convertCarrierAttrToContentValues(cv, cvs, attr, index + 1);
cv.remove(CarrierId.All.SPN);
found = true;
@@ -472,6 +496,14 @@
found = true;
}
break;
+ case PRIVILEGE_ACCESS_RULE:
+ for (String str : attr.privilegeAccessRule) {
+ cv.put(CarrierId.All.PRIVILEGE_ACCESS_RULE, str);
+ convertCarrierAttrToContentValues(cv, cvs, attr, index + 1);
+ cv.remove(CarrierId.All.PRIVILEGE_ACCESS_RULE);
+ found = true;
+ }
+ break;
default:
Log.e(TAG, "unsupported index: " + index);
break;
@@ -517,7 +549,10 @@
carrierList = assets;
version = assets.version;
}
- if (ota != null && ota.version > version) {
+ // bypass version check for ota carrier id test
+ if (ota != null && ((Build.IS_DEBUGGABLE && SystemProperties.getBoolean(
+ "persist.telephony.test.carrierid.ota", false))
+ || (ota.version > version))) {
carrierList = ota;
version = ota.version;
}
@@ -584,9 +619,7 @@
}
return count;
} else {
- mCurrentSubscriptionMap.put(subId,
- new Pair(cv.getAsInteger(CarrierId.CARRIER_ID),
- cv.getAsString(CarrierId.CARRIER_NAME)));
+ mCurrentSubscriptionMap.put(subId, new ContentValues(cv));
getContext().getContentResolver().notifyChange(CarrierId.CONTENT_URI, null);
return 1;
}
@@ -618,9 +651,9 @@
for (int i = 0; i < c.getColumnCount(); i++) {
final String columnName = c.getColumnName(i);
if (CarrierId.CARRIER_ID.equals(columnName)) {
- row.add(mCurrentSubscriptionMap.get(subId).first);
+ row.add(mCurrentSubscriptionMap.get(subId).get(CarrierId.CARRIER_ID));
} else if (CarrierId.CARRIER_NAME.equals(columnName)) {
- row.add(mCurrentSubscriptionMap.get(subId).second);
+ row.add(mCurrentSubscriptionMap.get(subId).get(CarrierId.CARRIER_NAME));
} else {
throw new IllegalArgumentException("Invalid column " + projectionIn[i]);
}
diff --git a/src/com/android/providers/telephony/HbpcdLookupDatabaseHelper.java b/src/com/android/providers/telephony/HbpcdLookupDatabaseHelper.java
index 2debc57..0149687 100644
--- a/src/com/android/providers/telephony/HbpcdLookupDatabaseHelper.java
+++ b/src/com/android/providers/telephony/HbpcdLookupDatabaseHelper.java
@@ -102,6 +102,7 @@
mContext = context;
// Memory optimization - close idle connections after 30s of inactivity
setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
+ setWriteAheadLoggingEnabled(false);
}
@Override
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index 1dc2106..3d3711a 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -16,6 +16,8 @@
package com.android.providers.telephony;
+import static android.provider.Telephony.RcsColumns.IS_RCS_TABLE_SCHEMA_CODE_COMPLETE;
+
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
@@ -284,7 +286,8 @@
}
}
- private MmsSmsDatabaseHelper(Context context, MmsSmsDatabaseErrorHandler dbErrorHandler) {
+ @VisibleForTesting
+ MmsSmsDatabaseHelper(Context context, MmsSmsDatabaseErrorHandler dbErrorHandler) {
super(context, DATABASE_NAME, null, DATABASE_VERSION, dbErrorHandler);
mContext = context;
// Memory optimization - close idle connections after 30s of inactivity
@@ -294,6 +297,7 @@
} catch (IllegalArgumentException e) {
// ignore
}
+ setWriteAheadLoggingEnabled(false);
}
private static synchronized MmsSmsDatabaseErrorHandler getDbErrorHandler(Context context) {
@@ -523,7 +527,7 @@
localLog("onCreate: Creating all SMS-MMS tables.");
// if FBE is not supported, or if this onCreate is for CE partition database
if (!StorageManager.isFileEncryptedNativeOrEmulated()
- || mContext.isCredentialProtectedStorage()) {
+ || (mContext != null && mContext.isCredentialProtectedStorage())) {
localLog("onCreate: broadcasting ACTION_SMS_MMS_DB_CREATED");
// Broadcast ACTION_SMS_MMS_DB_CREATED
Intent intent = new Intent(Sms.Intents.ACTION_SMS_MMS_DB_CREATED);
@@ -545,6 +549,14 @@
createMmsTables(db);
createSmsTables(db);
createCommonTables(db);
+
+ if (IS_RCS_TABLE_SCHEMA_CODE_COMPLETE) {
+ RcsProviderThreadHelper.createThreadTables(db);
+ RcsProviderParticipantHelper.createParticipantTables(db);
+ RcsProviderMessageHelper.createRcsMessageTables(db);
+ RcsProviderEventHelper.createRcsEventTables(db);
+ }
+
createCommonTriggers(db);
createMmsTriggers(db);
createWordsTables(db);
@@ -708,7 +720,8 @@
}
}
- private void createMmsTables(SQLiteDatabase db) {
+ @VisibleForTesting
+ void createMmsTables(SQLiteDatabase db) {
// N.B.: Whenever the columns here are changed, the columns in
// {@ref MmsSmsProvider} must be changed to match.
db.execSQL("CREATE TABLE " + MmsProvider.TABLE_PDU + " (" +
@@ -1030,7 +1043,8 @@
"message_body TEXT," + // message body
"display_originating_addr TEXT);";
// email address if from an email gateway, otherwise same as address
- private void createSmsTables(SQLiteDatabase db) {
+ @VisibleForTesting
+ void createSmsTables(SQLiteDatabase db) {
// N.B.: Whenever the columns here are changed, the columns in
// {@ref MmsSmsProvider} must be changed to match.
db.execSQL(CREATE_SMS_TABLE_STRING);
@@ -1056,7 +1070,8 @@
Sms.TYPE + "=" + Sms.MESSAGE_TYPE_SENT + ";");
}
- private void createCommonTables(SQLiteDatabase db) {
+ @VisibleForTesting
+ void createCommonTables(SQLiteDatabase db) {
// TODO Ensure that each entry is removed when the last use of
// any address equivalent to its address is removed.
@@ -1625,6 +1640,15 @@
} finally {
db.endTransaction();
}
+ // fall through
+ case 67:
+ if (currentVersion <= 67 || !IS_RCS_TABLE_SCHEMA_CODE_COMPLETE) {
+ return;
+ }
+ RcsProviderThreadHelper.createThreadTables(db);
+ RcsProviderParticipantHelper.createParticipantTables(db);
+ RcsProviderMessageHelper.createRcsMessageTables(db);
+ RcsProviderEventHelper.createRcsEventTables(db);
return;
}
diff --git a/src/com/android/providers/telephony/MmsSmsProvider.java b/src/com/android/providers/telephony/MmsSmsProvider.java
index 1653cd9..8c3555c 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -110,7 +110,7 @@
/**
* the name of the table that is used to store the canonical addresses for both SMS and MMS.
*/
- private static final String TABLE_CANONICAL_ADDRESSES = "canonical_addresses";
+ static final String TABLE_CANONICAL_ADDRESSES = "canonical_addresses";
/**
* the name of the table that is used to store the conversation threads.
@@ -1272,10 +1272,15 @@
@Override
public Uri insert(Uri uri, ContentValues values) {
- if (URI_MATCHER.match(uri) == URI_PENDING_MSG) {
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ int matchIndex = URI_MATCHER.match(uri);
+
+ if (matchIndex == URI_PENDING_MSG) {
long rowId = db.insert(TABLE_PENDING_MSG, null, values);
- return Uri.parse(uri + "/" + rowId);
+ return uri.buildUpon().appendPath(Long.toString(rowId)).build();
+ } else if (matchIndex == URI_CANONICAL_ADDRESS) {
+ long rowId = db.insert(TABLE_CANONICAL_ADDRESSES, null, values);
+ return uri.buildUpon().appendPath(Long.toString(rowId)).build();
}
throw new UnsupportedOperationException(NO_DELETES_INSERTS_OR_UPDATES + uri);
}
diff --git a/src/com/android/providers/telephony/RcsProvider.java b/src/com/android/providers/telephony/RcsProvider.java
new file mode 100644
index 0000000..3235b27
--- /dev/null
+++ b/src/com/android/providers/telephony/RcsProvider.java
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2018 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.providers.telephony;
+
+import static android.provider.Telephony.RcsColumns.RcsThreadColumns.RCS_THREAD_URI_PART;
+import static android.provider.Telephony.RcsColumns.TRANSACTION_FAILED;
+
+import static com.android.providers.telephony.RcsProviderUtil.buildUriWithRowIdAppended;
+import static com.android.providers.telephony.RcsProviderUtil.returnUriAsIsIfSuccessful;
+
+import android.app.AppOpsManager;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Content provider to handle RCS messages. The functionality here is similar to SmsProvider,
+ * MmsProvider etc.
+ *
+ * The provider has constraints around inserting, updating and deleting - the user needs to know
+ * whether they are inserting a message that is incoming/outgoing, or the thread they are inserting
+ * is a group or p2p etc. This is in order to keep the implementation simple and avoid complex
+ * queries.
+ *
+ * @hide
+ */
+public class RcsProvider extends ContentProvider {
+ static final String TAG = "RcsProvider";
+ static final String AUTHORITY = "rcs";
+ private static final String CONTENT_AUTHORITY = "content://" + AUTHORITY;
+
+ private static final Uri PARTICIPANT_URI_PREFIX =
+ Uri.parse(CONTENT_AUTHORITY + "/participant/");
+ private static final Uri P2P_THREAD_URI_PREFIX = Uri.parse(CONTENT_AUTHORITY + "/p2p_thread/");
+ private static final Uri FILE_TRANSFER_PREFIX = Uri.parse(
+ CONTENT_AUTHORITY + "/file_transfer/");
+ static final Uri GROUP_THREAD_URI_PREFIX = Uri.parse(CONTENT_AUTHORITY + "/group_thread/");
+
+ // Rcs table names
+ static final String RCS_THREAD_TABLE = "rcs_thread";
+ static final String RCS_1_TO_1_THREAD_TABLE = "rcs_1_to_1_thread";
+ static final String RCS_GROUP_THREAD_TABLE = "rcs_group_thread";
+ static final String UNIFIED_RCS_THREAD_VIEW = "rcs_unified_rcs_thread_view";
+ static final String RCS_PARTICIPANT_TABLE = "rcs_participant";
+ static final String RCS_PARTICIPANT_THREAD_JUNCTION_TABLE = "rcs_thread_participant";
+ static final String RCS_MESSAGE_TABLE = "rcs_message";
+ static final String RCS_INCOMING_MESSAGE_TABLE = "rcs_incoming_message";
+ static final String RCS_OUTGOING_MESSAGE_TABLE = "rcs_outgoing_message";
+ static final String RCS_MESSAGE_DELIVERY_TABLE = "rcs_message_delivery";
+ static final String UNIFIED_MESSAGE_VIEW = "rcs_unified_message_view";
+ static final String RCS_FILE_TRANSFER_TABLE = "rcs_file_transfer";
+ static final String RCS_THREAD_EVENT_TABLE = "rcs_thread_event";
+ static final String RCS_PARTICIPANT_EVENT_TABLE = "rcs_participant_event";
+ static final String RCS_UNIFIED_EVENT_VIEW = "rcs_unified_event_view";
+
+ private static final UriMatcher URL_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
+
+ private static final int UNIFIED_RCS_THREAD = 1;
+ private static final int UNIFIED_RCS_THREAD_WITH_ID = 2;
+ private static final int PARTICIPANT = 3;
+ private static final int PARTICIPANT_WITH_ID = 4;
+ private static final int PARTICIPANT_ALIAS_CHANGE_EVENT = 5;
+ private static final int PARTICIPANT_ALIAS_CHANGE_EVENT_WITH_ID = 6;
+ private static final int P2P_THREAD = 7;
+ private static final int P2P_THREAD_WITH_ID = 8;
+ private static final int P2P_THREAD_PARTICIPANT = 9;
+ private static final int P2P_THREAD_PARTICIPANT_WITH_ID = 10;
+ private static final int GROUP_THREAD = 11;
+ private static final int GROUP_THREAD_WITH_ID = 12;
+ private static final int GROUP_THREAD_PARTICIPANT = 13;
+ private static final int GROUP_THREAD_PARTICIPANT_WITH_ID = 14;
+ private static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT = 15;
+ private static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT_WITH_ID = 16;
+ private static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT = 17;
+ private static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT_WITH_ID = 18;
+ private static final int GROUP_THREAD_NAME_CHANGE_EVENT = 19;
+ private static final int GROUP_THREAD_NAME_CHANGE_EVENT_WITH_ID = 20;
+ private static final int GROUP_THREAD_ICON_CHANGE_EVENT = 21;
+ private static final int GROUP_THREAD_ICON_CHANGE_EVENT_WITH_ID = 22;
+ private static final int UNIFIED_MESSAGE = 23;
+ private static final int UNIFIED_MESSAGE_WITH_ID = 24;
+ private static final int UNIFIED_MESSAGE_WITH_FILE_TRANSFER = 25;
+ private static final int INCOMING_MESSAGE = 26;
+ private static final int INCOMING_MESSAGE_WITH_ID = 27;
+ private static final int OUTGOING_MESSAGE = 28;
+ private static final int OUTGOING_MESSAGE_WITH_ID = 29;
+ private static final int OUTGOING_MESSAGE_DELIVERY = 30;
+ private static final int OUTGOING_MESSAGE_DELIVERY_WITH_ID = 31;
+ private static final int UNIFIED_MESSAGE_ON_THREAD = 32;
+ private static final int UNIFIED_MESSAGE_ON_THREAD_WITH_ID = 33;
+ private static final int INCOMING_MESSAGE_ON_P2P_THREAD = 34;
+ private static final int INCOMING_MESSAGE_ON_P2P_THREAD_WITH_ID = 35;
+ private static final int OUTGOING_MESSAGE_ON_P2P_THREAD = 36;
+ private static final int OUTGOING_MESSAGE_ON_P2P_THREAD_WITH_ID = 37;
+ private static final int INCOMING_MESSAGE_ON_GROUP_THREAD = 38;
+ private static final int INCOMING_MESSAGE_ON_GROUP_THREAD_WITH_ID = 39;
+ private static final int OUTGOING_MESSAGE_ON_GROUP_THREAD = 40;
+ private static final int OUTGOING_MESSAGE_ON_GROUP_THREAD_WITH_ID = 41;
+ private static final int FILE_TRANSFER_WITH_ID = 42;
+ private static final int EVENT = 43;
+ private static final int CANONICAL_ADDRESS = 44;
+
+ SQLiteOpenHelper mDbOpenHelper;
+
+ @VisibleForTesting
+ RcsProviderThreadHelper mThreadHelper;
+ @VisibleForTesting
+ RcsProviderParticipantHelper mParticipantHelper;
+ @VisibleForTesting
+ RcsProviderMessageHelper mMessageHelper;
+ @VisibleForTesting
+ RcsProviderEventHelper mEventHelper;
+ @VisibleForTesting
+ RcsProviderCanonicalAddressHelper mCanonicalAddressHelper;
+
+ static {
+ // example URI: content://rcs/thread
+ URL_MATCHER.addURI(AUTHORITY, RCS_THREAD_URI_PART, UNIFIED_RCS_THREAD);
+
+ // example URI: content://rcs/thread/4, where 4 is the thread id.
+ URL_MATCHER.addURI(AUTHORITY, "thread/#", UNIFIED_RCS_THREAD_WITH_ID);
+
+ // example URI: content://rcs/participant
+ URL_MATCHER.addURI(AUTHORITY, "participant", PARTICIPANT);
+
+ // example URI: content://rcs/participant/12, where 12 is the participant id
+ URL_MATCHER.addURI(AUTHORITY, "participant/#", PARTICIPANT_WITH_ID);
+
+ // example URI: content://rcs/participant/12/alias_change_event, where 12 is the participant
+ // id.
+ URL_MATCHER.addURI(AUTHORITY, "participant/#/alias_change_event",
+ PARTICIPANT_ALIAS_CHANGE_EVENT);
+
+ // example URI: content://rcs/participant/12/alias_change_event/4, where 12 is the
+ // participant id, and 4 is the event id
+ URL_MATCHER.addURI(AUTHORITY, "participant/#/alias_change_event/#",
+ PARTICIPANT_ALIAS_CHANGE_EVENT_WITH_ID);
+
+ // example URI: content://rcs/p2p_thread
+ URL_MATCHER.addURI(AUTHORITY, "p2p_thread", P2P_THREAD);
+
+ // example URI: content://rcs/p2p_thread/4, where 4 is the thread id
+ URL_MATCHER.addURI(AUTHORITY, "p2p_thread/#", P2P_THREAD_WITH_ID);
+
+ // example URI: content://rcs/p2p_thread/4/participant, where 4 is the thread id
+ URL_MATCHER.addURI(AUTHORITY, "p2p_thread/#/participant", P2P_THREAD_PARTICIPANT);
+
+ // example URI: content://rcs/p2p_thread/9/participant/3", only supports a 1 time insert.
+ // 9 is the thread ID, 3 is the participant ID.
+ URL_MATCHER.addURI(AUTHORITY, "p2p_thread/#/participant/#", P2P_THREAD_PARTICIPANT_WITH_ID);
+
+ // example URI: content://rcs/group_thread
+ URL_MATCHER.addURI(AUTHORITY, "group_thread", GROUP_THREAD);
+
+ // example URI: content://rcs/group_thread/13, where 13 is the _id in rcs_threads table.
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#", GROUP_THREAD_WITH_ID);
+
+ // example URI: content://rcs/group_thread/13/participant_joined_event. Supports
+ // queries and inserts
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/participant_joined_event",
+ GROUP_THREAD_PARTICIPANT_JOINED_EVENT);
+
+ // example URI: content://rcs/group_thread/13/participant_joined_event/3. Supports deletes.
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/participant_joined_event/#",
+ GROUP_THREAD_PARTICIPANT_JOINED_EVENT_WITH_ID);
+
+ // example URI: content://rcs/group_thread/13/participant_left_event. Supports queries
+ // and inserts
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/participant_left_event",
+ GROUP_THREAD_PARTICIPANT_LEFT_EVENT);
+
+ // example URI: content://rcs/group_thread/13/participant_left_event/5. Supports deletes
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/participant_left_event/#",
+ GROUP_THREAD_PARTICIPANT_LEFT_EVENT_WITH_ID);
+
+ // example URI: content://rcs/group_thread/13/name_changed_event. Supports queries and
+ // inserts
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/name_changed_event",
+ GROUP_THREAD_NAME_CHANGE_EVENT);
+
+ // example URI: content://rcs/group_thread/13/name_changed_event/7. Supports deletes
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/name_changed_event/#",
+ GROUP_THREAD_NAME_CHANGE_EVENT_WITH_ID);
+
+ // example URI: content://rcs/group_thread/13/icon_changed_event. Supports queries and
+ // inserts
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/icon_changed_event",
+ GROUP_THREAD_ICON_CHANGE_EVENT);
+
+ // example URI: content://rcs/group_thread/13/icon_changed_event/9. Supports deletes
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/icon_changed_event/#",
+ GROUP_THREAD_ICON_CHANGE_EVENT_WITH_ID);
+
+ // example URI: content://rcs/group_thread/18/participant
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/participant",
+ GROUP_THREAD_PARTICIPANT);
+
+ // example URI: content://rcs/group_thread/21/participant/4, only supports inserts and
+ // deletes
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/participant/#",
+ GROUP_THREAD_PARTICIPANT_WITH_ID);
+
+ // example URI: content://rcs/message
+ URL_MATCHER.addURI(AUTHORITY, "message", UNIFIED_MESSAGE);
+
+ // example URI: content://rcs/message/4, where 4 is the message id.
+ URL_MATCHER.addURI(AUTHORITY, "message/#", UNIFIED_MESSAGE_WITH_ID);
+
+ // example URI: content://rcs/message/4/file_transfer, only supports inserts
+ URL_MATCHER.addURI(AUTHORITY, "message/#/file_transfer",
+ UNIFIED_MESSAGE_WITH_FILE_TRANSFER);
+
+ // example URI: content://rcs/incoming_message
+ URL_MATCHER.addURI(AUTHORITY, "incoming_message", INCOMING_MESSAGE);
+
+ // example URI: content://rcs/incoming_message/45
+ URL_MATCHER.addURI(AUTHORITY, "incoming_message/#", INCOMING_MESSAGE_WITH_ID);
+
+ // example URI: content://rcs/outgoing_message
+ URL_MATCHER.addURI(AUTHORITY, "outgoing_message", OUTGOING_MESSAGE);
+
+ // example URI: content://rcs/outgoing_message/54
+ URL_MATCHER.addURI(AUTHORITY, "outgoing_message/#", OUTGOING_MESSAGE_WITH_ID);
+
+ // example URI: content://rcs/outgoing_message/54/delivery. Only supports queries
+ URL_MATCHER.addURI(AUTHORITY, "outgoing_message/#/delivery", OUTGOING_MESSAGE_DELIVERY);
+
+ // example URI: content://rcs/outgoing_message/9/delivery/4. Does not support queries
+ URL_MATCHER.addURI(AUTHORITY, "outgoing_message/#/delivery/#",
+ OUTGOING_MESSAGE_DELIVERY_WITH_ID);
+
+ // example URI: content://rcs/thread/5/message, only supports querying.
+ URL_MATCHER.addURI(AUTHORITY, "thread/#/message", UNIFIED_MESSAGE_ON_THREAD);
+
+ // example URI: content://rcs/thread/5/message/40, only supports querying.
+ URL_MATCHER.addURI(AUTHORITY, "thread/#/message/#", UNIFIED_MESSAGE_ON_THREAD_WITH_ID);
+
+ // example URI: content://rcs/p2p_thread/3/incoming_message. Only available for inserting
+ // incoming messages onto a 1 to 1 thread.
+ URL_MATCHER.addURI(AUTHORITY, "p2p_thread/#/incoming_message",
+ INCOMING_MESSAGE_ON_P2P_THREAD);
+
+ // example URI: content://rcs/p2p_thread/11/incoming_message/45. Only supports querying
+ URL_MATCHER.addURI(AUTHORITY, "p2p_thread/#/incoming_message/#",
+ INCOMING_MESSAGE_ON_P2P_THREAD_WITH_ID);
+
+ // example URI: content://rcs/p2p_thread/3/outgoing_message. Only available for inserting
+ // outgoing messages onto a 1 to 1 thread.
+ URL_MATCHER.addURI(AUTHORITY, "p2p_thread/#/outgoing_message",
+ OUTGOING_MESSAGE_ON_P2P_THREAD);
+
+ // example URI: content://rcs/p2p_thread/11/outgoing_message/46. Only supports querying
+ URL_MATCHER.addURI(AUTHORITY, "p2p_thread/#/outgoing_message/#",
+ OUTGOING_MESSAGE_ON_P2P_THREAD_WITH_ID);
+
+ // example URI: content://rcs/group_thread/3/incoming_message. Only available for inserting
+ // incoming messages onto a group thread.
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/incoming_message",
+ INCOMING_MESSAGE_ON_GROUP_THREAD);
+
+ // example URI: content://rcs/group_thread/3/incoming_message/71. Only supports querying
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/incoming_message/#",
+ INCOMING_MESSAGE_ON_GROUP_THREAD_WITH_ID);
+
+ // example URI: content://rcs/group_thread/3/outgoing_message. Only available for inserting
+ // outgoing messages onto a group thread.
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/outgoing_message",
+ OUTGOING_MESSAGE_ON_GROUP_THREAD);
+
+ // example URI: content://rcs/group_thread/13/outgoing_message/72. Only supports querying
+ URL_MATCHER.addURI(AUTHORITY, "group_thread/#/outgoing_message/#",
+ OUTGOING_MESSAGE_ON_GROUP_THREAD_WITH_ID);
+
+ // example URI: content://rcs/file_transfer/1. Does not support insertion
+ URL_MATCHER.addURI(AUTHORITY, "file_transfer/#", FILE_TRANSFER_WITH_ID);
+
+ // example URI: content://rcs/event
+ URL_MATCHER.addURI(AUTHORITY, "event", EVENT);
+
+ URL_MATCHER.addURI(AUTHORITY, "canonical-address", CANONICAL_ADDRESS);
+ }
+
+ @Override
+ public boolean onCreate() {
+ // Use the credential encrypted mmssms.db for RCS messages.
+ mDbOpenHelper = MmsSmsDatabaseHelper.getInstanceForCe(getContext());
+ mParticipantHelper = new RcsProviderParticipantHelper(mDbOpenHelper);
+ mThreadHelper = new RcsProviderThreadHelper(mDbOpenHelper);
+ mMessageHelper = new RcsProviderMessageHelper(mDbOpenHelper);
+ mEventHelper = new RcsProviderEventHelper(mDbOpenHelper);
+ mCanonicalAddressHelper = new RcsProviderCanonicalAddressHelper(mDbOpenHelper);
+ return true;
+ }
+
+ /**
+ * ContentResolver has a weird bug that if both query methods are overridden, it will always
+ * pick the bundle one to call, but will still require us to override this one as it is
+ * abstract. Work around by putting parameters in a bundle.
+ */
+ @Override
+ public synchronized Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ Bundle bundle = new Bundle();
+ bundle.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection);
+ bundle.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
+ bundle.putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER, sortOrder);
+ return query(uri, projection, bundle, null);
+ }
+
+ @Override
+ public synchronized Cursor query(Uri uri, String[] projection, Bundle queryArgs,
+ CancellationSignal unused) {
+ int match = URL_MATCHER.match(uri);
+
+ String selection = null;
+ String[] selectionArgs = null;
+ String sortOrder = null;
+
+ if (queryArgs != null) {
+ selection = queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SELECTION);
+ selectionArgs = queryArgs.getStringArray(
+ ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS);
+ sortOrder = queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER);
+ }
+
+ switch (match) {
+ case UNIFIED_RCS_THREAD:
+ return mThreadHelper.queryUnifiedThread(queryArgs);
+ case UNIFIED_RCS_THREAD_WITH_ID:
+ return mThreadHelper.queryUnifiedThreadUsingId(uri, projection);
+ case PARTICIPANT:
+ return mParticipantHelper.queryParticipant(queryArgs);
+ case PARTICIPANT_WITH_ID:
+ return mParticipantHelper.queryParticipantWithId(uri, projection);
+ case PARTICIPANT_ALIAS_CHANGE_EVENT:
+ Log.e(TAG, "Querying individual event types is not supported, uri: " + uri);
+ break;
+ case PARTICIPANT_ALIAS_CHANGE_EVENT_WITH_ID:
+ Log.e(TAG, "Querying participant events with id's is not supported, uri: " + uri);
+ break;
+ case P2P_THREAD:
+ return mThreadHelper.query1to1Thread(projection, selection,
+ selectionArgs, sortOrder);
+ case P2P_THREAD_WITH_ID:
+ return mThreadHelper.query1To1ThreadUsingId(uri, projection);
+ case P2P_THREAD_PARTICIPANT:
+ return mParticipantHelper.queryParticipantIn1To1Thread(uri);
+ case P2P_THREAD_PARTICIPANT_WITH_ID:
+ Log.e(TAG,
+ "Querying participants in 1 to 1 threads via id's is not supported, uri: "
+ + uri);
+ break;
+ case GROUP_THREAD:
+ return mThreadHelper.queryGroupThread(projection, selection,
+ selectionArgs, sortOrder);
+ case GROUP_THREAD_WITH_ID:
+ return mThreadHelper.queryGroupThreadUsingId(uri, projection);
+ case GROUP_THREAD_PARTICIPANT:
+ return mParticipantHelper.queryParticipantsInGroupThread(uri);
+ case GROUP_THREAD_PARTICIPANT_WITH_ID:
+ return mParticipantHelper.queryParticipantInGroupThreadWithId(uri);
+ case GROUP_THREAD_PARTICIPANT_JOINED_EVENT:
+ case GROUP_THREAD_PARTICIPANT_JOINED_EVENT_WITH_ID:
+ case GROUP_THREAD_PARTICIPANT_LEFT_EVENT:
+ case GROUP_THREAD_PARTICIPANT_LEFT_EVENT_WITH_ID:
+ case GROUP_THREAD_NAME_CHANGE_EVENT:
+ case GROUP_THREAD_NAME_CHANGE_EVENT_WITH_ID:
+ case GROUP_THREAD_ICON_CHANGE_EVENT:
+ case GROUP_THREAD_ICON_CHANGE_EVENT_WITH_ID:
+ Log.e(TAG, "Querying individual event types is not supported, uri: " + uri);
+ break;
+ case UNIFIED_MESSAGE:
+ return mMessageHelper.queryMessages(queryArgs);
+ case UNIFIED_MESSAGE_WITH_ID:
+ return mMessageHelper.queryUnifiedMessageWithId(uri);
+ case UNIFIED_MESSAGE_WITH_FILE_TRANSFER:
+ Log.e(TAG,
+ "Querying file transfers through messages is not supported, uri: " + uri);
+ case INCOMING_MESSAGE:
+ return mMessageHelper.queryIncomingMessageWithSelection(selection, selectionArgs);
+ case INCOMING_MESSAGE_WITH_ID:
+ return mMessageHelper.queryIncomingMessageWithId(uri);
+ case OUTGOING_MESSAGE:
+ return mMessageHelper.queryOutgoingMessageWithSelection(selection, selectionArgs);
+ case OUTGOING_MESSAGE_WITH_ID:
+ return mMessageHelper.queryOutgoingMessageWithId(uri);
+ case OUTGOING_MESSAGE_DELIVERY:
+ return mMessageHelper.queryOutgoingMessageDeliveries(uri);
+ case OUTGOING_MESSAGE_DELIVERY_WITH_ID:
+ Log.e(TAG,
+ "Querying deliveries with message and participant ids is not supported, "
+ + "uri: "
+ + uri);
+ case UNIFIED_MESSAGE_ON_THREAD:
+ return mMessageHelper.queryAllMessagesOnThread(uri, selection, selectionArgs);
+ case UNIFIED_MESSAGE_ON_THREAD_WITH_ID:
+ return mMessageHelper.queryUnifiedMessageWithIdInThread(uri);
+ case INCOMING_MESSAGE_ON_P2P_THREAD:
+ Log.e(TAG,
+ "Querying incoming messages on P2P thread with selection is not "
+ + "supported, uri: "
+ + uri);
+ break;
+ case INCOMING_MESSAGE_ON_P2P_THREAD_WITH_ID:
+ return mMessageHelper.queryUnifiedMessageWithIdInThread(uri);
+ case OUTGOING_MESSAGE_ON_P2P_THREAD:
+ Log.e(TAG,
+ "Querying outgoing messages on P2P thread with selection is not "
+ + "supported, uri: "
+ + uri);
+ break;
+ case OUTGOING_MESSAGE_ON_P2P_THREAD_WITH_ID:
+ return mMessageHelper.queryUnifiedMessageWithIdInThread(uri);
+ case INCOMING_MESSAGE_ON_GROUP_THREAD:
+ Log.e(TAG,
+ "Querying incoming messages on group thread with selection is not "
+ + "supported, uri: "
+ + uri);
+ break;
+ case INCOMING_MESSAGE_ON_GROUP_THREAD_WITH_ID:
+ return mMessageHelper.queryUnifiedMessageWithIdInThread(uri);
+ case OUTGOING_MESSAGE_ON_GROUP_THREAD:
+ Log.e(TAG,
+ "Querying outgoing messages on group thread with selection is not "
+ + "supported, uri: "
+ + uri);
+ break;
+ case OUTGOING_MESSAGE_ON_GROUP_THREAD_WITH_ID:
+ return mMessageHelper.queryUnifiedMessageWithIdInThread(uri);
+ case FILE_TRANSFER_WITH_ID:
+ return mMessageHelper.queryFileTransfer(uri);
+ case EVENT:
+ return mEventHelper.queryEvents(queryArgs);
+ case CANONICAL_ADDRESS:
+ String canonicalAddress = uri.getQueryParameter("address");
+ return mCanonicalAddressHelper.getOrCreateCanonicalAddress(canonicalAddress);
+ default:
+ Log.e(TAG, "Invalid query: " + uri);
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @Override
+ public synchronized Uri insert(Uri uri, ContentValues values) {
+ int match = URL_MATCHER.match(uri);
+ long rowId;
+
+ switch (match) {
+ case UNIFIED_RCS_THREAD:
+ case UNIFIED_RCS_THREAD_WITH_ID:
+ Log.e(TAG, "Inserting into unified thread view is not supported, uri: " + uri);
+ break;
+ case PARTICIPANT:
+ return buildUriWithRowIdAppended(PARTICIPANT_URI_PREFIX,
+ mParticipantHelper.insertParticipant(values));
+ case PARTICIPANT_WITH_ID:
+ Log.e(TAG, "Inserting participant with a specified ID is not supported, uri: "
+ + uri);
+ break;
+ case PARTICIPANT_ALIAS_CHANGE_EVENT:
+ return buildUriWithRowIdAppended(uri,
+ mEventHelper.insertParticipantEvent(uri, values));
+ case PARTICIPANT_ALIAS_CHANGE_EVENT_WITH_ID:
+ Log.e(TAG, "Inserting participant events with id's is not supported, uri: " + uri);
+ break;
+ case P2P_THREAD:
+ return buildUriWithRowIdAppended(P2P_THREAD_URI_PREFIX,
+ mThreadHelper.insert1To1Thread(values));
+ case P2P_THREAD_WITH_ID:
+ Log.e(TAG, "Inserting a thread with a specified ID is not supported, uri: " + uri);
+ break;
+ case P2P_THREAD_PARTICIPANT:
+ Log.e(TAG,
+ "Inserting a participant into a thread via content values is not "
+ + "supported, uri: "
+ + uri);
+ break;
+ case P2P_THREAD_PARTICIPANT_WITH_ID:
+ Log.e(TAG,
+ "Inserting participant into a thread via URI is not supported, uri: "
+ + uri);
+ break;
+ case GROUP_THREAD:
+ return buildUriWithRowIdAppended(GROUP_THREAD_URI_PREFIX,
+ mThreadHelper.insertGroupThread(values));
+ case GROUP_THREAD_WITH_ID:
+ Log.e(TAG, "Inserting a thread with a specified ID is not supported, uri: " + uri);
+ break;
+ case GROUP_THREAD_PARTICIPANT_JOINED_EVENT:
+ return buildUriWithRowIdAppended(uri,
+ mEventHelper.insertParticipantJoinedEvent(uri, values));
+ case GROUP_THREAD_PARTICIPANT_JOINED_EVENT_WITH_ID:
+ Log.e(TAG, "Inserting thread events with id's is not supported, uri: " + uri);
+ break;
+ case GROUP_THREAD_PARTICIPANT_LEFT_EVENT:
+ return buildUriWithRowIdAppended(uri,
+ mEventHelper.insertParticipantLeftEvent(uri, values));
+ case GROUP_THREAD_PARTICIPANT_LEFT_EVENT_WITH_ID:
+ Log.e(TAG, "Inserting thread events with id's is not supported, uri: " + uri);
+ break;
+ case GROUP_THREAD_NAME_CHANGE_EVENT:
+ return buildUriWithRowIdAppended(uri,
+ mEventHelper.insertThreadNameChangeEvent(uri, values));
+ case GROUP_THREAD_NAME_CHANGE_EVENT_WITH_ID:
+ Log.e(TAG, "Inserting thread events with id's is not supported, uri: " + uri);
+ break;
+ case GROUP_THREAD_ICON_CHANGE_EVENT:
+ return buildUriWithRowIdAppended(uri,
+ mEventHelper.insertThreadIconChangeEvent(uri, values));
+ case GROUP_THREAD_ICON_CHANGE_EVENT_WITH_ID:
+ Log.e(TAG, "Inserting thread events with id's is not supported, uri: " + uri);
+ break;
+ case GROUP_THREAD_PARTICIPANT:
+ rowId = mParticipantHelper.insertParticipantIntoGroupThread(values);
+ if (rowId == TRANSACTION_FAILED) {
+ return null;
+ }
+ return mParticipantHelper.getParticipantInThreadUri(values, rowId);
+ case GROUP_THREAD_PARTICIPANT_WITH_ID:
+ return returnUriAsIsIfSuccessful(uri,
+ mParticipantHelper.insertParticipantIntoGroupThreadWithId(uri));
+ case UNIFIED_MESSAGE:
+ case UNIFIED_MESSAGE_WITH_ID:
+ Log.e(TAG, "Inserting into unified message view is not supported, uri: " + uri);
+ break;
+ case UNIFIED_MESSAGE_WITH_FILE_TRANSFER:
+ return buildUriWithRowIdAppended(FILE_TRANSFER_PREFIX,
+ mMessageHelper.insertFileTransferToMessage(uri, values));
+ case INCOMING_MESSAGE:
+ case INCOMING_MESSAGE_WITH_ID:
+ case OUTGOING_MESSAGE:
+ case OUTGOING_MESSAGE_WITH_ID:
+ Log.e(TAG, "Inserting a message without a thread is not supported, uri: "
+ + uri);
+ break;
+ case OUTGOING_MESSAGE_DELIVERY:
+ Log.e(TAG,
+ "Inserting an outgoing message delivery without a participant is not "
+ + "supported, uri: "
+ + uri);
+ break;
+ case OUTGOING_MESSAGE_DELIVERY_WITH_ID:
+ return returnUriAsIsIfSuccessful(uri,
+ mMessageHelper.insertMessageDelivery(uri, values));
+ case UNIFIED_MESSAGE_ON_THREAD:
+ case UNIFIED_MESSAGE_ON_THREAD_WITH_ID:
+ Log.e(TAG,
+ "Inserting a message on unified thread view is not supported, uri: " + uri);
+ break;
+ case INCOMING_MESSAGE_ON_P2P_THREAD:
+ return mMessageHelper.insertMessageOnThread(uri, values, /* isIncoming= */
+ true, /* is1To1 */ true);
+ case OUTGOING_MESSAGE_ON_P2P_THREAD:
+ return mMessageHelper.insertMessageOnThread(uri, values, /* isIncoming= */
+ false, /* is1To1 */ true);
+ case INCOMING_MESSAGE_ON_GROUP_THREAD:
+ return mMessageHelper.insertMessageOnThread(uri, values, /* isIncoming= */
+ true, /* is1To1 */ false);
+ case OUTGOING_MESSAGE_ON_GROUP_THREAD:
+ return mMessageHelper.insertMessageOnThread(uri, values, /* isIncoming= */
+ false, /* is1To1 */ false);
+ case INCOMING_MESSAGE_ON_P2P_THREAD_WITH_ID:
+ case OUTGOING_MESSAGE_ON_P2P_THREAD_WITH_ID:
+ case INCOMING_MESSAGE_ON_GROUP_THREAD_WITH_ID:
+ case OUTGOING_MESSAGE_ON_GROUP_THREAD_WITH_ID:
+ Log.e(TAG, "Inserting a message with a specific id is not supported, uri: " + uri);
+ break;
+ case FILE_TRANSFER_WITH_ID:
+ Log.e(TAG, "Inserting a file transfer without a message is not supported, uri: "
+ + uri);
+ break;
+ case EVENT:
+ Log.e(TAG,
+ "Inserting event using unified event query is not supported, uri: " + uri);
+ break;
+ default:
+ Log.e(TAG, "Invalid insert: " + uri);
+ }
+
+ return null;
+ }
+
+ @Override
+ public synchronized int delete(Uri uri, String selection, String[] selectionArgs) {
+ int match = URL_MATCHER.match(uri);
+ int deletedCount = 0;
+
+ switch (match) {
+ case UNIFIED_RCS_THREAD:
+ case UNIFIED_RCS_THREAD_WITH_ID:
+ Log.e(TAG, "Deleting entries from unified view is not allowed: " + uri);
+ break;
+ case PARTICIPANT:
+ Log.e(TAG, "Deleting participant with selection is not allowed: " + uri);
+ break;
+ case PARTICIPANT_WITH_ID:
+ return mParticipantHelper.deleteParticipantWithId(uri);
+ case PARTICIPANT_ALIAS_CHANGE_EVENT:
+ Log.e(TAG, "Deleting participant events without id is not allowed: " + uri);
+ break;
+ case PARTICIPANT_ALIAS_CHANGE_EVENT_WITH_ID:
+ return mEventHelper.deleteParticipantEvent(uri);
+ case P2P_THREAD:
+ return mThreadHelper.delete1To1Thread(selection, selectionArgs);
+ case P2P_THREAD_WITH_ID:
+ return mThreadHelper.delete1To1ThreadWithId(uri);
+ case P2P_THREAD_PARTICIPANT:
+ Log.e(TAG, "Removing participant from 1 to 1 thread is not allowed, uri: " + uri);
+ break;
+ case GROUP_THREAD:
+ return mThreadHelper.deleteGroupThread(selection, selectionArgs);
+ case GROUP_THREAD_WITH_ID:
+ return mThreadHelper.deleteGroupThreadWithId(uri);
+ case GROUP_THREAD_PARTICIPANT_JOINED_EVENT_WITH_ID:
+ return mEventHelper.deleteGroupThreadEvent(uri);
+ case GROUP_THREAD_PARTICIPANT_LEFT_EVENT_WITH_ID:
+ return mEventHelper.deleteGroupThreadEvent(uri);
+ case GROUP_THREAD_NAME_CHANGE_EVENT_WITH_ID:
+ return mEventHelper.deleteGroupThreadEvent(uri);
+ case GROUP_THREAD_PARTICIPANT_JOINED_EVENT:
+ case GROUP_THREAD_PARTICIPANT_LEFT_EVENT:
+ case GROUP_THREAD_NAME_CHANGE_EVENT:
+ case GROUP_THREAD_ICON_CHANGE_EVENT:
+ Log.e(TAG, "Deleting thread events via selection is not allowed, uri: " + uri);
+ break;
+ case GROUP_THREAD_ICON_CHANGE_EVENT_WITH_ID:
+ return mEventHelper.deleteGroupThreadEvent(uri);
+ case GROUP_THREAD_PARTICIPANT:
+ Log.e(TAG,
+ "Deleting a participant from group thread via selection is not allowed, "
+ + "uri: "
+ + uri);
+ break;
+ case GROUP_THREAD_PARTICIPANT_WITH_ID:
+ return mParticipantHelper.deleteParticipantFromGroupThread(uri);
+ case UNIFIED_MESSAGE:
+ Log.e(TAG,
+ "Deleting message from unified view with selection is not allowed: " + uri);
+ break;
+ case UNIFIED_MESSAGE_WITH_ID:
+ Log.e(TAG, "Deleting message from unified view with id is not allowed: " + uri);
+ break;
+ case UNIFIED_MESSAGE_WITH_FILE_TRANSFER:
+ Log.e(TAG, "Deleting file transfer using message uri is not allowed, uri: " + uri);
+ break;
+ case INCOMING_MESSAGE:
+ return mMessageHelper.deleteIncomingMessageWithSelection(selection, selectionArgs);
+ case INCOMING_MESSAGE_WITH_ID:
+ return mMessageHelper.deleteIncomingMessageWithId(uri);
+ case OUTGOING_MESSAGE:
+ return mMessageHelper.deleteOutgoingMessageWithSelection(selection, selectionArgs);
+ case OUTGOING_MESSAGE_WITH_ID:
+ return mMessageHelper.deleteOutgoingMessageWithId(uri);
+ case OUTGOING_MESSAGE_DELIVERY:
+ case OUTGOING_MESSAGE_DELIVERY_WITH_ID:
+ Log.e(TAG, "Deleting message deliveries is not supported, uri: " + uri);
+ break;
+ case UNIFIED_MESSAGE_ON_THREAD:
+ case UNIFIED_MESSAGE_ON_THREAD_WITH_ID:
+ case INCOMING_MESSAGE_ON_P2P_THREAD:
+ case INCOMING_MESSAGE_ON_P2P_THREAD_WITH_ID:
+ case OUTGOING_MESSAGE_ON_P2P_THREAD:
+ case OUTGOING_MESSAGE_ON_P2P_THREAD_WITH_ID:
+ case INCOMING_MESSAGE_ON_GROUP_THREAD:
+ case INCOMING_MESSAGE_ON_GROUP_THREAD_WITH_ID:
+ case OUTGOING_MESSAGE_ON_GROUP_THREAD:
+ case OUTGOING_MESSAGE_ON_GROUP_THREAD_WITH_ID:
+ Log.e(TAG, "Deleting messages using thread uris is not supported, uri: " + uri);
+ break;
+ case FILE_TRANSFER_WITH_ID:
+ return mMessageHelper.deleteFileTransfer(uri);
+ case EVENT:
+ Log.e(TAG, "Deleting events using unified event uri is not supported, uri: " + uri);
+ break;
+ default:
+ Log.e(TAG, "Invalid delete: " + uri);
+ }
+
+ return deletedCount;
+ }
+
+ @Override
+ public synchronized int update(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs) {
+ int match = URL_MATCHER.match(uri);
+ int updatedCount = 0;
+
+ switch (match) {
+ case UNIFIED_RCS_THREAD:
+ case UNIFIED_RCS_THREAD_WITH_ID:
+ Log.e(TAG, "Updating unified thread view is not supported, uri: " + uri);
+ break;
+ case PARTICIPANT:
+ Log.e(TAG, "Updating participants with selection is not supported, uri: " + uri);
+ break;
+ case PARTICIPANT_WITH_ID:
+ return mParticipantHelper.updateParticipantWithId(values, uri);
+ case PARTICIPANT_ALIAS_CHANGE_EVENT:
+ case PARTICIPANT_ALIAS_CHANGE_EVENT_WITH_ID:
+ Log.e(TAG, "Updating events is not supported, uri: " + uri);
+ break;
+ case P2P_THREAD:
+ return mThreadHelper.update1To1Thread(values, selection, selectionArgs);
+ case P2P_THREAD_WITH_ID:
+ return mThreadHelper.update1To1ThreadWithId(values, uri);
+ case P2P_THREAD_PARTICIPANT:
+ Log.e(TAG, "Updating junction table entries is not supported, uri: " + uri);
+ break;
+ case GROUP_THREAD:
+ return mThreadHelper.updateGroupThread(values, selection, selectionArgs);
+ case GROUP_THREAD_WITH_ID:
+ return mThreadHelper.updateGroupThreadWithId(values, uri);
+ case GROUP_THREAD_PARTICIPANT_JOINED_EVENT:
+ case GROUP_THREAD_PARTICIPANT_JOINED_EVENT_WITH_ID:
+ case GROUP_THREAD_PARTICIPANT_LEFT_EVENT:
+ case GROUP_THREAD_PARTICIPANT_LEFT_EVENT_WITH_ID:
+ case GROUP_THREAD_NAME_CHANGE_EVENT:
+ case GROUP_THREAD_NAME_CHANGE_EVENT_WITH_ID:
+ case GROUP_THREAD_ICON_CHANGE_EVENT:
+ case GROUP_THREAD_ICON_CHANGE_EVENT_WITH_ID:
+ Log.e(TAG, "Updating thread events is not supported, uri: " + uri);
+ break;
+ case GROUP_THREAD_PARTICIPANT:
+ case GROUP_THREAD_PARTICIPANT_WITH_ID:
+ Log.e(TAG, "Updating junction table entries is not supported, uri: " + uri);
+ break;
+ case UNIFIED_MESSAGE:
+ case UNIFIED_MESSAGE_WITH_ID:
+ Log.e(TAG, "Updating unified message view is not supported, uri: " + uri);
+ break;
+ case UNIFIED_MESSAGE_WITH_FILE_TRANSFER:
+ Log.e(TAG,
+ "Updating file transfer using unified message uri is not supported, uri: "
+ + uri);
+ case INCOMING_MESSAGE:
+ Log.e(TAG,
+ "Updating an incoming message via selection is not supported, uri: " + uri);
+ break;
+ case INCOMING_MESSAGE_WITH_ID:
+ return mMessageHelper.updateIncomingMessage(uri, values);
+ case OUTGOING_MESSAGE:
+ Log.e(TAG,
+ "Updating an outgoing message via selection is not supported, uri: " + uri);
+ break;
+ case OUTGOING_MESSAGE_WITH_ID:
+ return mMessageHelper.updateOutgoingMessage(uri, values);
+ case OUTGOING_MESSAGE_DELIVERY:
+ Log.e(TAG, "Updating message deliveries using message uris is not supported, uri: "
+ + uri);
+ break;
+ case OUTGOING_MESSAGE_DELIVERY_WITH_ID:
+ return mMessageHelper.updateDelivery(uri, values);
+ case UNIFIED_MESSAGE_ON_THREAD:
+ case UNIFIED_MESSAGE_ON_THREAD_WITH_ID:
+ case INCOMING_MESSAGE_ON_P2P_THREAD:
+ case INCOMING_MESSAGE_ON_P2P_THREAD_WITH_ID:
+ case OUTGOING_MESSAGE_ON_P2P_THREAD:
+ case OUTGOING_MESSAGE_ON_P2P_THREAD_WITH_ID:
+ case INCOMING_MESSAGE_ON_GROUP_THREAD:
+ case INCOMING_MESSAGE_ON_GROUP_THREAD_WITH_ID:
+ case OUTGOING_MESSAGE_ON_GROUP_THREAD:
+ case OUTGOING_MESSAGE_ON_GROUP_THREAD_WITH_ID:
+ Log.e(TAG, "Updating messages using threads uris is not supported, uri: " + uri);
+ break;
+ case FILE_TRANSFER_WITH_ID:
+ return mMessageHelper.updateFileTransfer(uri, values);
+ case EVENT:
+ Log.e(TAG, "Updating events is not supported, uri: " + uri);
+ break;
+ default:
+ Log.e(TAG, "Invalid update: " + uri);
+ }
+
+ return updatedCount;
+ }
+}
diff --git a/src/com/android/providers/telephony/RcsProviderCanonicalAddressHelper.java b/src/com/android/providers/telephony/RcsProviderCanonicalAddressHelper.java
new file mode 100644
index 0000000..496b512
--- /dev/null
+++ b/src/com/android/providers/telephony/RcsProviderCanonicalAddressHelper.java
@@ -0,0 +1,54 @@
+package com.android.providers.telephony;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.provider.BaseColumns;
+import android.provider.Telephony;
+
+public class RcsProviderCanonicalAddressHelper {
+ SQLiteOpenHelper mSQLiteOpenHelper;
+
+ RcsProviderCanonicalAddressHelper(SQLiteOpenHelper sqLiteOpenHelper) {
+ mSQLiteOpenHelper = sqLiteOpenHelper;
+ }
+
+ Cursor getOrCreateCanonicalAddress(String canonicalAddress) {
+ SQLiteDatabase db = mSQLiteOpenHelper.getReadableDatabase();
+
+ Cursor cursor = db.query(
+ MmsSmsProvider.TABLE_CANONICAL_ADDRESSES,
+ new String[]{BaseColumns._ID}, Telephony.CanonicalAddressesColumns.ADDRESS + "=?",
+ new String[]{canonicalAddress}, null, null, null);
+
+ if (cursor != null && cursor.getCount() > 0) {
+ return cursor;
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ return insertCanonicalAddress(canonicalAddress);
+ }
+
+ private Cursor insertCanonicalAddress(String canonicalAddress) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Telephony.CanonicalAddressesColumns.ADDRESS, canonicalAddress);
+
+ SQLiteDatabase db = mSQLiteOpenHelper.getWritableDatabase();
+
+ long id = db.insert(MmsSmsProvider.TABLE_CANONICAL_ADDRESSES, null, contentValues);
+
+ if (id == -1) {
+ return null;
+ }
+
+ MatrixCursor matrixCursor = new MatrixCursor(new String[]{BaseColumns._ID}, 1);
+ matrixCursor.addRow(new Object[]{id});
+
+ return matrixCursor;
+ }
+}
diff --git a/src/com/android/providers/telephony/RcsProviderEventHelper.java b/src/com/android/providers/telephony/RcsProviderEventHelper.java
new file mode 100644
index 0000000..c6a6fc6
--- /dev/null
+++ b/src/com/android/providers/telephony/RcsProviderEventHelper.java
@@ -0,0 +1,254 @@
+/*
+ * 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.providers.telephony;
+
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantEventColumns.NEW_ALIAS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.ICON_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.NAME_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_JOINED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_LEFT_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsThreadColumns.RCS_THREAD_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.DESTINATION_PARTICIPANT_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.EVENT_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.EVENT_TYPE_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.NEW_ICON_URI_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.NEW_NAME_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.SOURCE_PARTICIPANT_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.TIMESTAMP_COLUMN;
+import static android.provider.Telephony.RcsColumns.TRANSACTION_FAILED;
+import static android.telephony.ims.RcsEventQueryParams.ALL_EVENTS;
+import static android.telephony.ims.RcsEventQueryParams.ALL_GROUP_THREAD_EVENTS;
+import static android.telephony.ims.RcsEventQueryParams.EVENT_QUERY_PARAMETERS_KEY;
+
+import static android.telephony.ims.RcsQueryContinuationToken.EVENT_QUERY_CONTINUATION_TOKEN_TYPE;
+import static android.telephony.ims.RcsQueryContinuationToken.QUERY_CONTINUATION_TOKEN;
+import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_EVENT_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_THREAD_EVENT_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_THREAD_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_UNIFIED_EVENT_VIEW;
+import static com.android.providers.telephony.RcsProvider.TAG;
+import static com.android.providers.telephony.RcsProviderThreadHelper.getThreadIdFromUri;
+import static com.android.providers.telephony.RcsProviderUtil.INSERTION_FAILED;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.Bundle;
+import android.telephony.ims.RcsEventQueryParams;
+import android.telephony.ims.RcsQueryContinuationToken;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Constants and helpers related to events for {@link RcsProvider} to keep the code clean.
+ *
+ * @hide
+ */
+class RcsProviderEventHelper {
+ private static final int PARTICIPANT_INDEX_IN_EVENT_URI = 1;
+ private static final int EVENT_INDEX_IN_EVENT_URI = 3;
+
+ @VisibleForTesting
+ public static void createRcsEventTables(SQLiteDatabase db) {
+ Log.d(TAG, "Creating event tables");
+
+ // Add the event tables
+ db.execSQL("CREATE TABLE " + RCS_THREAD_EVENT_TABLE + "(" + EVENT_ID_COLUMN
+ + " INTEGER PRIMARY KEY AUTOINCREMENT, " + RCS_THREAD_ID_COLUMN + " INTEGER, "
+ + SOURCE_PARTICIPANT_ID_COLUMN + " INTEGER, " + EVENT_TYPE_COLUMN + " INTEGER, "
+ + TIMESTAMP_COLUMN + " INTEGER, " + DESTINATION_PARTICIPANT_ID_COLUMN + " INTEGER, "
+ + NEW_ICON_URI_COLUMN + " TEXT, " + NEW_NAME_COLUMN + " TEXT, " + " FOREIGN KEY ("
+ + RCS_THREAD_ID_COLUMN + ") REFERENCES " + RCS_THREAD_TABLE + " ("
+ + RCS_THREAD_ID_COLUMN + "), FOREIGN KEY (" + SOURCE_PARTICIPANT_ID_COLUMN
+ + ") REFERENCES " + RCS_PARTICIPANT_TABLE + " (" + RCS_PARTICIPANT_ID_COLUMN
+ + "))");
+
+ db.execSQL("CREATE TABLE " + RCS_PARTICIPANT_EVENT_TABLE + "(" + EVENT_ID_COLUMN
+ + " INTEGER PRIMARY KEY AUTOINCREMENT, " + SOURCE_PARTICIPANT_ID_COLUMN +
+ " INTEGER, " + TIMESTAMP_COLUMN + " INTEGER, "
+ + NEW_ALIAS_COLUMN + " TEXT," + " FOREIGN KEY (" + SOURCE_PARTICIPANT_ID_COLUMN
+ + ") REFERENCES " + RCS_PARTICIPANT_TABLE + " (" + RCS_PARTICIPANT_ID_COLUMN
+ + "))");
+
+ // Add the views
+
+ // The following is a unified event view that puts every entry in both tables into one query
+ db.execSQL("CREATE VIEW " + RCS_UNIFIED_EVENT_VIEW + " AS "
+ + "SELECT " + PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE + " AS " + EVENT_TYPE_COLUMN
+ + ", " + EVENT_ID_COLUMN + ", " + SOURCE_PARTICIPANT_ID_COLUMN + ", "
+ + TIMESTAMP_COLUMN + ", " + NEW_ALIAS_COLUMN + ", NULL as " + RCS_THREAD_ID_COLUMN
+ + ", NULL as " + DESTINATION_PARTICIPANT_ID_COLUMN + ", NULL as "
+ + NEW_ICON_URI_COLUMN + ", NULL as " + NEW_NAME_COLUMN + " "
+ + "FROM " + RCS_PARTICIPANT_EVENT_TABLE + " "
+ + "UNION "
+ + "SELECT " + EVENT_TYPE_COLUMN + ", " + EVENT_ID_COLUMN + ", "
+ + SOURCE_PARTICIPANT_ID_COLUMN + ", " + TIMESTAMP_COLUMN + ", "
+ + "NULL as " + NEW_ALIAS_COLUMN + ", " + RCS_THREAD_ID_COLUMN + ", "
+ + DESTINATION_PARTICIPANT_ID_COLUMN + ", " + NEW_ICON_URI_COLUMN + ", "
+ + NEW_NAME_COLUMN + " "
+ + "FROM " + RCS_THREAD_EVENT_TABLE);
+ }
+
+ private final SQLiteOpenHelper mSqLiteOpenHelper;
+
+ Cursor queryEvents(Bundle bundle) {
+ RcsEventQueryParams queryParameters = null;
+ RcsQueryContinuationToken continuationToken = null;
+
+ if (bundle != null) {
+ queryParameters = bundle.getParcelable(EVENT_QUERY_PARAMETERS_KEY);
+ continuationToken = bundle.getParcelable(QUERY_CONTINUATION_TOKEN);
+ }
+
+ if (continuationToken != null) {
+ return RcsProviderUtil.performContinuationQuery(mSqLiteOpenHelper.getReadableDatabase(),
+ continuationToken);
+ }
+
+ // if no query parameters were entered, build an empty query parameters object
+ if (queryParameters == null) {
+ queryParameters = new RcsEventQueryParams.Builder().build();
+ }
+
+ return performInitialQuery(queryParameters);
+ }
+
+ private Cursor performInitialQuery(RcsEventQueryParams queryParameters) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+ StringBuilder rawQuery = new StringBuilder("SELECT * FROM ").append(RCS_UNIFIED_EVENT_VIEW);
+
+ int eventType = queryParameters.getEventType();
+ if (eventType != ALL_EVENTS) {
+ rawQuery.append(" WHERE ").append(EVENT_TYPE_COLUMN);
+ if (eventType == ALL_GROUP_THREAD_EVENTS) {
+ rawQuery.append(" IN (").append(
+ PARTICIPANT_JOINED_EVENT_TYPE).append(", ").append(
+ PARTICIPANT_LEFT_EVENT_TYPE).append(", ").append(
+ ICON_CHANGED_EVENT_TYPE).append(", ").append(
+ NAME_CHANGED_EVENT_TYPE).append(
+ ")");
+ } else {
+ rawQuery.append("=").append(eventType);
+ }
+ }
+
+ rawQuery.append(" ORDER BY ");
+
+ int sortingProperty = queryParameters.getSortingProperty();
+ if (sortingProperty == RcsEventQueryParams.SORT_BY_TIMESTAMP) {
+ rawQuery.append(TIMESTAMP_COLUMN);
+ } else {
+ rawQuery.append(EVENT_ID_COLUMN);
+ }
+
+ rawQuery.append(queryParameters.getSortDirection() ? " ASC " : " DESC ");
+
+ RcsProviderUtil.appendLimit(rawQuery, queryParameters.getLimit());
+ String rawQueryAsString = rawQuery.toString();
+ Cursor cursor = db.rawQuery(rawQueryAsString, null);
+
+ // if the query was paginated, build the next query
+ int limit = queryParameters.getLimit();
+ if (limit > 0) {
+ RcsProviderUtil.createContinuationTokenBundle(cursor,
+ new RcsQueryContinuationToken(EVENT_QUERY_CONTINUATION_TOKEN_TYPE,
+ rawQueryAsString, limit, limit), QUERY_CONTINUATION_TOKEN);
+ }
+
+ return cursor;
+ }
+
+ RcsProviderEventHelper(SQLiteOpenHelper sqLiteOpenHelper) {
+ mSqLiteOpenHelper = sqLiteOpenHelper;
+ }
+
+ long insertParticipantEvent(Uri uri, ContentValues values) {
+ String participantId = getParticipantIdFromUri(uri);
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ values.put(SOURCE_PARTICIPANT_ID_COLUMN, participantId);
+ long rowId = db.insert(RCS_PARTICIPANT_EVENT_TABLE, SOURCE_PARTICIPANT_ID_COLUMN, values);
+ values.remove(SOURCE_PARTICIPANT_ID_COLUMN);
+
+ if (rowId == INSERTION_FAILED) {
+ return TRANSACTION_FAILED;
+ }
+
+ return rowId;
+ }
+
+ long insertParticipantJoinedEvent(Uri uri, ContentValues values) {
+ return insertGroupThreadEvent(uri, values, PARTICIPANT_JOINED_EVENT_TYPE);
+ }
+
+ long insertParticipantLeftEvent(Uri uri, ContentValues values) {
+ return insertGroupThreadEvent(uri, values, PARTICIPANT_LEFT_EVENT_TYPE);
+ }
+
+ long insertThreadNameChangeEvent(Uri uri, ContentValues values) {
+ return insertGroupThreadEvent(uri, values, NAME_CHANGED_EVENT_TYPE);
+ }
+
+ long insertThreadIconChangeEvent(Uri uri, ContentValues values) {
+ return insertGroupThreadEvent(uri, values, ICON_CHANGED_EVENT_TYPE);
+ }
+
+ private long insertGroupThreadEvent(Uri uri, ContentValues valuesParameter,
+ int eventType) {
+ String threadId = getThreadIdFromUri(uri);
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ ContentValues values = new ContentValues(valuesParameter);
+ values.put(EVENT_TYPE_COLUMN, eventType);
+ values.put(RCS_THREAD_ID_COLUMN, threadId);
+ long rowId = db.insert(RCS_THREAD_EVENT_TABLE, EVENT_ID_COLUMN, values);
+
+ if (rowId == INSERTION_FAILED) {
+ return TRANSACTION_FAILED;
+ }
+
+ return rowId;
+ }
+
+ int deleteParticipantEvent(Uri uri) {
+ String eventId = getEventIdFromEventUri(uri);
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+
+ return db.delete(RCS_PARTICIPANT_EVENT_TABLE, EVENT_ID_COLUMN + "=?",
+ new String[]{eventId});
+ }
+
+ int deleteGroupThreadEvent(Uri uri) {
+ String eventId = getEventIdFromEventUri(uri);
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+
+ return db.delete(RCS_THREAD_EVENT_TABLE, EVENT_ID_COLUMN + "=?", new String[]{eventId});
+ }
+
+ private String getEventIdFromEventUri(Uri uri) {
+ return uri.getPathSegments().get(EVENT_INDEX_IN_EVENT_URI);
+ }
+
+ private String getParticipantIdFromUri(Uri uri) {
+ return uri.getPathSegments().get(PARTICIPANT_INDEX_IN_EVENT_URI);
+ }
+}
diff --git a/src/com/android/providers/telephony/RcsProviderMessageHelper.java b/src/com/android/providers/telephony/RcsProviderMessageHelper.java
new file mode 100644
index 0000000..670c641
--- /dev/null
+++ b/src/com/android/providers/telephony/RcsProviderMessageHelper.java
@@ -0,0 +1,707 @@
+/*
+ * Copyright (C) 2018 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.providers.telephony;
+
+import static android.provider.Telephony.RcsColumns.CONTENT_AND_AUTHORITY;
+import static android.provider.Telephony.RcsColumns.Rcs1To1ThreadColumns.RCS_1_TO_1_THREAD_URI_PART;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.CONTENT_TYPE_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.CONTENT_URI_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.FILE_SIZE_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.FILE_TRANSFER_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.HEIGHT_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.DURATION_MILLIS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.PREVIEW_TYPE_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.PREVIEW_URI_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.SESSION_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.SUCCESSFULLY_TRANSFERRED_BYTES;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.TRANSFER_STATUS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.WIDTH_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.RCS_GROUP_THREAD_URI_PART;
+import static android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns.ARRIVAL_TIMESTAMP_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns.INCOMING_MESSAGE_URI_PART;
+import static android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns.SENDER_PARTICIPANT_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.GLOBAL_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.LATITUDE_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.LONGITUDE_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.MESSAGE_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.MESSAGE_TEXT_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.ORIGINATION_TIMESTAMP_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.STATUS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.SUB_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageDeliveryColumns.DELIVERED_TIMESTAMP_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsOutgoingMessageColumns.OUTGOING_MESSAGE_URI_PART;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadColumns.RCS_THREAD_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_INCOMING;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_OUTGOING;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.UNIFIED_INCOMING_MESSAGE_VIEW;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.UNIFIED_OUTGOING_MESSAGE_VIEW;
+import static android.provider.Telephony.RcsColumns.TRANSACTION_FAILED;
+import static android.telephony.ims.RcsMessageQueryParams.MESSAGE_QUERY_PARAMETERS_KEY;
+import static android.telephony.ims.RcsMessageQueryParams.THREAD_ID_NOT_SET;
+
+import static android.telephony.ims.RcsQueryContinuationToken.MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE;
+import static android.telephony.ims.RcsQueryContinuationToken.QUERY_CONTINUATION_TOKEN;
+import static com.android.providers.telephony.RcsProvider.RCS_FILE_TRANSFER_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_INCOMING_MESSAGE_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_MESSAGE_DELIVERY_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_MESSAGE_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_OUTGOING_MESSAGE_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_THREAD_JUNCTION_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_THREAD_TABLE;
+import static com.android.providers.telephony.RcsProvider.TAG;
+import static com.android.providers.telephony.RcsProvider.UNIFIED_MESSAGE_VIEW;
+import static com.android.providers.telephony.RcsProviderThreadHelper.getThreadIdFromUri;
+import static com.android.providers.telephony.RcsProviderUtil.INSERTION_FAILED;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns;
+import android.provider.Telephony.RcsColumns.RcsMessageDeliveryColumns;
+import android.telephony.ims.RcsMessageQueryParams;
+import android.telephony.ims.RcsQueryContinuationToken;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Constants and helpers related to messages for {@link RcsProvider} to keep the code clean.
+ *
+ * @hide
+ */
+public class RcsProviderMessageHelper {
+ private static final int MESSAGE_ID_INDEX_IN_URI = 1;
+ private static final int MESSAGE_ID_INDEX_IN_THREAD_URI = 3;
+
+ private final SQLiteOpenHelper mSqLiteOpenHelper;
+
+ @VisibleForTesting
+ public static void createRcsMessageTables(SQLiteDatabase db) {
+ Log.d(TAG, "Creating message tables");
+
+ // Add the message tables
+ db.execSQL("CREATE TABLE " + RCS_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN
+ + " INTEGER PRIMARY KEY AUTOINCREMENT, " + RCS_THREAD_ID_COLUMN + " INTEGER, "
+ + GLOBAL_ID_COLUMN + " TEXT, " + SUB_ID_COLUMN + " INTEGER, " + MESSAGE_TEXT_COLUMN
+ + " TEXT," + LATITUDE_COLUMN + " REAL, " + LONGITUDE_COLUMN + " REAL, "
+ + STATUS_COLUMN + " INTEGER, " + ORIGINATION_TIMESTAMP_COLUMN
+ + " INTEGER, FOREIGN KEY(" + RCS_THREAD_ID_COLUMN + ") REFERENCES "
+ + RCS_THREAD_TABLE + "(" + RCS_THREAD_ID_COLUMN + "))");
+
+ db.execSQL("CREATE TABLE " + RCS_INCOMING_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN
+ + " INTEGER PRIMARY KEY, " + SENDER_PARTICIPANT_ID_COLUMN + " INTEGER, "
+ + ARRIVAL_TIMESTAMP_COLUMN + " INTEGER, "
+ + RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN + " INTEGER, FOREIGN KEY ("
+ + MESSAGE_ID_COLUMN + ") REFERENCES " + RCS_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN
+ + "))");
+
+ db.execSQL("CREATE TABLE " + RCS_OUTGOING_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN
+ + " INTEGER PRIMARY KEY, FOREIGN KEY (" + MESSAGE_ID_COLUMN + ") REFERENCES "
+ + RCS_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN + "))");
+
+ db.execSQL("CREATE TABLE " + RCS_MESSAGE_DELIVERY_TABLE + "(" + MESSAGE_ID_COLUMN
+ + " INTEGER, " + RCS_PARTICIPANT_ID_COLUMN + " INTEGER, "
+ + DELIVERED_TIMESTAMP_COLUMN + " INTEGER, "
+ + RcsMessageDeliveryColumns.SEEN_TIMESTAMP_COLUMN + " INTEGER, "
+ + "CONSTRAINT message_delivery PRIMARY KEY (" + MESSAGE_ID_COLUMN + ", "
+ + RCS_PARTICIPANT_ID_COLUMN + "), FOREIGN KEY (" + MESSAGE_ID_COLUMN
+ + ") REFERENCES " + RCS_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN + "), FOREIGN KEY ("
+ + RCS_PARTICIPANT_ID_COLUMN + ") REFERENCES " + RCS_PARTICIPANT_TABLE + "("
+ + RCS_PARTICIPANT_ID_COLUMN + "))");
+
+ db.execSQL("CREATE TABLE " + RCS_FILE_TRANSFER_TABLE + " (" + FILE_TRANSFER_ID_COLUMN
+ + " INTEGER PRIMARY KEY AUTOINCREMENT, " + MESSAGE_ID_COLUMN + " INTEGER, "
+ + SESSION_ID_COLUMN + " TEXT, " + CONTENT_URI_COLUMN + " TEXT, "
+ + CONTENT_TYPE_COLUMN + " TEXT, " + FILE_SIZE_COLUMN + " INTEGER, "
+ + SUCCESSFULLY_TRANSFERRED_BYTES + " INTEGER, " + TRANSFER_STATUS_COLUMN +
+ " INTEGER, " + WIDTH_COLUMN + " INTEGER, " + HEIGHT_COLUMN + " INTEGER, "
+ + DURATION_MILLIS_COLUMN + " INTEGER, " + PREVIEW_URI_COLUMN + " TEXT, "
+ + PREVIEW_TYPE_COLUMN + " TEXT, FOREIGN KEY (" + MESSAGE_ID_COLUMN + ") REFERENCES "
+ + RCS_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN + "))");
+
+ // Add the views
+ //
+ // The following view inner joins incoming messages with all messages, inner joins outgoing
+ // messages with all messages, and unions them together, while also adding an is_incoming
+ // column for easily telling where the record came from. This may have been achieved with
+ // an outer join but SQLite doesn't support them.
+ //
+ // CREATE VIEW unified_message_view AS
+ //
+ // SELECT rcs_message.rcs_message_row_id,
+ // rcs_message.rcs_thread_id,
+ // rcs_message.rcs_message_global_id,
+ // rcs_message.sub_id,
+ // rcs_message.status,
+ // rcs_message.origination_timestamp,
+ // rcs_message.rcs_text,
+ // rcs_message.latitude,
+ // rcs_message.longitude,
+ // 0 AS sender_participant,
+ // 0 AS arrival_timestamp,
+ // 0 AS seen_timestamp,
+ // outgoing AS message_type
+ //
+ // FROM rcs_message INNER JOIN rcs_outgoing_message
+ // ON rcs_message.rcs_message_row_id=rcs_outgoing_message.rcs_message_row_id
+ //
+ // UNION
+ //
+ // SELECT rcs_message.rcs_message_row_id,
+ // rcs_message.rcs_thread_id,
+ // rcs_message.rcs_message_global_id,
+ // rcs_message.sub_id,
+ // rcs_message.status,
+ // rcs_message.origination_timestamp,
+ // rcs_message.rcs_text,
+ // rcs_message.latitude,
+ // rcs_message.longitude,
+ // rcs_incoming_message.sender_participant,
+ // rcs_incoming_message.arrival_timestamp,
+ // rcs_incoming_message.seen_timestamp,
+ // incoming AS message_type
+ //
+ // FROM rcs_message INNER JOIN rcs_incoming_message
+ // ON rcs_message.rcs_message_row_id=rcs_incoming_message.rcs_message_row_id
+ //
+ db.execSQL("CREATE VIEW " + UNIFIED_MESSAGE_VIEW + " AS SELECT "
+ + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + GLOBAL_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + SUB_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + STATUS_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + ORIGINATION_TIMESTAMP_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + MESSAGE_TEXT_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + LATITUDE_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + LONGITUDE_COLUMN + ", "
+ + "0 AS " + SENDER_PARTICIPANT_ID_COLUMN + ", "
+ + "0 AS " + ARRIVAL_TIMESTAMP_COLUMN + ", "
+ + "0 AS " + RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN + ", "
+ + MESSAGE_TYPE_OUTGOING + " AS " + MESSAGE_TYPE_COLUMN
+ + " FROM " + RCS_MESSAGE_TABLE + " INNER JOIN " + RCS_OUTGOING_MESSAGE_TABLE
+ + " ON " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "="
+ + RCS_OUTGOING_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN
+ + " UNION SELECT "
+ + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + GLOBAL_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + SUB_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + STATUS_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + ORIGINATION_TIMESTAMP_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + MESSAGE_TEXT_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + LATITUDE_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + LONGITUDE_COLUMN + ", "
+ + RCS_INCOMING_MESSAGE_TABLE + "." + SENDER_PARTICIPANT_ID_COLUMN + ", "
+ + RCS_INCOMING_MESSAGE_TABLE + "." + ARRIVAL_TIMESTAMP_COLUMN + ", "
+ + RCS_INCOMING_MESSAGE_TABLE + "." + RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN
+ + ", "
+ + MESSAGE_TYPE_INCOMING + " AS " + MESSAGE_TYPE_COLUMN
+ + " FROM " + RCS_MESSAGE_TABLE + " INNER JOIN " + RCS_INCOMING_MESSAGE_TABLE
+ + " ON " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "="
+ + RCS_INCOMING_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN);
+
+ // The following view inner joins incoming messages with all messages
+ //
+ // CREATE VIEW unified_incoming_message_view AS
+ //
+ // SELECT rcs_message.rcs_message_row_id,
+ // rcs_message.rcs_thread_id,
+ // rcs_message.rcs_message_global_id,
+ // rcs_message.sub_id,
+ // rcs_message.status,
+ // rcs_message.origination_timestamp,
+ // rcs_message.rcs_text,
+ // rcs_message.latitude,
+ // rcs_message.longitude,
+ // rcs_incoming_message.sender_participant,
+ // rcs_incoming_message.arrival_timestamp,
+ // rcs_incoming_message.seen_timestamp,
+ //
+ // FROM rcs_message INNER JOIN rcs_incoming_message
+ // ON rcs_message.rcs_message_row_id=rcs_incoming_message.rcs_message_row_id
+
+ db.execSQL("CREATE VIEW " + UNIFIED_INCOMING_MESSAGE_VIEW + " AS SELECT "
+ + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + GLOBAL_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + SUB_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + STATUS_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + ORIGINATION_TIMESTAMP_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + MESSAGE_TEXT_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + LATITUDE_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + LONGITUDE_COLUMN + ", "
+ + RCS_INCOMING_MESSAGE_TABLE + "." + SENDER_PARTICIPANT_ID_COLUMN + ", "
+ + RCS_INCOMING_MESSAGE_TABLE + "." + ARRIVAL_TIMESTAMP_COLUMN + ", "
+ + RCS_INCOMING_MESSAGE_TABLE + "." + RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN
+ + " FROM " + RCS_MESSAGE_TABLE + " INNER JOIN " + RCS_INCOMING_MESSAGE_TABLE
+ + " ON " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "="
+ + RCS_INCOMING_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN);
+
+ // The following view inner joins outgoing messages with all messages.
+ //
+ // CREATE VIEW unified_outgoing_message AS
+ //
+ // SELECT rcs_message.rcs_message_row_id,
+ // rcs_message.rcs_thread_id,
+ // rcs_message.rcs_message_global_id,
+ // rcs_message.sub_id,
+ // rcs_message.status,
+ // rcs_message.origination_timestamp
+ // rcs_message.rcs_text,
+ // rcs_message.latitude,
+ // rcs_message.longitude,
+ //
+ // FROM rcs_message INNER JOIN rcs_outgoing_message
+ // ON rcs_message.rcs_message_row_id=rcs_outgoing_message.rcs_message_row_id
+
+ db.execSQL("CREATE VIEW " + UNIFIED_OUTGOING_MESSAGE_VIEW + " AS SELECT "
+ + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + GLOBAL_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + SUB_ID_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + STATUS_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + ORIGINATION_TIMESTAMP_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + MESSAGE_TEXT_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + LATITUDE_COLUMN + ", "
+ + RCS_MESSAGE_TABLE + "." + LONGITUDE_COLUMN
+ + " FROM " + RCS_MESSAGE_TABLE + " INNER JOIN " + RCS_OUTGOING_MESSAGE_TABLE
+ + " ON " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "="
+ + RCS_OUTGOING_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN);
+
+ // Add triggers
+
+ // Delete the corresponding rcs_message row upon deleting a row in rcs_incoming_message
+ //
+ // CREATE TRIGGER delete_common_message_after_incoming
+ // AFTER DELETE ON rcs_incoming_message
+ // BEGIN
+ // DELETE FROM rcs_message WHERE rcs_message.rcs_message_row_id=OLD.rcs_message_row_id;
+ // END
+ db.execSQL("CREATE TRIGGER deleteCommonMessageAfterIncoming AFTER DELETE ON "
+ + RCS_INCOMING_MESSAGE_TABLE + " BEGIN DELETE FROM " + RCS_MESSAGE_TABLE
+ + " WHERE " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "=OLD."
+ + MESSAGE_ID_COLUMN + "; END");
+
+ // Delete the corresponding rcs_message row upon deleting a row in rcs_outgoing_message
+ //
+ // CREATE TRIGGER delete_common_message_after_outgoing
+ // AFTER DELETE ON rcs_outgoing_message
+ // BEGIN
+ // DELETE FROM rcs_message WHERE rcs_message.rcs_message_row_id=OLD.rcs_message_row_id;
+ // END
+ db.execSQL("CREATE TRIGGER deleteCommonMessageAfterOutgoing AFTER DELETE ON "
+ + RCS_OUTGOING_MESSAGE_TABLE + " BEGIN DELETE FROM " + RCS_MESSAGE_TABLE
+ + " WHERE " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "=OLD."
+ + MESSAGE_ID_COLUMN + "; END");
+ }
+
+ RcsProviderMessageHelper(SQLiteOpenHelper sqLiteOpenHelper) {
+ mSqLiteOpenHelper = sqLiteOpenHelper;
+ }
+
+ Cursor queryMessages(Bundle bundle) {
+ RcsMessageQueryParams queryParameters = null;
+ RcsQueryContinuationToken continuationToken = null;
+
+ if (bundle != null) {
+ queryParameters = bundle.getParcelable(MESSAGE_QUERY_PARAMETERS_KEY);
+ continuationToken = bundle.getParcelable(QUERY_CONTINUATION_TOKEN);
+ }
+
+ if (continuationToken != null) {
+ return RcsProviderUtil.performContinuationQuery(mSqLiteOpenHelper.getReadableDatabase(),
+ continuationToken);
+ }
+
+ // if no parameters were entered, build an empty query parameters object
+ if (queryParameters == null) {
+ queryParameters = new RcsMessageQueryParams.Builder().build();
+ }
+
+ return performInitialQuery(queryParameters);
+ }
+
+ private Cursor performInitialQuery(RcsMessageQueryParams queryParameters) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+
+ StringBuilder rawQuery = new StringBuilder("SELECT * FROM ").append(UNIFIED_MESSAGE_VIEW);
+
+ int messageType = queryParameters.getMessageType();
+ String messageLike = queryParameters.getMessageLike();
+ int threadId = queryParameters.getThreadId();
+
+ boolean isMessageLikePresent = !TextUtils.isEmpty(messageLike);
+ boolean isMessageTypeFiltered = (messageType == MESSAGE_TYPE_INCOMING)
+ || (messageType == MESSAGE_TYPE_OUTGOING);
+ boolean isThreadFiltered = threadId != THREAD_ID_NOT_SET;
+
+ if (isMessageLikePresent || isMessageTypeFiltered || isThreadFiltered) {
+ rawQuery.append(" WHERE ");
+ }
+
+ if (messageType == MESSAGE_TYPE_INCOMING) {
+ rawQuery.append(MESSAGE_TYPE_COLUMN).append("=").append(MESSAGE_TYPE_INCOMING);
+ } else if (messageType == MESSAGE_TYPE_OUTGOING) {
+ rawQuery.append(MESSAGE_TYPE_COLUMN).append("=").append(MESSAGE_TYPE_OUTGOING);
+ }
+
+ if (isMessageLikePresent) {
+ if (isMessageTypeFiltered) {
+ rawQuery.append(" AND ");
+ }
+ rawQuery.append(MESSAGE_TEXT_COLUMN).append(" LIKE \"").append(messageLike)
+ .append("\"");
+ }
+
+ if (isThreadFiltered) {
+ if (isMessageLikePresent || isMessageTypeFiltered) {
+ rawQuery.append(" AND ");
+ }
+ rawQuery.append(RCS_THREAD_ID_COLUMN).append("=").append(threadId);
+ }
+
+ // TODO - figure out a way to see if this message has file transfer or not. Ideally we
+ // should join the unified table with file transfer table, but using a trigger to change a
+ // flag on rcs_message would also work
+
+ rawQuery.append(" ORDER BY ");
+
+ int sortingProperty = queryParameters.getSortingProperty();
+ if (sortingProperty == RcsMessageQueryParams.SORT_BY_TIMESTAMP) {
+ rawQuery.append(ORIGINATION_TIMESTAMP_COLUMN);
+ } else {
+ rawQuery.append(MESSAGE_ID_COLUMN);
+ }
+
+ rawQuery.append(queryParameters.getSortDirection() ? " ASC " : " DESC ");
+
+ RcsProviderUtil.appendLimit(rawQuery, queryParameters.getLimit());
+ String rawQueryAsString = rawQuery.toString();
+ Cursor cursor = db.rawQuery(rawQueryAsString, null);
+
+ // If the query was paginated, build the next query
+ int limit = queryParameters.getLimit();
+ if (limit > 0) {
+ RcsProviderUtil.createContinuationTokenBundle(cursor,
+ new RcsQueryContinuationToken(MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE,
+ rawQueryAsString, limit, limit), QUERY_CONTINUATION_TOKEN);
+ }
+
+ return cursor;
+ }
+
+ Cursor queryUnifiedMessageWithId(Uri uri) {
+ return queryUnifiedMessageWithSelection(getMessageIdSelection(uri), null);
+ }
+
+ Cursor queryUnifiedMessageWithIdInThread(Uri uri) {
+ return queryUnifiedMessageWithSelection(getMessageIdSelectionInThreadUri(uri), null);
+ }
+
+ Cursor queryUnifiedMessageWithSelection(String selection, String[] selectionArgs) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+ return db.query(UNIFIED_MESSAGE_VIEW, null, selection, selectionArgs, null, null, null,
+ null);
+ }
+
+ Cursor queryIncomingMessageWithId(Uri uri) {
+ return queryIncomingMessageWithSelection(getMessageIdSelection(uri), null);
+ }
+
+ Cursor queryIncomingMessageWithSelection(String selection, String[] selectionArgs) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+ return db.query(UNIFIED_INCOMING_MESSAGE_VIEW, null, selection, selectionArgs, null, null,
+ null, null);
+ }
+
+ Cursor queryOutgoingMessageWithId(Uri uri) {
+ return queryOutgoingMessageWithSelection(getMessageIdSelection(uri), null);
+ }
+
+ Cursor queryOutgoingMessageWithSelection(String selection, String[] selectionArgs) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+ return db.query(UNIFIED_OUTGOING_MESSAGE_VIEW, null, selection, selectionArgs, null, null,
+ null, null);
+ }
+
+ Cursor queryAllMessagesOnThread(Uri uri, String selection, String[] selectionArgs) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+
+ String appendedSelection = appendThreadIdToSelection(uri, selection);
+ return db.query(UNIFIED_MESSAGE_VIEW, null, appendedSelection, null, null, null, null);
+ }
+
+ Uri insertMessageOnThread(Uri uri, ContentValues valuesParameter, boolean isIncoming,
+ boolean is1To1) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+
+ String threadId = RcsProviderThreadHelper.getThreadIdFromUri(uri);
+ ContentValues values = new ContentValues(valuesParameter);
+ values.put(RCS_THREAD_ID_COLUMN, Integer.parseInt(threadId));
+
+ db.beginTransaction();
+
+ ContentValues subMessageTableValues = new ContentValues();
+ if (isIncoming) {
+ subMessageTableValues = getIncomingMessageValues(values);
+ }
+
+ long rowId;
+ try {
+ rowId = db.insert(RCS_MESSAGE_TABLE, MESSAGE_ID_COLUMN, values);
+ if (rowId == INSERTION_FAILED) {
+ return null;
+ }
+
+ subMessageTableValues.put(MESSAGE_ID_COLUMN, rowId);
+ long tempId = db.insert(
+ isIncoming ? RCS_INCOMING_MESSAGE_TABLE : RCS_OUTGOING_MESSAGE_TABLE,
+ MESSAGE_ID_COLUMN, subMessageTableValues);
+ if (tempId == INSERTION_FAILED) {
+ return null;
+ }
+
+ // if the thread is outgoing, insert message deliveries
+ if (!isIncoming && !insertMessageDeliveriesForOutgoingMessageCreation(db, tempId,
+ threadId)) {
+ return null;
+ }
+
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+
+ values.remove(RCS_THREAD_ID_COLUMN);
+
+ String threadPart = is1To1 ? RCS_1_TO_1_THREAD_URI_PART : RCS_GROUP_THREAD_URI_PART;
+ String messagePart = isIncoming ? INCOMING_MESSAGE_URI_PART : OUTGOING_MESSAGE_URI_PART;
+
+ return CONTENT_AND_AUTHORITY.buildUpon().appendPath(threadPart).appendPath(threadId).
+ appendPath(messagePart).appendPath(Long.toString(rowId)).build();
+ }
+
+ // Tries to insert deliveries for outgoing message, returns false if it fails.
+ private boolean insertMessageDeliveriesForOutgoingMessageCreation(
+ SQLiteDatabase dbInTransaction, long messageId, String threadId) {
+ try (Cursor participantsInThreadCursor = dbInTransaction.query(
+ RCS_PARTICIPANT_THREAD_JUNCTION_TABLE, null, RCS_THREAD_ID_COLUMN + "=?",
+ new String[]{threadId}, null, null, null)) {
+ if (participantsInThreadCursor == null) {
+ return false;
+ }
+
+ while (participantsInThreadCursor.moveToNext()) {
+ String participantId = participantsInThreadCursor.getString(
+ participantsInThreadCursor.getColumnIndex(
+ RCS_PARTICIPANT_ID_COLUMN));
+
+ long insertionRow = insertMessageDelivery(Long.toString(messageId), participantId,
+ new ContentValues());
+
+ if (insertionRow == INSERTION_FAILED) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ long insertMessageDelivery(Uri uri, ContentValues values) {
+ String messageId = getMessageIdFromUri(uri);
+ String participantId = getParticipantIdFromDeliveryUri(uri);
+ return insertMessageDelivery(messageId, participantId, values);
+ }
+
+ private long insertMessageDelivery(String messageId, String participantId,
+ ContentValues valuesParameter) {
+ ContentValues values = new ContentValues(valuesParameter);
+ values.put(MESSAGE_ID_COLUMN, messageId);
+ values.put(RCS_PARTICIPANT_ID_COLUMN, participantId);
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.insert(RCS_MESSAGE_DELIVERY_TABLE, MESSAGE_ID_COLUMN, values);
+ }
+
+ int updateDelivery(Uri uri, ContentValues contentValues) {
+ String messageId = getMessageIdFromUri(uri);
+ String participantId = getParticipantIdFromDeliveryUri(uri);
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.update(RCS_MESSAGE_DELIVERY_TABLE, contentValues,
+ MESSAGE_ID_COLUMN + "=? AND " + RCS_PARTICIPANT_ID_COLUMN + "=?",
+ new String[]{messageId, participantId});
+ }
+
+ int deleteIncomingMessageWithId(Uri uri) {
+ return deleteIncomingMessageWithSelection(getMessageIdSelection(uri), null);
+ }
+
+ int deleteIncomingMessageWithSelection(String selection, String[] selectionArgs) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.delete(RCS_INCOMING_MESSAGE_TABLE, selection, selectionArgs);
+ }
+
+ int deleteOutgoingMessageWithId(Uri uri) {
+ return deleteOutgoingMessageWithSelection(getMessageIdSelection(uri), null);
+ }
+
+ int deleteOutgoingMessageWithSelection(String selection, String[] selectionArgs) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.delete(RCS_OUTGOING_MESSAGE_TABLE, selection, selectionArgs);
+ }
+
+ int updateIncomingMessage(Uri uri, ContentValues values) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+
+ ContentValues incomingMessageValues = getIncomingMessageValues(values);
+
+ int updateCountInIncoming = 0;
+ int updateCountInCommon = 0;
+ db.beginTransaction();
+ if (!incomingMessageValues.isEmpty()) {
+ updateCountInIncoming = db.update(RCS_INCOMING_MESSAGE_TABLE, incomingMessageValues,
+ getMessageIdSelection(uri), null);
+ }
+ if (!values.isEmpty()) {
+ updateCountInCommon = db.update(RCS_MESSAGE_TABLE, values, getMessageIdSelection(uri),
+ null);
+ }
+ db.setTransactionSuccessful();
+ db.endTransaction();
+
+ return Math.max(updateCountInIncoming, updateCountInCommon);
+ }
+
+ int updateOutgoingMessage(Uri uri, ContentValues values) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.update(RCS_MESSAGE_TABLE, values, getMessageIdSelection(uri), null);
+ }
+
+ Cursor queryOutgoingMessageDeliveries(Uri uri) {
+ String messageId = getMessageIdFromUri(uri);
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+ return db.query(RCS_MESSAGE_DELIVERY_TABLE, null, MESSAGE_ID_COLUMN + "=?",
+ new String[]{messageId}, null, null, null);
+ }
+
+ Cursor queryFileTransfer(Uri uri) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+ return db.query(RCS_FILE_TRANSFER_TABLE, null, FILE_TRANSFER_ID_COLUMN + "=?",
+ new String[]{getFileTransferIdFromUri(uri)}, null, null, null, null);
+ }
+
+ long insertFileTransferToMessage(Uri uri, ContentValues values) {
+ String messageId = getMessageIdFromUri(uri);
+ values.put(MESSAGE_ID_COLUMN, messageId);
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ long rowId = db.insert(RCS_FILE_TRANSFER_TABLE, MESSAGE_ID_COLUMN, values);
+ values.remove(MESSAGE_ID_COLUMN);
+
+ if (rowId == INSERTION_FAILED) {
+ rowId = TRANSACTION_FAILED;
+ }
+
+ return rowId;
+ }
+
+ int deleteFileTransfer(Uri uri) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.delete(RCS_FILE_TRANSFER_TABLE, FILE_TRANSFER_ID_COLUMN + "=?",
+ new String[]{getFileTransferIdFromUri(uri)});
+ }
+
+ int updateFileTransfer(Uri uri, ContentValues values) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.update(RCS_FILE_TRANSFER_TABLE, values,
+ FILE_TRANSFER_ID_COLUMN + "=?", new String[]{getFileTransferIdFromUri(uri)});
+ }
+
+ /**
+ * Removes the incoming message values out of all values and returns as a separate content
+ * values object.
+ */
+ private ContentValues getIncomingMessageValues(ContentValues allValues) {
+ ContentValues incomingMessageValues = new ContentValues();
+
+ if (allValues.containsKey(SENDER_PARTICIPANT_ID_COLUMN)) {
+ incomingMessageValues.put(SENDER_PARTICIPANT_ID_COLUMN,
+ allValues.getAsInteger(SENDER_PARTICIPANT_ID_COLUMN));
+ allValues.remove(SENDER_PARTICIPANT_ID_COLUMN);
+ }
+
+ if (allValues.containsKey(ARRIVAL_TIMESTAMP_COLUMN)) {
+ incomingMessageValues.put(
+ ARRIVAL_TIMESTAMP_COLUMN, allValues.getAsLong(ARRIVAL_TIMESTAMP_COLUMN));
+ allValues.remove(ARRIVAL_TIMESTAMP_COLUMN);
+ }
+
+ if (allValues.containsKey(RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN)) {
+ incomingMessageValues.put(
+ RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN,
+ allValues.getAsLong(RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN));
+ allValues.remove(RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN);
+ }
+
+ return incomingMessageValues;
+ }
+
+ private String appendThreadIdToSelection(Uri uri, String selection) {
+ String threadIdSelection = RCS_THREAD_ID_COLUMN + "=" + getThreadIdFromUri(uri);
+
+ if (TextUtils.isEmpty(selection)) {
+ return threadIdSelection;
+ }
+
+ return "(" + selection + ") AND " + threadIdSelection;
+ }
+
+ private String getMessageIdSelection(Uri uri) {
+ return MESSAGE_ID_COLUMN + "=" + getMessageIdFromUri(uri);
+ }
+
+ private String getMessageIdSelectionInThreadUri(Uri uri) {
+ return MESSAGE_ID_COLUMN + "=" + getMessageIdFromThreadUri(uri);
+ }
+
+ private String getMessageIdFromUri(Uri uri) {
+ return uri.getPathSegments().get(MESSAGE_ID_INDEX_IN_URI);
+ }
+
+ private String getFileTransferIdFromUri(Uri uri) {
+ // this works because messages and file transfer uri's have the same indices.
+ return getMessageIdFromUri(uri);
+ }
+
+ private String getParticipantIdFromDeliveryUri(Uri uri) {
+ // this works because messages in threads and participants in deliveries have the same
+ // indices.
+ return getMessageIdFromThreadUri(uri);
+ }
+
+ private String getMessageIdFromThreadUri(Uri uri) {
+ return uri.getPathSegments().get(MESSAGE_ID_INDEX_IN_THREAD_URI);
+ }
+}
diff --git a/src/com/android/providers/telephony/RcsProviderParticipantHelper.java b/src/com/android/providers/telephony/RcsProviderParticipantHelper.java
new file mode 100644
index 0000000..ab23d6d
--- /dev/null
+++ b/src/com/android/providers/telephony/RcsProviderParticipantHelper.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2018 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.providers.telephony;
+
+import static android.provider.Telephony.CanonicalAddressesColumns.ADDRESS;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.CANONICAL_ADDRESS_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_ALIAS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_URI_PART;
+import static android.provider.Telephony.RcsColumns.RcsParticipantHelpers.RCS_PARTICIPANT_WITH_ADDRESS_VIEW;
+import static android.provider.Telephony.RcsColumns.RcsParticipantHelpers.RCS_PARTICIPANT_WITH_THREAD_VIEW;
+import static android.provider.Telephony.RcsColumns.RcsThreadColumns.RCS_THREAD_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.TRANSACTION_FAILED;
+import static android.telephony.ims.RcsParticipantQueryParams.PARTICIPANT_QUERY_PARAMETERS_KEY;
+
+import static android.telephony.ims.RcsQueryContinuationToken.PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE;
+import static android.telephony.ims.RcsQueryContinuationToken.QUERY_CONTINUATION_TOKEN;
+import static com.android.providers.telephony.RcsProvider.GROUP_THREAD_URI_PREFIX;
+import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_THREAD_JUNCTION_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_THREAD_TABLE;
+import static com.android.providers.telephony.RcsProvider.TAG;
+import static com.android.providers.telephony.RcsProviderThreadHelper.getThreadIdFromUri;
+import static com.android.providers.telephony.RcsProviderUtil.INSERTION_FAILED;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.Bundle;
+import android.telephony.ims.RcsParticipantQueryParams;
+import android.telephony.ims.RcsQueryContinuationToken;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Constants and helpers related to participants for {@link RcsProvider} to keep the code clean.
+ *
+ * @hide
+ */
+class RcsProviderParticipantHelper {
+ private static final int PARTICIPANT_ID_INDEX_IN_URI = 1;
+ private static final int PARTICIPANT_ID_INDEX_IN_THREAD_URI = 3;
+
+ @VisibleForTesting
+ public static void createParticipantTables(SQLiteDatabase db) {
+ Log.d(TAG, "Creating participant tables");
+
+ // create participant tables
+ db.execSQL("CREATE TABLE " + RCS_PARTICIPANT_TABLE + " (" +
+ RCS_PARTICIPANT_ID_COLUMN + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ CANONICAL_ADDRESS_ID_COLUMN + " INTEGER ," +
+ RCS_ALIAS_COLUMN + " TEXT, " +
+ "FOREIGN KEY(" + CANONICAL_ADDRESS_ID_COLUMN + ") "
+ + "REFERENCES canonical_addresses(_id)" +
+ ");");
+
+ db.execSQL("CREATE TABLE " + RCS_PARTICIPANT_THREAD_JUNCTION_TABLE + " (" +
+ RCS_THREAD_ID_COLUMN + " INTEGER, " +
+ RCS_PARTICIPANT_ID_COLUMN + " INTEGER, " +
+ "CONSTRAINT thread_participant PRIMARY KEY("
+ + RCS_THREAD_ID_COLUMN + ", " + RCS_PARTICIPANT_ID_COLUMN + "), " +
+ "FOREIGN KEY(" + RCS_THREAD_ID_COLUMN
+ + ") REFERENCES " + RCS_THREAD_TABLE + "(" + RCS_THREAD_ID_COLUMN + "), " +
+ "FOREIGN KEY(" + RCS_PARTICIPANT_ID_COLUMN
+ + ") REFERENCES " + RCS_PARTICIPANT_TABLE + "(" + RCS_PARTICIPANT_ID_COLUMN + "))");
+
+ // create views
+
+ // The following view joins rcs_participant table with canonical_addresses table to add the
+ // actual address of a participant in the result.
+ db.execSQL("CREATE VIEW " + RCS_PARTICIPANT_WITH_ADDRESS_VIEW + " AS SELECT "
+ + "rcs_participant.rcs_participant_id, rcs_participant.canonical_address_id, "
+ + "rcs_participant.rcs_alias, canonical_addresses.address FROM rcs_participant "
+ + "LEFT JOIN canonical_addresses ON "
+ + "rcs_participant.canonical_address_id=canonical_addresses._id");
+
+ // The following view is the rcs_participant_with_address_view above, plus the information
+ // on which threads this participant contributes to, to enable getting participants of a
+ // thread
+ db.execSQL("CREATE VIEW " + RCS_PARTICIPANT_WITH_THREAD_VIEW + " AS SELECT "
+ + "rcs_participant.rcs_participant_id, rcs_participant.canonical_address_id, "
+ + "rcs_participant.rcs_alias, canonical_addresses.address, rcs_thread_participant"
+ + ".rcs_thread_id FROM rcs_participant LEFT JOIN canonical_addresses ON "
+ + "rcs_participant.canonical_address_id=canonical_addresses._id LEFT JOIN "
+ + "rcs_thread_participant ON rcs_participant.rcs_participant_id="
+ + "rcs_thread_participant.rcs_participant_id");
+
+ // TODO - create indexes for faster querying
+ }
+
+ private final SQLiteOpenHelper mSqLiteOpenHelper;
+
+ RcsProviderParticipantHelper(SQLiteOpenHelper sqLiteOpenHelper) {
+ mSqLiteOpenHelper = sqLiteOpenHelper;
+ }
+
+ Cursor queryParticipant(Bundle bundle) {
+ RcsParticipantQueryParams queryParameters = null;
+ RcsQueryContinuationToken continuationToken = null;
+
+ if (bundle != null) {
+ queryParameters = bundle.getParcelable(PARTICIPANT_QUERY_PARAMETERS_KEY);
+ continuationToken = bundle.getParcelable(QUERY_CONTINUATION_TOKEN);
+ }
+
+ if (continuationToken != null) {
+ return RcsProviderUtil.performContinuationQuery(mSqLiteOpenHelper.getReadableDatabase(),
+ continuationToken);
+ }
+
+ if (queryParameters == null) {
+ queryParameters = new RcsParticipantQueryParams.Builder().build();
+ }
+
+ return performInitialQuery(queryParameters);
+ }
+
+ private Cursor performInitialQuery(RcsParticipantQueryParams queryParameters) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+
+ StringBuilder rawQuery = buildInitialRawQuery(queryParameters);
+ RcsProviderUtil.appendLimit(rawQuery, queryParameters.getLimit());
+ String rawQueryAsString = rawQuery.toString();
+ Cursor cursor = db.rawQuery(rawQueryAsString, null);
+
+ // If the query was paginated, build the next query
+ int limit = queryParameters.getLimit();
+ if (limit > 0) {
+ RcsProviderUtil.createContinuationTokenBundle(cursor,
+ new RcsQueryContinuationToken(PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE,
+ rawQueryAsString, limit, limit), QUERY_CONTINUATION_TOKEN);
+ }
+
+ return cursor;
+ }
+
+ private StringBuilder buildInitialRawQuery(RcsParticipantQueryParams queryParameters) {
+ StringBuilder rawQuery = new StringBuilder("SELECT * FROM ");
+
+ boolean isThreadFiltered = queryParameters.getThreadId() > 0;
+
+ if (isThreadFiltered) {
+ rawQuery.append(RCS_PARTICIPANT_WITH_THREAD_VIEW);
+ } else {
+ rawQuery.append(RCS_PARTICIPANT_WITH_ADDRESS_VIEW);
+ }
+
+ boolean isAliasFiltered = !TextUtils.isEmpty(queryParameters.getAliasLike());
+ boolean isCanonicalAddressFiltered = !TextUtils.isEmpty(
+ queryParameters.getCanonicalAddressLike());
+
+ if (isAliasFiltered || isCanonicalAddressFiltered || isThreadFiltered) {
+ rawQuery.append(" WHERE ");
+ }
+
+ if (isAliasFiltered) {
+ rawQuery.append(RCS_ALIAS_COLUMN).append(" LIKE \"").append(
+ queryParameters.getAliasLike()).append("\"");
+ }
+
+ if (isCanonicalAddressFiltered) {
+ if (isAliasFiltered) {
+ rawQuery.append(" AND ");
+ }
+ rawQuery.append(ADDRESS).append(" LIKE \"").append(
+ queryParameters.getCanonicalAddressLike()).append("\"");
+ }
+
+ if (isThreadFiltered) {
+ if (isAliasFiltered || isCanonicalAddressFiltered) {
+ rawQuery.append(" AND ");
+ }
+ rawQuery.append(RCS_THREAD_ID_COLUMN).append("=").append(queryParameters.getThreadId());
+ }
+
+ rawQuery.append(" ORDER BY ");
+
+ int sortingProperty = queryParameters.getSortingProperty();
+ if (sortingProperty == RcsParticipantQueryParams.SORT_BY_ALIAS) {
+ rawQuery.append(RCS_ALIAS_COLUMN);
+ } else if (sortingProperty == RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS) {
+ rawQuery.append(ADDRESS);
+ } else {
+ rawQuery.append(RCS_PARTICIPANT_ID_COLUMN);
+ }
+ rawQuery.append(queryParameters.getSortDirection() ? " ASC " : " DESC ");
+
+ return rawQuery;
+ }
+
+ Cursor queryParticipantWithId(Uri uri, String[] projection) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+ return db.query(RCS_PARTICIPANT_WITH_ADDRESS_VIEW, projection,
+ getParticipantIdSelection(uri), null, null, null, null);
+ }
+
+ Cursor queryParticipantIn1To1Thread(Uri uri) {
+ String threadId = getThreadIdFromUri(uri);
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+
+ return db.rawQuery(
+ " SELECT * "
+ + "FROM rcs_participant "
+ + "WHERE rcs_participant.rcs_participant_id = ("
+ + " SELECT rcs_thread_participant.rcs_participant_id "
+ + " FROM rcs_thread_participant "
+ + " WHERE rcs_thread_participant.rcs_thread_id=" + threadId + ")", null);
+ }
+
+ Cursor queryParticipantsInGroupThread(Uri uri) {
+ String threadId = getThreadIdFromUri(uri);
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+
+ return db.rawQuery(" SELECT * "
+ + "FROM rcs_participant "
+ + "WHERE rcs_participant.rcs_participant_id = ("
+ + " SELECT rcs_participant_id "
+ + " FROM rcs_thread_participant "
+ + " WHERE rcs_thread_id= " + threadId + ")", null);
+ }
+
+ Cursor queryParticipantInGroupThreadWithId(Uri uri) {
+ String threadId = getThreadIdFromUri(uri);
+ String participantId = getParticipantIdFromUri(uri);
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+
+ return db.rawQuery(" SELECT * "
+ + "FROM rcs_participant "
+ + "WHERE rcs_participant.rcs_participant_id = ("
+ + " SELECT rcs_participant_id "
+ + " FROM rcs_thread_participant "
+ + " WHERE rcs_thread_id=? AND rcs_participant_id=?)",
+ new String[]{threadId, participantId});
+ }
+
+ long insertParticipant(ContentValues contentValues) {
+ if (!contentValues.containsKey(CANONICAL_ADDRESS_ID_COLUMN) || TextUtils.isEmpty(
+ contentValues.getAsString(CANONICAL_ADDRESS_ID_COLUMN))) {
+ Log.e(TAG,
+ "RcsProviderParticipantHelper: Inserting participants without canonical "
+ + "address is not supported");
+ return TRANSACTION_FAILED;
+ }
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ long rowId = db.insert(RCS_PARTICIPANT_TABLE, RCS_PARTICIPANT_ID_COLUMN, contentValues);
+ Log.e(TAG, "Inserted participant with rowId=" + rowId);
+ if (rowId < 0) {
+ return TRANSACTION_FAILED;
+ }
+ return rowId;
+ }
+
+ /**
+ * Inserts a participant into group thread. This function returns the participant ID instead of
+ * the row id in the junction table
+ */
+ long insertParticipantIntoGroupThread(ContentValues values) {
+ if (!values.containsKey(RCS_THREAD_ID_COLUMN) || !values.containsKey(
+ RCS_PARTICIPANT_ID_COLUMN)) {
+ Log.e(TAG, "RcsProviderParticipantHelper: Cannot insert participant into group.");
+ return TRANSACTION_FAILED;
+ }
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ long insertedRowId = db.insert(RCS_PARTICIPANT_THREAD_JUNCTION_TABLE,
+ RCS_PARTICIPANT_ID_COLUMN,
+ values);
+
+ if (insertedRowId == INSERTION_FAILED) {
+ return TRANSACTION_FAILED;
+ }
+
+ return values.getAsLong(RCS_PARTICIPANT_ID_COLUMN);
+ }
+
+ /**
+ * Inserts a participant into group thread. This function returns the participant ID instead of
+ * the row id in the junction table
+ */
+ long insertParticipantIntoGroupThreadWithId(Uri uri) {
+ String threadId = getThreadIdFromUri(uri);
+ String participantId = getParticipantIdFromUri(uri);
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+
+ ContentValues contentValues = new ContentValues(2);
+ contentValues.put(RCS_THREAD_ID_COLUMN, threadId);
+ contentValues.put(RCS_PARTICIPANT_ID_COLUMN, participantId);
+
+ long insertedRowId = db.insert(
+ RCS_PARTICIPANT_THREAD_JUNCTION_TABLE, RCS_PARTICIPANT_ID_COLUMN, contentValues);
+
+ if (insertedRowId == INSERTION_FAILED) {
+ return TRANSACTION_FAILED;
+ }
+
+ return Long.parseLong(participantId);
+ }
+
+ int deleteParticipantWithId(Uri uri) {
+ String participantId = uri.getPathSegments().get(PARTICIPANT_ID_INDEX_IN_URI);
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+
+ // See if this participant is involved in any threads
+ Cursor cursor = db.query(RCS_PARTICIPANT_THREAD_JUNCTION_TABLE, null,
+ RCS_PARTICIPANT_ID_COLUMN + "=?", new String[]{participantId}, null, null, null);
+
+ int participatingThreadCount = 0;
+ if (cursor != null) {
+ participatingThreadCount = cursor.getCount();
+ cursor.close();
+ }
+
+ if (participatingThreadCount > 0) {
+ Log.e(TAG,
+ "RcsProviderParticipantHelper: Can't delete participant while it is still in "
+ + "RCS threads, uri:"
+ + uri);
+ return 0;
+ }
+
+ return db.delete(RCS_PARTICIPANT_TABLE, RCS_PARTICIPANT_ID_COLUMN + "=?",
+ new String[]{participantId});
+ }
+
+ int deleteParticipantFromGroupThread(Uri uri) {
+ String threadId = getThreadIdFromUri(uri);
+ String participantId = getParticipantIdFromUri(uri);
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ // TODO check to remove owner
+ return db.delete(RCS_PARTICIPANT_THREAD_JUNCTION_TABLE,
+ RCS_THREAD_ID_COLUMN + "=? AND " + RCS_PARTICIPANT_ID_COLUMN + "=?",
+ new String[]{threadId, participantId});
+ }
+
+ int updateParticipant(ContentValues contentValues, String selection, String[] selectionArgs) {
+ if (contentValues.containsKey(RCS_PARTICIPANT_ID_COLUMN)) {
+ Log.e(TAG, "RcsProviderParticipantHelper: Updating participant id is not supported");
+ return 0;
+ }
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.update(RCS_PARTICIPANT_TABLE, contentValues, selection, selectionArgs);
+ }
+
+ int updateParticipantWithId(ContentValues contentValues, Uri uri) {
+ return updateParticipant(contentValues, getParticipantIdSelection(uri), null);
+ }
+
+ private String getParticipantIdSelection(Uri uri) {
+ return RCS_PARTICIPANT_ID_COLUMN + "=" + uri.getPathSegments().get(
+ PARTICIPANT_ID_INDEX_IN_URI);
+ }
+
+ Uri getParticipantInThreadUri(ContentValues values, long rowId) {
+ if (values == null) {
+ return null;
+ }
+ Integer threadId = values.getAsInteger(RCS_THREAD_ID_COLUMN);
+ if (threadId == null) {
+ return null;
+ }
+
+ return GROUP_THREAD_URI_PREFIX.buildUpon().appendPath(
+ Integer.toString(threadId)).appendPath(RCS_PARTICIPANT_URI_PART).appendPath(
+ Long.toString(rowId)).build();
+ }
+
+ private String getParticipantIdFromUri(Uri uri) {
+ return uri.getPathSegments().get(PARTICIPANT_ID_INDEX_IN_THREAD_URI);
+ }
+}
diff --git a/src/com/android/providers/telephony/RcsProviderThreadHelper.java b/src/com/android/providers/telephony/RcsProviderThreadHelper.java
new file mode 100644
index 0000000..c1a35ef
--- /dev/null
+++ b/src/com/android/providers/telephony/RcsProviderThreadHelper.java
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2018 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.providers.telephony;
+
+import static android.provider.Telephony.RcsColumns.Rcs1To1ThreadColumns.FALLBACK_THREAD_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.CONFERENCE_URI_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.GROUP_ICON_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.GROUP_NAME_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.OWNER_PARTICIPANT_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.MESSAGE_TEXT_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.ORIGINATION_TIMESTAMP_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.STATUS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadColumns.RCS_THREAD_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_COLUMN;
+import static android.provider.Telephony.RcsColumns.TRANSACTION_FAILED;
+import static android.telephony.ims.RcsQueryContinuationToken.QUERY_CONTINUATION_TOKEN;
+import static android.telephony.ims.RcsQueryContinuationToken.THREAD_QUERY_CONTINUATION_TOKEN_TYPE;
+import static android.telephony.ims.RcsThreadQueryParams.THREAD_QUERY_PARAMETERS_KEY;
+
+import static com.android.providers.telephony.RcsProvider.RCS_1_TO_1_THREAD_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_GROUP_THREAD_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_MESSAGE_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_THREAD_JUNCTION_TABLE;
+import static com.android.providers.telephony.RcsProvider.RCS_THREAD_TABLE;
+import static com.android.providers.telephony.RcsProvider.TAG;
+import static com.android.providers.telephony.RcsProvider.UNIFIED_RCS_THREAD_VIEW;
+import static com.android.providers.telephony.RcsProviderUtil.INSERTION_FAILED;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.BaseColumns;
+import android.telephony.ims.RcsQueryContinuationToken;
+import android.telephony.ims.RcsThreadQueryParams;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Constants and helpers related to threads for {@link RcsProvider} to keep the code clean.
+ *
+ * @hide
+ */
+class RcsProviderThreadHelper {
+ private static final int THREAD_ID_INDEX_IN_URI = 1;
+
+ @VisibleForTesting
+ public static void createThreadTables(SQLiteDatabase db) {
+ Log.d(TAG, "Creating thread tables");
+
+ // Add the thread tables
+ db.execSQL("CREATE TABLE " + RCS_THREAD_TABLE + " (" +
+ RCS_THREAD_ID_COLUMN + " INTEGER PRIMARY KEY AUTOINCREMENT);");
+
+ db.execSQL("CREATE TABLE " + RCS_1_TO_1_THREAD_TABLE + " (" +
+ RCS_THREAD_ID_COLUMN + " INTEGER PRIMARY KEY, " +
+ FALLBACK_THREAD_ID_COLUMN + " INTEGER, " +
+ "FOREIGN KEY(" + RCS_THREAD_ID_COLUMN
+ + ") REFERENCES " + RCS_THREAD_TABLE + "(" + RCS_THREAD_ID_COLUMN + ")," +
+ "FOREIGN KEY(" + FALLBACK_THREAD_ID_COLUMN
+ + ") REFERENCES threads( " + BaseColumns._ID + "))");
+
+ db.execSQL("CREATE TABLE " + RCS_GROUP_THREAD_TABLE + " (" +
+ RCS_THREAD_ID_COLUMN + " INTEGER PRIMARY KEY, " +
+ OWNER_PARTICIPANT_COLUMN + " INTEGER, " +
+ GROUP_NAME_COLUMN + " TEXT, " +
+ GROUP_ICON_COLUMN + " TEXT, " +
+ CONFERENCE_URI_COLUMN + " TEXT, " +
+ "FOREIGN KEY(" + RCS_THREAD_ID_COLUMN
+ + ") REFERENCES " + RCS_THREAD_TABLE + "(" + RCS_THREAD_ID_COLUMN + "))");
+
+ // Add the views
+
+ // The following is a unified thread view. Since SQLite does not support right or full
+ // joins, we are using a union with null values for unused variables for each thread type.
+ // The thread_type column is an easy way to figure out whether the entry came from a 1 to 1
+ // thread or a group thread. The last message in each thread is appended to the table
+ // entries to figure out the latest threads and snippet text. We use COALESCE so that MAX()
+ // can take null values into account in order to have threads with no messages still
+ // represented here
+ //
+ // SELECT <1 to 1 thread and first message>
+ // FROM (
+ // SELECT *
+ // FROM rcs_1_to_1_thread LEFT JOIN rcs_message
+ // ON rcs_1_to_1_thread.rcs_thread_id=rcs_message.rcs_thread_id)
+ // GROUP BY rcs_thread_id
+ // HAVING MAX(COALESCE(origination_timestamp,1))
+ //
+ // UNION
+ // SELECT <group thread and first message>
+ // FROM (
+ // SELECT *
+ // FROM rcs_group_thread LEFT JOIN rcs_message
+ // ON rcs_group_thread.rcs_thread_id=rcs_message.rcs_thread_id)
+ // GROUP BY rcs_thread_id
+ // HAVING MAX(COALESCE(origination_timestamp,1))
+
+ db.execSQL("CREATE VIEW " + UNIFIED_RCS_THREAD_VIEW + " AS "
+ + "SELECT "
+ + RCS_THREAD_ID_COLUMN + ", "
+ + FALLBACK_THREAD_ID_COLUMN + ", "
+ + "null AS " + OWNER_PARTICIPANT_COLUMN + ", "
+ + "null AS " + GROUP_NAME_COLUMN + ", "
+ + "null AS " + GROUP_ICON_COLUMN + ", "
+ + "null AS " + CONFERENCE_URI_COLUMN + ", "
+ + "0 AS " + THREAD_TYPE_COLUMN + ", "
+ + ORIGINATION_TIMESTAMP_COLUMN + ", "
+ + MESSAGE_TEXT_COLUMN + ", "
+ + STATUS_COLUMN
+ + " FROM (SELECT * FROM "
+ + RCS_1_TO_1_THREAD_TABLE + " LEFT JOIN " + RCS_MESSAGE_TABLE
+ + " ON "
+ + RCS_1_TO_1_THREAD_TABLE + "." + RCS_THREAD_ID_COLUMN + "="
+ + RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ")"
+ + " GROUP BY " + RCS_THREAD_ID_COLUMN
+ + " HAVING MAX(COALESCE("
+ + ORIGINATION_TIMESTAMP_COLUMN + ", 1))"
+ + " UNION SELECT "
+ + RCS_THREAD_ID_COLUMN + ", "
+ + "null AS " + FALLBACK_THREAD_ID_COLUMN + ", "
+ + OWNER_PARTICIPANT_COLUMN + ", "
+ + GROUP_NAME_COLUMN + ", "
+ + GROUP_ICON_COLUMN + ", "
+ + CONFERENCE_URI_COLUMN + ", "
+ + "1 AS " + THREAD_TYPE_COLUMN + ", "
+ + ORIGINATION_TIMESTAMP_COLUMN + ", "
+ + MESSAGE_TEXT_COLUMN + ", "
+ + STATUS_COLUMN
+ + " FROM (SELECT * FROM "
+ + RCS_GROUP_THREAD_TABLE + " LEFT JOIN " + RCS_MESSAGE_TABLE
+ + " ON "
+ + RCS_GROUP_THREAD_TABLE + "." + RCS_THREAD_ID_COLUMN + "="
+ + RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ")"
+ + " GROUP BY " + RCS_THREAD_ID_COLUMN
+ + " HAVING MAX(COALESCE("
+ + ORIGINATION_TIMESTAMP_COLUMN + ", 1))");
+
+ // Add the triggers
+
+ // Delete the corresponding rcs_thread row upon deleting a row in rcs_1_to_1_thread
+ //
+ // CREATE TRIGGER deleteRcsThreadAfter1to1
+ // AFTER DELETE ON rcs_1_to_1_thread
+ // BEGIN
+ // DELETE FROM rcs_thread WHERE rcs_thread._id=OLD.rcs_thread_id;
+ // END
+ db.execSQL("CREATE TRIGGER deleteRcsThreadAfter1to1 AFTER DELETE ON "
+ + RCS_1_TO_1_THREAD_TABLE + " BEGIN DELETE FROM " + RCS_THREAD_TABLE + " WHERE "
+ + RCS_THREAD_TABLE + "." + RCS_THREAD_ID_COLUMN + "=OLD." + RCS_THREAD_ID_COLUMN
+ + "; END");
+
+ // Delete the corresponding rcs_thread row upon deleting a row in rcs_group_thread
+ //
+ // CREATE TRIGGER deleteRcsThreadAfter1to1
+ // AFTER DELETE ON rcs_1_to_1_thread
+ // BEGIN
+ // DELETE FROM rcs_thread WHERE rcs_thread._id=OLD.rcs_thread_id;
+ // END
+ db.execSQL("CREATE TRIGGER deleteRcsThreadAfterGroup AFTER DELETE ON "
+ + RCS_GROUP_THREAD_TABLE + " BEGIN DELETE FROM " + RCS_THREAD_TABLE + " WHERE "
+ + RCS_THREAD_TABLE + "." + RCS_THREAD_ID_COLUMN + "=OLD." + RCS_THREAD_ID_COLUMN
+ + "; END");
+
+ // Delete the junction table entries upon deleting a 1 to 1 thread
+ //
+ // CREATE TRIGGER delete1To1JunctionEntries
+ // AFTER
+ // DELETE ON rcs_1_to_1_thread
+ // BEGIN
+ // DELETE FROM
+ // rcs_thread_participant
+ // WHERE
+ // rcs_thread_participant.rcs_thread_id = OLD.rcs_thread_id;
+ // END
+ db.execSQL("CREATE TRIGGER delete1To1JunctionEntries AFTER DELETE ON "
+ + RCS_1_TO_1_THREAD_TABLE + " BEGIN DELETE FROM "
+ + RCS_PARTICIPANT_THREAD_JUNCTION_TABLE + " WHERE "
+ + RCS_PARTICIPANT_THREAD_JUNCTION_TABLE + "." + RCS_THREAD_ID_COLUMN + "=OLD."
+ + RCS_THREAD_ID_COLUMN + "; END");
+
+ // Delete the junction table entries upon deleting a group thread
+ //
+ // CREATE TRIGGER delete1To1JunctionEntries
+ // AFTER
+ // DELETE ON rcs_1_to_1_thread
+ // BEGIN
+ // DELETE FROM
+ // rcs_thread_participant
+ // WHERE
+ // rcs_thread_participant.rcs_thread_id = OLD.rcs_thread_id;
+ // END
+ db.execSQL("CREATE TRIGGER deleteGroupJunctionEntries AFTER DELETE ON "
+ + RCS_GROUP_THREAD_TABLE + " BEGIN DELETE FROM "
+ + RCS_PARTICIPANT_THREAD_JUNCTION_TABLE + " WHERE "
+ + RCS_PARTICIPANT_THREAD_JUNCTION_TABLE + "." + RCS_THREAD_ID_COLUMN + "=OLD."
+ +RCS_THREAD_ID_COLUMN + "; END");
+
+ // TODO - delete all messages in a thread after deleting a thread
+
+ // TODO - create indexes for faster querying
+ }
+
+ private final SQLiteOpenHelper mSqLiteOpenHelper;
+
+ RcsProviderThreadHelper(SQLiteOpenHelper sqLiteOpenHelper) {
+ mSqLiteOpenHelper = sqLiteOpenHelper;
+ }
+
+ Cursor queryUnifiedThread(Bundle bundle) {
+ RcsThreadQueryParams queryParameters = null;
+ RcsQueryContinuationToken continuationToken = null;
+ if (bundle != null) {
+ queryParameters = bundle.getParcelable(
+ THREAD_QUERY_PARAMETERS_KEY);
+ continuationToken = bundle.getParcelable(QUERY_CONTINUATION_TOKEN);
+ }
+
+ if (continuationToken != null) {
+ return RcsProviderUtil.performContinuationQuery(mSqLiteOpenHelper.getReadableDatabase(),
+ continuationToken);
+ }
+
+ if (queryParameters == null) {
+ queryParameters = new RcsThreadQueryParams.Builder().build();
+ }
+
+ return performInitialQuery(queryParameters);
+ }
+
+ private Cursor performInitialQuery(RcsThreadQueryParams queryParameters) {
+ if (queryParameters == null) {
+ // return everything for test purposes
+ queryParameters = new RcsThreadQueryParams.Builder().build();
+ }
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+ StringBuilder rawQuery = new StringBuilder("SELECT * FROM ").append(
+ UNIFIED_RCS_THREAD_VIEW);
+
+ if (queryParameters.getThreadType() == RcsThreadQueryParams.THREAD_TYPE_1_TO_1) {
+ rawQuery.append(" WHERE ").append(THREAD_TYPE_COLUMN).append("=0");
+ } else if (queryParameters.getThreadType() == RcsThreadQueryParams.THREAD_TYPE_GROUP) {
+ rawQuery.append(" WHERE ").append(THREAD_TYPE_COLUMN).append("=1");
+ }
+
+ rawQuery.append(" ORDER BY ");
+
+ if (queryParameters.getSortingProperty() == RcsThreadQueryParams.SORT_BY_TIMESTAMP) {
+ rawQuery.append(ORIGINATION_TIMESTAMP_COLUMN);
+ } else {
+ rawQuery.append(RCS_THREAD_ID_COLUMN);
+ }
+
+ rawQuery.append(queryParameters.getSortDirection() ? " ASC " : " DESC ");
+ RcsProviderUtil.appendLimit(rawQuery, queryParameters.getLimit());
+
+ String rawQueryAsString = rawQuery.toString();
+ Cursor cursor = db.rawQuery(rawQueryAsString, null);
+
+ // If this is a paginated query, build the next query and return as a Cursor extra. Only do
+ // this if the current query returned a result.
+ int limit = queryParameters.getLimit();
+ if (limit > 0) {
+ RcsProviderUtil.createContinuationTokenBundle(cursor,
+ new RcsQueryContinuationToken(THREAD_QUERY_CONTINUATION_TOKEN_TYPE,
+ rawQueryAsString, limit, limit), QUERY_CONTINUATION_TOKEN);
+ }
+
+ return cursor;
+ }
+
+ Cursor queryUnifiedThreadUsingId(Uri uri, String[] projection) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+ String threadId = getThreadIdFromUri(uri);
+
+ return db.query(UNIFIED_RCS_THREAD_VIEW, projection, RCS_THREAD_ID_COLUMN + "=?",
+ new String[]{threadId},
+ null, null, null);
+ }
+
+ Cursor query1to1Thread(String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+ return db.query(RCS_1_TO_1_THREAD_TABLE, projection, selection, selectionArgs, null,
+ null, sortOrder);
+ }
+
+ Cursor query1To1ThreadUsingId(Uri uri, String[] projection) {
+ return query1to1Thread(projection, getThreadIdSelection(uri), null, null);
+ }
+
+ Cursor queryGroupThread(String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
+ return db.query(RCS_GROUP_THREAD_TABLE, projection, selection, selectionArgs, null,
+ null, sortOrder);
+ }
+
+ Cursor queryGroupThreadUsingId(Uri uri, String[] projection) {
+ return queryGroupThread(projection, getThreadIdSelection(uri), null, null);
+ }
+
+ /**
+ * @param contentValues should contain the participant ID of the other participant under key
+ * {@link RCS_PARTICIPANT_ID_COLUMN}
+ */
+ long insert1To1Thread(ContentValues contentValues) {
+ if (contentValues.containsKey(RCS_THREAD_ID_COLUMN)) {
+ Log.e(RcsProvider.TAG,
+ "RcsProviderThreadHelper: inserting threads with IDs is not supported");
+ return TRANSACTION_FAILED;
+ }
+
+ Long participantId = contentValues.getAsLong(RCS_PARTICIPANT_ID_COLUMN);
+ if (participantId == null) {
+ Log.e(RcsProvider.TAG,
+ "inserting threads without participant IDs is not supported");
+ return TRANSACTION_FAILED;
+ }
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ try {
+ db.beginTransaction();
+
+ if (hasExisting1To1ThreadForParticipant(db, participantId)) {
+ return TRANSACTION_FAILED;
+ }
+
+ long threadId = insertIntoCommonRcsThreads(db);
+ if (threadId == -1) {
+ return TRANSACTION_FAILED;
+ }
+
+ if (insertP2pThread(db, threadId) == -1) {
+ return TRANSACTION_FAILED;
+ }
+
+ if (insertParticipantIntoP2pThread(db, threadId, participantId) == -1) {
+ return TRANSACTION_FAILED;
+ }
+
+ db.setTransactionSuccessful();
+
+ return threadId;
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ private long insertP2pThread(SQLiteDatabase db, long threadId) {
+ ContentValues contentValues = new ContentValues(1);
+ contentValues.put(RCS_THREAD_ID_COLUMN, threadId);
+
+ return db.insert(RCS_1_TO_1_THREAD_TABLE, RCS_THREAD_ID_COLUMN, contentValues);
+ }
+
+ private long insertParticipantIntoP2pThread(
+ SQLiteDatabase db, long threadId, long participantId) {
+ ContentValues contentValues = new ContentValues(2);
+ contentValues.put(RCS_THREAD_ID_COLUMN, threadId);
+ contentValues.put(RCS_PARTICIPANT_ID_COLUMN, participantId);
+
+ return db.insert(
+ RCS_PARTICIPANT_THREAD_JUNCTION_TABLE, RCS_PARTICIPANT_ID_COLUMN, contentValues);
+ }
+
+ private boolean hasExisting1To1ThreadForParticipant(SQLiteDatabase db, long participantId) {
+ String table = joinOnColumn(
+ RCS_PARTICIPANT_THREAD_JUNCTION_TABLE,
+ RCS_1_TO_1_THREAD_TABLE,
+ RCS_THREAD_ID_COLUMN);
+
+ try (Cursor cursor = db.query(
+ table,
+ null,
+ RCS_PARTICIPANT_ID_COLUMN + "=?",
+ new String[]{Long.toString(participantId)},
+ null,
+ null,
+ null)) {
+ if (cursor == null || cursor.getCount() == 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private String joinOnColumn(String t1, String t2, String col) {
+ return t1 + " JOIN " + t2 + " ON (" + t1 + "." + col + "=" + t2 + "." + col + ")";
+ }
+
+ long insertGroupThread(ContentValues contentValues) {
+ long returnValue = TRANSACTION_FAILED;
+ if (contentValues.containsKey(RCS_THREAD_ID_COLUMN)) {
+ Log.e(RcsProvider.TAG,
+ "RcsProviderThreadHelper: inserting threads with IDs is not supported");
+ return returnValue;
+ }
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ try {
+ db.beginTransaction();
+
+ // Insert into the common rcs_threads table
+ long rowId = insertIntoCommonRcsThreads(db);
+ if (rowId == INSERTION_FAILED) {
+ return returnValue;
+ }
+
+ // Add the rowId in rcs_threads table as a foreign key in rcs_group_table
+ contentValues.put(RCS_THREAD_ID_COLUMN, rowId);
+ db.insert(RCS_GROUP_THREAD_TABLE, RCS_THREAD_ID_COLUMN, contentValues);
+ contentValues.remove(RCS_THREAD_ID_COLUMN);
+
+ db.setTransactionSuccessful();
+ returnValue = rowId;
+ } finally {
+ db.endTransaction();
+ }
+ return returnValue;
+ }
+
+ private long insertIntoCommonRcsThreads(SQLiteDatabase db) {
+ return db.insert(RCS_THREAD_TABLE, RCS_THREAD_ID_COLUMN, new ContentValues());
+ }
+
+ int delete1To1Thread(String selection, String[] selectionArgs) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.delete(RCS_1_TO_1_THREAD_TABLE, selection, selectionArgs);
+ }
+
+ int delete1To1ThreadWithId(Uri uri) {
+ return delete1To1Thread(getThreadIdSelection(uri), null);
+ }
+
+ int deleteGroupThread(String selection, String[] selectionArgs) {
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.delete(RCS_GROUP_THREAD_TABLE, selection, selectionArgs);
+ }
+
+ int deleteGroupThreadWithId(Uri uri) {
+ return deleteGroupThread(getThreadIdSelection(uri), null);
+ }
+
+ int update1To1Thread(ContentValues values, String selection, String[] selectionArgs) {
+ if (values.containsKey(RCS_THREAD_ID_COLUMN)) {
+ Log.e(TAG,
+ "RcsProviderThreadHelper: updating thread id for 1 to 1 threads is not "
+ + "allowed");
+ return 0;
+ }
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.update(RCS_1_TO_1_THREAD_TABLE, values, selection, selectionArgs);
+ }
+
+ int update1To1ThreadWithId(ContentValues values, Uri uri) {
+ return update1To1Thread(values, getThreadIdSelection(uri), null);
+ }
+
+ int updateGroupThread(ContentValues values, String selection, String[] selectionArgs) {
+ if (values.containsKey(RCS_THREAD_ID_COLUMN)) {
+ Log.e(TAG,
+ "RcsProviderThreadHelper: updating thread id for group threads is not "
+ + "allowed");
+ return 0;
+ }
+
+ SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
+ return db.update(RCS_GROUP_THREAD_TABLE, values, selection, selectionArgs);
+ }
+
+ int updateGroupThreadWithId(ContentValues values, Uri uri) {
+ return updateGroupThread(values, getThreadIdSelection(uri), null);
+ }
+
+ private String getThreadIdSelection(Uri uri) {
+ return RCS_THREAD_ID_COLUMN + "=" + getThreadIdFromUri(uri);
+ }
+
+ static String getThreadIdFromUri(Uri uri) {
+ return uri.getPathSegments().get(THREAD_ID_INDEX_IN_URI);
+ }
+}
diff --git a/src/com/android/providers/telephony/RcsProviderUtil.java b/src/com/android/providers/telephony/RcsProviderUtil.java
new file mode 100644
index 0000000..38f7be1
--- /dev/null
+++ b/src/com/android/providers/telephony/RcsProviderUtil.java
@@ -0,0 +1,95 @@
+/*
+ * 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.providers.telephony;
+
+import static android.provider.Telephony.RcsColumns.TRANSACTION_FAILED;
+
+import static android.telephony.ims.RcsQueryContinuationToken.QUERY_CONTINUATION_TOKEN;
+import static com.android.providers.telephony.RcsProvider.TAG;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.telephony.ims.RcsQueryContinuationToken;
+import android.text.TextUtils;
+import android.util.Log;
+
+class RcsProviderUtil {
+ /**
+ * The value returned when database insertion fails,
+ * @see SQLiteDatabase#insert(String, String, ContentValues)
+ */
+ static final int INSERTION_FAILED = -1;
+
+ static void appendLimit(StringBuilder stringBuilder, int limit) {
+ if (limit > 0) {
+ stringBuilder.append(" LIMIT ").append(limit);
+ }
+ }
+
+ static void createContinuationTokenBundle(Cursor cursor, Parcelable continuationToken,
+ String tokenKey) {
+ if (cursor.getCount() > 0) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(tokenKey, continuationToken);
+ cursor.setExtras(bundle);
+ }
+ }
+
+ /**
+ * This method gets a token with raw query, performs the query, increments the token's offset
+ * and returns it as a cursor extra
+ */
+ static Cursor performContinuationQuery(SQLiteDatabase db,
+ RcsQueryContinuationToken continuationToken) {
+ String rawQuery = continuationToken.getRawQuery();
+ int offset = continuationToken.getOffset();
+
+ if (offset <= 0 || TextUtils.isEmpty(rawQuery)) {
+ Log.e(TAG, "RcsProviderUtil: Invalid continuation token");
+ return null;
+ }
+
+ String continuationQuery = rawQuery + " OFFSET " + offset;
+
+ Cursor cursor = db.rawQuery(continuationQuery, null);
+ if (cursor.getCount() > 0) {
+ continuationToken.incrementOffset();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(QUERY_CONTINUATION_TOKEN, continuationToken);
+ cursor.setExtras(bundle);
+ }
+
+ return cursor;
+ }
+
+ static Uri buildUriWithRowIdAppended(Uri prefix, long rowId) {
+ if (rowId == TRANSACTION_FAILED) {
+ return null;
+ }
+ return Uri.withAppendedPath(prefix, Long.toString(rowId));
+ }
+
+ static Uri returnUriAsIsIfSuccessful(Uri uri, long rowId) {
+ if (rowId == TRANSACTION_FAILED) {
+ return null;
+ }
+ return uri;
+ }
+}
diff --git a/src/com/android/providers/telephony/ServiceStateProvider.java b/src/com/android/providers/telephony/ServiceStateProvider.java
index e589171..7f870b0 100644
--- a/src/com/android/providers/telephony/ServiceStateProvider.java
+++ b/src/com/android/providers/telephony/ServiceStateProvider.java
@@ -61,7 +61,6 @@
import static android.provider.Telephony.ServiceStateTable.CDMA_ERI_ICON_INDEX;
import static android.provider.Telephony.ServiceStateTable.CDMA_ERI_ICON_MODE;
import static android.provider.Telephony.ServiceStateTable.IS_EMERGENCY_ONLY;
-import static android.provider.Telephony.ServiceStateTable.IS_DATA_ROAMING_FROM_REGISTRATION;
import static android.provider.Telephony.ServiceStateTable.IS_USING_CARRIER_AGGREGATION;
@@ -94,7 +93,6 @@
CDMA_ERI_ICON_INDEX,
CDMA_ERI_ICON_MODE,
IS_EMERGENCY_ONLY,
- IS_DATA_ROAMING_FROM_REGISTRATION,
IS_USING_CARRIER_AGGREGATION,
};
@@ -153,8 +151,6 @@
newSS.setCdmaEriIconIndex(values.getAsInteger(CDMA_ERI_ICON_INDEX));
newSS.setCdmaEriIconMode(values.getAsInteger(CDMA_ERI_ICON_MODE));
newSS.setEmergencyOnly(values.getAsBoolean(IS_EMERGENCY_ONLY));
- newSS.setDataRoamingFromRegistration(
- values.getAsBoolean(IS_DATA_ROAMING_FROM_REGISTRATION));
newSS.setIsUsingCarrierAggregation(values.getAsBoolean(IS_USING_CARRIER_AGGREGATION));
// notify listeners
@@ -235,8 +231,6 @@
final int cdma_eri_icon_index = ss.getCdmaEriIconIndex();
final int cdma_eri_icon_mode = ss.getCdmaEriIconMode();
final int is_emergency_only = (ss.isEmergencyOnly()) ? 1 : 0;
- final int is_data_roaming_from_registration =
- (ss.getDataRoamingFromRegistration()) ? 1 : 0;
final int is_using_carrier_aggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
return buildSingleRowResult(projection, sColumns, new Object[] {
@@ -261,7 +255,6 @@
cdma_eri_icon_index,
cdma_eri_icon_mode,
is_emergency_only,
- is_data_roaming_from_registration,
is_using_carrier_aggregation,
});
}
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index 2b40d7e..95db869 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -464,7 +464,6 @@
int match = sURLMatcher.match(url);
String table = TABLE_SMS;
- boolean notifyIfNotDefault = true;
switch (match) {
case SMS_ALL:
@@ -503,10 +502,6 @@
case SMS_RAW_MESSAGE:
table = "raw";
- // The raw table is used by the telephony layer for storing an sms before
- // sending out a notification that an sms has arrived. We don't want to notify
- // the default sms app of changes to this table.
- notifyIfNotDefault = false;
break;
case SMS_STATUS_PENDING:
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index df1e58c..0b125bb 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -26,28 +26,29 @@
import static android.provider.Telephony.Carriers.CARRIER_DELETED_BUT_PRESENT_IN_XML;
import static android.provider.Telephony.Carriers.CARRIER_EDITED;
import static android.provider.Telephony.Carriers.CARRIER_ENABLED;
+import static android.provider.Telephony.Carriers.CARRIER_ID;
import static android.provider.Telephony.Carriers.CONTENT_URI;
import static android.provider.Telephony.Carriers.CURRENT;
import static android.provider.Telephony.Carriers.DEFAULT_SORT_ORDER;
-import static android.provider.Telephony.Carriers.EDITED;
-import static android.provider.Telephony.Carriers.MAX_CONNS;
-import static android.provider.Telephony.Carriers.MAX_CONNS_TIME;
+import static android.provider.Telephony.Carriers.EDITED_STATUS;
+import static android.provider.Telephony.Carriers.MAX_CONNECTIONS;
+import static android.provider.Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS;
import static android.provider.Telephony.Carriers.MCC;
import static android.provider.Telephony.Carriers.MMSC;
import static android.provider.Telephony.Carriers.MMSPORT;
import static android.provider.Telephony.Carriers.MMSPROXY;
import static android.provider.Telephony.Carriers.MNC;
-import static android.provider.Telephony.Carriers.MODEM_COGNITIVE;
+import static android.provider.Telephony.Carriers.MODEM_PERSIST;
import static android.provider.Telephony.Carriers.MTU;
import static android.provider.Telephony.Carriers.MVNO_MATCH_DATA;
import static android.provider.Telephony.Carriers.MVNO_TYPE;
import static android.provider.Telephony.Carriers.NAME;
import static android.provider.Telephony.Carriers.NETWORK_TYPE_BITMASK;
-import static android.provider.Telephony.Carriers.NO_SET_SET;
+import static android.provider.Telephony.Carriers.NO_APN_SET_ID;
import static android.provider.Telephony.Carriers.NUMERIC;
import static android.provider.Telephony.Carriers.OWNED_BY;
-import static android.provider.Telephony.Carriers.OWNED_BY_OTHERS;
import static android.provider.Telephony.Carriers.OWNED_BY_DPC;
+import static android.provider.Telephony.Carriers.OWNED_BY_OTHERS;
import static android.provider.Telephony.Carriers.PASSWORD;
import static android.provider.Telephony.Carriers.PORT;
import static android.provider.Telephony.Carriers.PROFILE_ID;
@@ -55,6 +56,8 @@
import static android.provider.Telephony.Carriers.PROXY;
import static android.provider.Telephony.Carriers.ROAMING_PROTOCOL;
import static android.provider.Telephony.Carriers.SERVER;
+import static android.provider.Telephony.Carriers.SKIP_464XLAT;
+import static android.provider.Telephony.Carriers.SKIP_464XLAT_DEFAULT;
import static android.provider.Telephony.Carriers.SUBSCRIPTION_ID;
import static android.provider.Telephony.Carriers.TYPE;
import static android.provider.Telephony.Carriers.UNEDITED;
@@ -64,11 +67,12 @@
import static android.provider.Telephony.Carriers.USER_EDITABLE;
import static android.provider.Telephony.Carriers.USER_EDITED;
import static android.provider.Telephony.Carriers.USER_VISIBLE;
-import static android.provider.Telephony.Carriers.WAIT_TIME;
+import static android.provider.Telephony.Carriers.WAIT_TIME_RETRY;
import static android.provider.Telephony.Carriers._ID;
import android.content.ComponentName;
import android.content.ContentProvider;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@@ -95,10 +99,12 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.Telephony;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -108,7 +114,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IApnSourceService;
import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.dataconnection.ApnSetting;
+import com.android.internal.telephony.dataconnection.ApnSettingUtils;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.util.XmlUtils;
@@ -116,15 +122,21 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Set;
+import java.util.zip.CRC32;
public class TelephonyProvider extends ContentProvider
{
@@ -133,7 +145,7 @@
private static final boolean DBG = true;
private static final boolean VDBG = false; // STOPSHIP if true
- private static final int DATABASE_VERSION = 26 << 16;
+ private static final int DATABASE_VERSION = 36 << 16;
private static final int URL_UNKNOWN = 0;
private static final int URL_TELEPHONY = 1;
private static final int URL_CURRENT = 2;
@@ -157,7 +169,11 @@
private static final int URL_ENFORCE_MANAGED = 20;
private static final int URL_PREFERAPNSET = 21;
private static final int URL_PREFERAPNSET_USING_SUBID = 22;
-
+ private static final int URL_SIM_APN_LIST = 23;
+ private static final int URL_SIM_APN_LIST_ID = 24;
+ private static final int URL_FILTERED_USING_SUBID = 25;
+ private static final int URL_SIM_APN_LIST_FILTERED = 26;
+ private static final int URL_SIM_APN_LIST_FILTERED_ID = 27;
private static final String TAG = "TelephonyProvider";
private static final String CARRIERS_TABLE = "carriers";
@@ -194,24 +210,24 @@
private static final ContentValues s_currentNullMap;
private static final ContentValues s_currentSetMap;
- private static final String IS_UNEDITED = EDITED + "=" + UNEDITED;
- private static final String IS_EDITED = EDITED + "!=" + UNEDITED;
- private static final String IS_USER_EDITED = EDITED + "=" + USER_EDITED;
- private static final String IS_NOT_USER_EDITED = EDITED + "!=" + USER_EDITED;
- private static final String IS_USER_DELETED = EDITED + "=" + USER_DELETED;
- private static final String IS_NOT_USER_DELETED = EDITED + "!=" + USER_DELETED;
+ private static final String IS_UNEDITED = EDITED_STATUS + "=" + UNEDITED;
+ private static final String IS_EDITED = EDITED_STATUS + "!=" + UNEDITED;
+ private static final String IS_USER_EDITED = EDITED_STATUS + "=" + USER_EDITED;
+ private static final String IS_NOT_USER_EDITED = EDITED_STATUS + "!=" + USER_EDITED;
+ private static final String IS_USER_DELETED = EDITED_STATUS + "=" + USER_DELETED;
+ private static final String IS_NOT_USER_DELETED = EDITED_STATUS + "!=" + USER_DELETED;
private static final String IS_USER_DELETED_BUT_PRESENT_IN_XML =
- EDITED + "=" + USER_DELETED_BUT_PRESENT_IN_XML;
+ EDITED_STATUS + "=" + USER_DELETED_BUT_PRESENT_IN_XML;
private static final String IS_NOT_USER_DELETED_BUT_PRESENT_IN_XML =
- EDITED + "!=" + USER_DELETED_BUT_PRESENT_IN_XML;
- private static final String IS_CARRIER_EDITED = EDITED + "=" + CARRIER_EDITED;
- private static final String IS_NOT_CARRIER_EDITED = EDITED + "!=" + CARRIER_EDITED;
- private static final String IS_CARRIER_DELETED = EDITED + "=" + CARRIER_DELETED;
- private static final String IS_NOT_CARRIER_DELETED = EDITED + "!=" + CARRIER_DELETED;
+ EDITED_STATUS + "!=" + USER_DELETED_BUT_PRESENT_IN_XML;
+ private static final String IS_CARRIER_EDITED = EDITED_STATUS + "=" + CARRIER_EDITED;
+ private static final String IS_NOT_CARRIER_EDITED = EDITED_STATUS + "!=" + CARRIER_EDITED;
+ private static final String IS_CARRIER_DELETED = EDITED_STATUS + "=" + CARRIER_DELETED;
+ private static final String IS_NOT_CARRIER_DELETED = EDITED_STATUS + "!=" + CARRIER_DELETED;
private static final String IS_CARRIER_DELETED_BUT_PRESENT_IN_XML =
- EDITED + "=" + CARRIER_DELETED_BUT_PRESENT_IN_XML;
+ EDITED_STATUS + "=" + CARRIER_DELETED_BUT_PRESENT_IN_XML;
private static final String IS_NOT_CARRIER_DELETED_BUT_PRESENT_IN_XML =
- EDITED + "!=" + CARRIER_DELETED_BUT_PRESENT_IN_XML;
+ EDITED_STATUS + "!=" + CARRIER_DELETED_BUT_PRESENT_IN_XML;
private static final String IS_OWNED_BY_DPC = OWNED_BY + "=" + OWNED_BY_DPC;
private static final String IS_NOT_OWNED_BY_DPC = OWNED_BY + "!=" + OWNED_BY_DPC;
@@ -220,6 +236,7 @@
private static final int INVALID_APN_ID = -1;
private static final List<String> CARRIERS_UNIQUE_FIELDS = new ArrayList<String>();
+ private static final Set<String> CARRIERS_BOOLEAN_FIELDS = new HashSet<String>();
private static final Map<String, String> CARRIERS_UNIQUE_FIELDS_DEFAULTS = new HashMap();
@VisibleForTesting
@@ -235,7 +252,8 @@
static {
// Columns not included in UNIQUE constraint: name, current, edited, user, server, password,
// authtype, type, protocol, roaming_protocol, sub_id, modem_cognitive, max_conns,
- // wait_time, max_conns_time, mtu, bearer_bitmask, user_visible, network_type_bitmask
+ // wait_time, max_conns_time, mtu, bearer_bitmask, user_visible, network_type_bitmask,
+ // skip_464xlat
CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(NUMERIC, "");
CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MCC, "");
CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MNC, "");
@@ -254,9 +272,19 @@
CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(ROAMING_PROTOCOL, "IP");
CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(USER_EDITABLE, "1");
CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(OWNED_BY, String.valueOf(OWNED_BY_OTHERS));
- CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(APN_SET_ID, String.valueOf(NO_SET_SET));
+ CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(APN_SET_ID, String.valueOf(NO_APN_SET_ID));
+ CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(CARRIER_ID,
+ String.valueOf(TelephonyManager.UNKNOWN_CARRIER_ID));
CARRIERS_UNIQUE_FIELDS.addAll(CARRIERS_UNIQUE_FIELDS_DEFAULTS.keySet());
+
+ // SQLite databases store bools as ints but the ContentValues objects passed in through
+ // queries use bools. As a result there is some special handling of boolean fields within
+ // the TelephonyProvider.
+ CARRIERS_BOOLEAN_FIELDS.add(CARRIER_ENABLED);
+ CARRIERS_BOOLEAN_FIELDS.add(MODEM_PERSIST);
+ CARRIERS_BOOLEAN_FIELDS.add(USER_VISIBLE);
+ CARRIERS_BOOLEAN_FIELDS.add(USER_EDITABLE);
}
@VisibleForTesting
@@ -267,6 +295,7 @@
NUMERIC + " TEXT DEFAULT ''," +
MCC + " TEXT DEFAULT ''," +
MNC + " TEXT DEFAULT ''," +
+ CARRIER_ID + " INTEGER DEFAULT " + TelephonyManager.UNKNOWN_CARRIER_ID + "," +
APN + " TEXT DEFAULT ''," +
USER + " TEXT DEFAULT ''," +
SERVER + " TEXT DEFAULT ''," +
@@ -281,31 +310,32 @@
CURRENT + " INTEGER," +
PROTOCOL + " TEXT DEFAULT " + DEFAULT_PROTOCOL + "," +
ROAMING_PROTOCOL + " TEXT DEFAULT " + DEFAULT_ROAMING_PROTOCOL + "," +
- CARRIER_ENABLED + " BOOLEAN DEFAULT 1," +
+ CARRIER_ENABLED + " BOOLEAN DEFAULT 1," + // SQLite databases store bools as ints
BEARER + " INTEGER DEFAULT 0," +
BEARER_BITMASK + " INTEGER DEFAULT 0," +
NETWORK_TYPE_BITMASK + " INTEGER DEFAULT 0," +
MVNO_TYPE + " TEXT DEFAULT ''," +
MVNO_MATCH_DATA + " TEXT DEFAULT ''," +
- SUBSCRIPTION_ID + " INTEGER DEFAULT "
- + SubscriptionManager.INVALID_SUBSCRIPTION_ID + "," +
+ SUBSCRIPTION_ID + " INTEGER DEFAULT " +
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID + "," +
PROFILE_ID + " INTEGER DEFAULT 0," +
- MODEM_COGNITIVE + " BOOLEAN DEFAULT 0," +
- MAX_CONNS + " INTEGER DEFAULT 0," +
- WAIT_TIME + " INTEGER DEFAULT 0," +
- MAX_CONNS_TIME + " INTEGER DEFAULT 0," +
+ MODEM_PERSIST + " BOOLEAN DEFAULT 0," +
+ MAX_CONNECTIONS + " INTEGER DEFAULT 0," +
+ WAIT_TIME_RETRY + " INTEGER DEFAULT 0," +
+ TIME_LIMIT_FOR_MAX_CONNECTIONS + " INTEGER DEFAULT 0," +
MTU + " INTEGER DEFAULT 0," +
- EDITED + " INTEGER DEFAULT " + UNEDITED + "," +
+ EDITED_STATUS + " INTEGER DEFAULT " + UNEDITED + "," +
USER_VISIBLE + " BOOLEAN DEFAULT 1," +
USER_EDITABLE + " BOOLEAN DEFAULT 1," +
OWNED_BY + " INTEGER DEFAULT " + OWNED_BY_OTHERS + "," +
- APN_SET_ID + " INTEGER DEFAULT " + NO_SET_SET + "," +
+ APN_SET_ID + " INTEGER DEFAULT " + NO_APN_SET_ID + "," +
+ SKIP_464XLAT + " INTEGER DEFAULT " + SKIP_464XLAT_DEFAULT + "," +
// Uniqueness collisions are used to trigger merge code so if a field is listed
// here it means we will accept both (user edited + new apn_conf definition)
// Columns not included in UNIQUE constraint: name, current, edited,
// user, server, password, authtype, type, sub_id, modem_cognitive, max_conns,
// wait_time, max_conns_time, mtu, bearer_bitmask, user_visible,
- // network_type_bitmask.
+ // network_type_bitmask, skip_464xlat.
"UNIQUE (" + TextUtils.join(", ", CARRIERS_UNIQUE_FIELDS) + "));";
}
@@ -330,6 +360,8 @@
+ " INTEGER DEFAULT " + SubscriptionManager.DATA_ROAMING_DEFAULT + ","
+ SubscriptionManager.MCC + " INTEGER DEFAULT 0,"
+ SubscriptionManager.MNC + " INTEGER DEFAULT 0,"
+ + SubscriptionManager.MCC_STRING + " TEXT,"
+ + SubscriptionManager.MNC_STRING + " TEXT,"
+ SubscriptionManager.SIM_PROVISIONING_STATUS
+ " INTEGER DEFAULT " + SubscriptionManager.SIM_PROVISIONED + ","
+ SubscriptionManager.IS_EMBEDDED + " INTEGER DEFAULT 0,"
@@ -353,7 +385,16 @@
+ SubscriptionManager.WFC_IMS_ENABLED + " INTEGER DEFAULT -1,"
+ SubscriptionManager.WFC_IMS_MODE + " INTEGER DEFAULT -1,"
+ SubscriptionManager.WFC_IMS_ROAMING_MODE + " INTEGER DEFAULT -1,"
- + SubscriptionManager.WFC_IMS_ROAMING_ENABLED + " INTEGER DEFAULT -1"
+ + SubscriptionManager.WFC_IMS_ROAMING_ENABLED + " INTEGER DEFAULT -1,"
+ + SubscriptionManager.IS_OPPORTUNISTIC + " INTEGER DEFAULT 0,"
+ + SubscriptionManager.GROUP_UUID + " TEXT,"
+ + SubscriptionManager.IS_METERED + " INTEGER DEFAULT 1,"
+ + SubscriptionManager.ISO_COUNTRY_CODE + " TEXT,"
+ + SubscriptionManager.CARRIER_ID + " INTEGER DEFAULT -1,"
+ + SubscriptionManager.PROFILE_CLASS + " INTEGER DEFAULT "
+ + SubscriptionManager.PROFILE_CLASS_DEFAULT + ","
+ + SubscriptionManager.SUBSCRIPTION_TYPE
+ + " INTEGER DEFAULT " + SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM
+ ");";
}
@@ -367,6 +408,7 @@
s_urlMatcher.addURI("telephony", "carriers/preferapnset", URL_PREFERAPNSET);
s_urlMatcher.addURI("telephony", "siminfo", URL_SIMINFO);
+ s_urlMatcher.addURI("telephony", "siminfo/#", URL_SIMINFO_USING_SUBID);
s_urlMatcher.addURI("telephony", "carriers/subId/*", URL_TELEPHONY_USING_SUBID);
s_urlMatcher.addURI("telephony", "carriers/current/subId/*", URL_CURRENT_USING_SUBID);
@@ -390,8 +432,17 @@
// Only called by Settings app, DcTracker and other telephony components to get a
// single APN according to whether DPC records are enforced.
s_urlMatcher.addURI("telephony", "carriers/filtered/#", URL_FILTERED_ID);
+ // Used by DcTracker to pass a subId.
+ s_urlMatcher.addURI("telephony", "carriers/filtered/subId/*", URL_FILTERED_USING_SUBID);
+
// Only Called by DevicePolicyManager to enforce DPC records.
s_urlMatcher.addURI("telephony", "carriers/enforce_managed", URL_ENFORCE_MANAGED);
+ s_urlMatcher.addURI("telephony", "carriers/sim_apn_list", URL_SIM_APN_LIST);
+ s_urlMatcher.addURI("telephony", "carriers/sim_apn_list/#", URL_SIM_APN_LIST_ID);
+ s_urlMatcher.addURI("telephony", "carriers/sim_apn_list/filtered",
+ URL_SIM_APN_LIST_FILTERED);
+ s_urlMatcher.addURI("telephony", "carriers/sim_apn_list/filtered/subId/*",
+ URL_SIM_APN_LIST_FILTERED_ID);
s_currentNullMap = new ContentValues(1);
s_currentNullMap.put(CURRENT, "0");
@@ -419,7 +470,8 @@
mInjector = injector;
}
- private static class DatabaseHelper extends SQLiteOpenHelper {
+ @VisibleForTesting
+ public static class DatabaseHelper extends SQLiteOpenHelper {
// Context to access resources with
private Context mContext;
@@ -433,12 +485,18 @@
mContext = context;
// Memory optimization - close idle connections after 30s of inactivity
setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
+ setWriteAheadLoggingEnabled(false);
}
- private static int getVersion(Context context) {
+ @VisibleForTesting
+ public static int getVersion(Context context) {
if (VDBG) log("getVersion:+");
// Get the database version, combining a static schema version and the XML version
Resources r = context.getResources();
+ if (r == null) {
+ loge("resources=null, return version=" + Integer.toHexString(DATABASE_VERSION));
+ return DATABASE_VERSION;
+ }
XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
try {
XmlUtils.beginDocument(parser, "apns");
@@ -518,9 +576,32 @@
} catch (IOException e) {
loge("IOException for " + file.getAbsolutePath() + ":" + e);
}
+
+ // The RRO may have been updated in a firmware upgrade. Add checksum for the
+ // resources to the total checksum so that apns in an RRO update is not missed.
+ try (InputStream inputStream = mContext.getResources().
+ openRawResource(com.android.internal.R.xml.apns)) {
+ byte[] array = toByteArray(inputStream);
+ CRC32 c = new CRC32();
+ c.update(array);
+ checksum += c.getValue();
+ if (DBG) log("Checksum after adding resource is " + checksum);
+ } catch (IOException | Resources.NotFoundException e) {
+ loge("Exception when calculating checksum for internal apn resources: " + e);
+ }
return checksum;
}
+ private static byte[] toByteArray(InputStream input) throws IOException {
+ byte[] buffer = new byte[128];
+ int bytesRead;
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ while ((bytesRead = input.read(buffer)) != -1) {
+ output.write(buffer, 0, bytesRead);
+ }
+ return output.toByteArray();
+ }
+
private long getApnConfChecksum() {
SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
return sp.getLong(APN_CONF_CHECKSUM, -1);
@@ -538,8 +619,10 @@
File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);
File oemConfFile = new File(Environment.getOemDirectory(), OEM_APNS_PATH);
File updatedConfFile = new File(Environment.getDataDirectory(), OTA_UPDATED_APNS_PATH);
- confFile = getNewerFile(confFile, oemConfFile);
- confFile = getNewerFile(confFile, updatedConfFile);
+ File productConfFile = new File(Environment.getProductDirectory(), PARTNER_APNS_PATH);
+ confFile = pickSecondIfExists(confFile, oemConfFile);
+ confFile = pickSecondIfExists(confFile, productConfFile);
+ confFile = pickSecondIfExists(confFile, updatedConfFile);
return confFile;
}
@@ -570,16 +653,20 @@
if (VDBG) log("dbh.initDatabase:+ db=" + db);
// Read internal APNS data
Resources r = mContext.getResources();
- XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
int publicversion = -1;
- try {
- XmlUtils.beginDocument(parser, "apns");
- publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
- loadApns(db, parser);
- } catch (Exception e) {
- loge("Got exception while loading APN database." + e);
- } finally {
- parser.close();
+ if (r != null) {
+ XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
+ try {
+ XmlUtils.beginDocument(parser, "apns");
+ publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
+ loadApns(db, parser);
+ } catch (Exception e) {
+ loge("Got exception while loading APN database." + e);
+ } finally {
+ parser.close();
+ }
+ } else {
+ loge("initDatabase: resources=null");
}
// Read external APNS data (partner-provided)
@@ -622,12 +709,12 @@
// Change USER_DELETED_BUT_PRESENT_IN_XML to USER_DELETED
ContentValues cv = new ContentValues();
- cv.put(EDITED, USER_DELETED);
+ cv.put(EDITED_STATUS, USER_DELETED);
db.update(CARRIERS_TABLE, cv, IS_USER_DELETED_BUT_PRESENT_IN_XML, null);
// Change CARRIER_DELETED_BUT_PRESENT_IN_XML to CARRIER_DELETED
cv = new ContentValues();
- cv.put(EDITED, CARRIER_DELETED);
+ cv.put(EDITED_STATUS, CARRIER_DELETED);
db.update(CARRIERS_TABLE, cv, IS_CARRIER_DELETED_BUT_PRESENT_IN_XML, null);
if (confreader != null) {
@@ -645,26 +732,16 @@
}
- private File getNewerFile(File sysApnFile, File altApnFile) {
+ private File pickSecondIfExists(File sysApnFile, File altApnFile) {
if (altApnFile.exists()) {
- // Alternate file exists. Use the newer one.
- long altFileTime = altApnFile.lastModified();
- long currFileTime = sysApnFile.lastModified();
- if (DBG) log("APNs Timestamp: altFileTime = " + altFileTime + " currFileTime = "
- + currFileTime);
-
- // To get the latest version from OEM or System image
- if (altFileTime > currFileTime) {
- if (DBG) log("APNs Timestamp: Alternate image " + altApnFile.getPath() +
- " is greater than System image");
- return altApnFile;
- }
+ if (DBG) log("Load APNs from " + altApnFile.getPath() +
+ " instead of " + sysApnFile.getPath());
+ return altApnFile;
} else {
- // No Apn in alternate image, so load it from system image.
- if (DBG) log("No APNs in OEM image = " + altApnFile.getPath() +
- " Load APNs from system image");
+ if (DBG) log("Load APNs from " + sysApnFile.getPath() +
+ " instead of " + altApnFile.getPath());
+ return sysApnFile;
}
- return sysApnFile;
}
@Override
@@ -984,7 +1061,7 @@
if (oldVersion < (24 << 16 | 6)) {
Cursor c = null;
String[] proj = {"_id"};
- recreateDB(c, db, proj, /* version */24);
+ recreateDB(db, proj, /* version */24);
if (VDBG) {
c = db.query(CARRIERS_TABLE, proj, null, null, null, null, null);
log("dbh.onUpgrade:- after upgrading total number of rows: " + c.getCount());
@@ -1024,7 +1101,7 @@
// Carriers.NO_SET_SET by default.
try {
db.execSQL("ALTER TABLE " + CARRIERS_TABLE + " ADD COLUMN " +
- APN_SET_ID + " INTEGER DEFAULT " + NO_SET_SET + ";");
+ APN_SET_ID + " INTEGER DEFAULT " + NO_APN_SET_ID + ";");
} catch (SQLiteException e) {
if (DBG) {
log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. " +
@@ -1033,9 +1110,169 @@
}
oldVersion = 26 << 16 | 6;
}
+
+ if (oldVersion < (27 << 16 | 6)) {
+ // Add the new MCC_STRING and MNC_STRING columns into the subscription table,
+ // and attempt to populate them.
+ try {
+ // Try to update the siminfo table. It might not be there.
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE +
+ " ADD COLUMN " + SubscriptionManager.MCC_STRING + " TEXT;");
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE +
+ " ADD COLUMN " + SubscriptionManager.MNC_STRING + " TEXT;");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+ " The table will get created in onOpen.");
+ }
+ }
+ // Migrate the old integer values over to strings
+ String[] proj = {SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
+ SubscriptionManager.MCC, SubscriptionManager.MNC};
+ try (Cursor c = db.query(SIMINFO_TABLE, proj, null, null, null, null, null)) {
+ while (c.moveToNext()) {
+ fillInMccMncStringAtCursor(mContext, db, c);
+ }
+ }
+ oldVersion = 27 << 16 | 6;
+ }
+
+ if (oldVersion < (28 << 16 | 6)) {
+ try {
+ // Try to update the siminfo table. It might not be there.
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+ + SubscriptionManager.IS_OPPORTUNISTIC + " INTEGER DEFAULT 0;");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+ "The table will get created in onOpen.");
+ }
+ }
+ oldVersion = 28 << 16 | 6;
+ }
+
+ if (oldVersion < (29 << 16 | 6)) {
+ try {
+ // Add a new column Telephony.CARRIER_ID into the database and add UNIQUE
+ // constraint into table. However, sqlite cannot add constraints to an existing
+ // table, so recreate the table.
+ String[] proj = {"_id"};
+ recreateDB(db, proj, /* version */29);
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. " +
+ "The table will get created in onOpen.");
+ }
+ }
+ oldVersion = 29 << 16 | 6;
+ }
+
+ if (oldVersion < (30 << 16 | 6)) {
+ try {
+ // Try to update the siminfo table. It might not be there.
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+ + SubscriptionManager.GROUP_UUID + " TEXT;");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+ "The table will get created in onOpen.");
+ }
+ }
+ oldVersion = 30 << 16 | 6;
+ }
+
+ if (oldVersion < (31 << 16 | 6)) {
+ try {
+ // Try to update the siminfo table. It might not be there.
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+ + SubscriptionManager.IS_METERED + " INTEGER DEFAULT 1;");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+ "The table will get created in onOpen.");
+ }
+ }
+ oldVersion = 31 << 16 | 6;
+ }
+
+ if (oldVersion < (32 << 16 | 6)) {
+ try {
+ // Try to update the siminfo table. It might not be there.
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+ + SubscriptionManager.ISO_COUNTRY_CODE + " TEXT;");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+ "The table will get created in onOpen.");
+ }
+ }
+ oldVersion = 32 << 16 | 6;
+ }
+
+ if (oldVersion < (33 << 16 | 6)) {
+ try {
+ // Try to update the siminfo table. It might not be there.
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+ + SubscriptionManager.CARRIER_ID + " INTEGER DEFAULT -1;");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+ "The table will get created in onOpen.");
+ }
+ }
+ oldVersion = 33 << 16 | 6;
+ }
+
+ if (oldVersion < (34 << 16 | 6)) {
+ try {
+ // Try to update the siminfo table. It might not be there.
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " +
+ SubscriptionManager.PROFILE_CLASS + " INTEGER DEFAULT " +
+ SubscriptionManager.PROFILE_CLASS_DEFAULT + ";");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+ "The table will get created in onOpen.");
+ }
+ }
+ oldVersion = 34 << 16 | 6;
+ }
+
+ if (oldVersion < (35 << 16 | 6)) {
+ try {
+ // Try to update the siminfo table. It might not be there.
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+ + SubscriptionManager.SUBSCRIPTION_TYPE + " INTEGER DEFAULT "
+ + SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM + ";");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+ "The table will get created in onOpen.");
+ }
+ }
+ oldVersion = 35 << 16 | 6;
+ }
+
+ if (oldVersion < (36 << 16 | 6)) {
+ // Add a new column Carriers.SKIP_464XLAT into the database and set the value to
+ // SKIP_464XLAT_DEFAULT.
+ try {
+ db.execSQL("ALTER TABLE " + CARRIERS_TABLE + " ADD COLUMN " +
+ SKIP_464XLAT + " INTEGER DEFAULT " + SKIP_464XLAT_DEFAULT + ";");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. " +
+ "The table will get created in onOpen.");
+ }
+ }
+ oldVersion = 36 << 16 | 6;
+ }
+
if (DBG) {
log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
}
+ // when adding fields to onUpgrade, also add a unit test to TelephonyDatabaseHelperTest
+ // and update the DATABASE_VERSION field and add a column in copyAllApnValues
}
private void recreateSimInfoDB(Cursor c, SQLiteDatabase db, String[] proj) {
@@ -1143,12 +1380,13 @@
}
}
- private void recreateDB(Cursor c, SQLiteDatabase db, String[] proj, int version) {
+ private void recreateDB(SQLiteDatabase db, String[] proj, int version) {
// Upgrade steps are:
// 1. Create a temp table- done in createCarriersTable()
// 2. copy over APNs from old table to new table - done in copyDataToTmpTable()
// 3. Drop the existing table.
// 4. Copy over the tmp table.
+ Cursor c;
if (VDBG) {
c = db.query(CARRIERS_TABLE, proj, null, null, null, null, null);
log("dbh.onUpgrade:- before upgrading total number of rows: " + c.getCount());
@@ -1166,7 +1404,7 @@
createCarriersTable(db, CARRIERS_TABLE_TMP);
- copyDataToTmpTable(db, c);
+ copyDataToTmpTable(db, c, version);
c.close();
db.execSQL("DROP TABLE IF EXISTS " + CARRIERS_TABLE);
@@ -1271,10 +1509,10 @@
queryValOrNull(MVNO_TYPE) +
queryValOrNull(MVNO_MATCH_DATA) +
queryValOrNull(PROFILE_ID) +
- queryVal2OrNull(MODEM_COGNITIVE) +
- queryValOrNull(MAX_CONNS) +
- queryValOrNull(WAIT_TIME) +
- queryValOrNull(MAX_CONNS_TIME) +
+ queryVal2OrNull(MODEM_PERSIST) +
+ queryValOrNull(MAX_CONNECTIONS) +
+ queryValOrNull(WAIT_TIME_RETRY) +
+ queryValOrNull(TIME_LIMIT_FOR_MAX_CONNECTIONS) +
queryValOrNull(MTU);
String[] whereArgs = new String[29];
int i = 0;
@@ -1309,15 +1547,13 @@
whereArgs[i++] = values.containsKey(ROAMING_PROTOCOL) ?
values.getAsString(ROAMING_PROTOCOL) : DEFAULT_ROAMING_PROTOCOL;
- if (values.containsKey(CARRIER_ENABLED) &&
- (values.getAsString(CARRIER_ENABLED).
- equalsIgnoreCase("false") ||
- values.getAsString(CARRIER_ENABLED).equals("0"))) {
- whereArgs[i++] = "false";
- whereArgs[i++] = "0";
+ if (values.containsKey(CARRIER_ENABLED)) {
+ whereArgs[i++] = convertStringToBoolString(values.getAsString(CARRIER_ENABLED));
+ whereArgs[i++] = convertStringToIntString(values.getAsString(CARRIER_ENABLED));
} else {
- whereArgs[i++] = "true";
- whereArgs[i++] = "1";
+ String defaultIntString = CARRIERS_UNIQUE_FIELDS_DEFAULTS.get(CARRIER_ENABLED);
+ whereArgs[i++] = convertStringToBoolString(defaultIntString);
+ whereArgs[i++] = defaultIntString;
}
whereArgs[i++] = values.containsKey(BEARER) ?
@@ -1329,10 +1565,10 @@
whereArgs[i++] = values.containsKey(PROFILE_ID) ?
values.getAsString(PROFILE_ID) : "0";
- if (values.containsKey(MODEM_COGNITIVE) &&
- (values.getAsString(MODEM_COGNITIVE).
+ if (values.containsKey(MODEM_PERSIST) &&
+ (values.getAsString(MODEM_PERSIST).
equalsIgnoreCase("true") ||
- values.getAsString(MODEM_COGNITIVE).equals("1"))) {
+ values.getAsString(MODEM_PERSIST).equals("1"))) {
whereArgs[i++] = "true";
whereArgs[i++] = "1";
} else {
@@ -1340,12 +1576,12 @@
whereArgs[i++] = "0";
}
- whereArgs[i++] = values.containsKey(MAX_CONNS) ?
- values.getAsString(MAX_CONNS) : "0";
- whereArgs[i++] = values.containsKey(WAIT_TIME) ?
- values.getAsString(WAIT_TIME) : "0";
- whereArgs[i++] = values.containsKey(MAX_CONNS_TIME) ?
- values.getAsString(MAX_CONNS_TIME) : "0";
+ whereArgs[i++] = values.containsKey(MAX_CONNECTIONS) ?
+ values.getAsString(MAX_CONNECTIONS) : "0";
+ whereArgs[i++] = values.containsKey(WAIT_TIME_RETRY) ?
+ values.getAsString(WAIT_TIME_RETRY) : "0";
+ whereArgs[i++] = values.containsKey(TIME_LIMIT_FOR_MAX_CONNECTIONS) ?
+ values.getAsString(TIME_LIMIT_FOR_MAX_CONNECTIONS) : "0";
whereArgs[i++] = values.containsKey(MTU) ?
values.getAsString(MTU) : "0";
@@ -1362,14 +1598,16 @@
db.delete(CARRIERS_TABLE, where, whereArgs);
}
- private void copyDataToTmpTable(SQLiteDatabase db, Cursor c) {
+ private void copyDataToTmpTable(SQLiteDatabase db, Cursor c, int version) {
// Move entries from CARRIERS_TABLE to CARRIERS_TABLE_TMP
if (c != null) {
while (c.moveToNext()) {
ContentValues cv = new ContentValues();
- copyApnValuesV17(cv, c);
- // Sync bearer bitmask and network type bitmask
- getNetworkTypeBitmaskFromCursor(cv, c);
+ copyAllApnValues(cv, c);
+ if (version == 24) {
+ // Sync bearer bitmask and network type bitmask
+ getNetworkTypeBitmaskFromCursor(cv, c);
+ }
try {
db.insertWithOnConflict(CARRIERS_TABLE_TMP, null, cv,
SQLiteDatabase.CONFLICT_ABORT);
@@ -1417,95 +1655,142 @@
getIntValueFromCursor(cv, c, BEARER);
getIntValueFromCursor(cv, c, SUBSCRIPTION_ID);
getIntValueFromCursor(cv, c, PROFILE_ID);
- getIntValueFromCursor(cv, c, MODEM_COGNITIVE);
- getIntValueFromCursor(cv, c, MAX_CONNS);
- getIntValueFromCursor(cv, c, WAIT_TIME);
- getIntValueFromCursor(cv, c, MAX_CONNS_TIME);
+ getIntValueFromCursor(cv, c, MODEM_PERSIST);
+ getIntValueFromCursor(cv, c, MAX_CONNECTIONS);
+ getIntValueFromCursor(cv, c, WAIT_TIME_RETRY);
+ getIntValueFromCursor(cv, c, TIME_LIMIT_FOR_MAX_CONNECTIONS);
getIntValueFromCursor(cv, c, MTU);
getIntValueFromCursor(cv, c, BEARER_BITMASK);
- getIntValueFromCursor(cv, c, EDITED);
+ getIntValueFromCursor(cv, c, EDITED_STATUS);
getIntValueFromCursor(cv, c, USER_VISIBLE);
}
+ private void copyAllApnValues(ContentValues cv, Cursor c) {
+ // String vals
+ getStringValueFromCursor(cv, c, NAME);
+ getStringValueFromCursor(cv, c, NUMERIC);
+ getStringValueFromCursor(cv, c, MCC);
+ getStringValueFromCursor(cv, c, MNC);
+ getStringValueFromCursor(cv, c, APN);
+ getStringValueFromCursor(cv, c, USER);
+ getStringValueFromCursor(cv, c, SERVER);
+ getStringValueFromCursor(cv, c, PASSWORD);
+ getStringValueFromCursor(cv, c, PROXY);
+ getStringValueFromCursor(cv, c, PORT);
+ getStringValueFromCursor(cv, c, MMSPROXY);
+ getStringValueFromCursor(cv, c, MMSPORT);
+ getStringValueFromCursor(cv, c, MMSC);
+ getStringValueFromCursor(cv, c, TYPE);
+ getStringValueFromCursor(cv, c, PROTOCOL);
+ getStringValueFromCursor(cv, c, ROAMING_PROTOCOL);
+ getStringValueFromCursor(cv, c, MVNO_TYPE);
+ getStringValueFromCursor(cv, c, MVNO_MATCH_DATA);
+
+ // bool/int vals
+ getIntValueFromCursor(cv, c, AUTH_TYPE);
+ getIntValueFromCursor(cv, c, CURRENT);
+ getIntValueFromCursor(cv, c, CARRIER_ENABLED);
+ getIntValueFromCursor(cv, c, BEARER);
+ getIntValueFromCursor(cv, c, SUBSCRIPTION_ID);
+ getIntValueFromCursor(cv, c, PROFILE_ID);
+ getIntValueFromCursor(cv, c, MODEM_PERSIST);
+ getIntValueFromCursor(cv, c, MAX_CONNECTIONS);
+ getIntValueFromCursor(cv, c, WAIT_TIME_RETRY);
+ getIntValueFromCursor(cv, c, TIME_LIMIT_FOR_MAX_CONNECTIONS);
+ getIntValueFromCursor(cv, c, MTU);
+ getIntValueFromCursor(cv, c, NETWORK_TYPE_BITMASK);
+ getIntValueFromCursor(cv, c, BEARER_BITMASK);
+ getIntValueFromCursor(cv, c, EDITED_STATUS);
+ getIntValueFromCursor(cv, c, USER_VISIBLE);
+ getIntValueFromCursor(cv, c, USER_EDITABLE);
+ getIntValueFromCursor(cv, c, OWNED_BY);
+ getIntValueFromCursor(cv, c, APN_SET_ID);
+ getIntValueFromCursor(cv, c, SKIP_464XLAT);
+ }
private void copyPreservedApnsToNewTable(SQLiteDatabase db, Cursor c) {
// Move entries from CARRIERS_TABLE to CARRIERS_TABLE_TMP
- if (c != null) {
- String[] persistApnsForPlmns = mContext.getResources().getStringArray(
- R.array.persist_apns_for_plmn);
- while (c.moveToNext()) {
- ContentValues cv = new ContentValues();
- String val;
- // Using V17 copy function for V15 upgrade. This should be fine since it handles
- // columns that may not exist properly (getStringValueFromCursor() and
- // getIntValueFromCursor() handle column index -1)
- copyApnValuesV17(cv, c);
- // Change bearer to a bitmask
- String bearerStr = c.getString(c.getColumnIndex(BEARER));
- if (!TextUtils.isEmpty(bearerStr)) {
- int bearer_bitmask = ServiceState.getBitmaskForTech(
- Integer.parseInt(bearerStr));
- cv.put(BEARER_BITMASK, bearer_bitmask);
+ if (c != null && mContext.getResources() != null) {
+ try {
+ String[] persistApnsForPlmns = mContext.getResources().getStringArray(
+ R.array.persist_apns_for_plmn);
+ while (c.moveToNext()) {
+ ContentValues cv = new ContentValues();
+ String val;
+ // Using V17 copy function for V15 upgrade. This should be fine since it handles
+ // columns that may not exist properly (getStringValueFromCursor() and
+ // getIntValueFromCursor() handle column index -1)
+ copyApnValuesV17(cv, c);
+ // Change bearer to a bitmask
+ String bearerStr = c.getString(c.getColumnIndex(BEARER));
+ if (!TextUtils.isEmpty(bearerStr)) {
+ int bearer_bitmask = ServiceState.getBitmaskForTech(
+ Integer.parseInt(bearerStr));
+ cv.put(BEARER_BITMASK, bearer_bitmask);
- int networkTypeBitmask = ServiceState.getBitmaskForTech(
- ServiceState.rilRadioTechnologyToNetworkType(
- Integer.parseInt(bearerStr)));
- cv.put(NETWORK_TYPE_BITMASK, networkTypeBitmask);
- }
-
- int userEditedColumnIdx = c.getColumnIndex("user_edited");
- if (userEditedColumnIdx != -1) {
- String user_edited = c.getString(userEditedColumnIdx);
- if (!TextUtils.isEmpty(user_edited)) {
- cv.put(EDITED, new Integer(user_edited));
+ int networkTypeBitmask = ServiceState.getBitmaskForTech(
+ ServiceState.rilRadioTechnologyToNetworkType(
+ Integer.parseInt(bearerStr)));
+ cv.put(NETWORK_TYPE_BITMASK, networkTypeBitmask);
}
- } else {
- cv.put(EDITED, CARRIER_EDITED);
- }
- // New EDITED column. Default value (UNEDITED) will
- // be used for all rows except for non-mvno entries for plmns indicated
- // by resource: those will be set to CARRIER_EDITED to preserve
- // their current values
- val = c.getString(c.getColumnIndex(NUMERIC));
- for (String s : persistApnsForPlmns) {
- if (!TextUtils.isEmpty(val) && val.equals(s) &&
- (!cv.containsKey(MVNO_TYPE) ||
- TextUtils.isEmpty(cv.getAsString(MVNO_TYPE)))) {
- if (userEditedColumnIdx == -1) {
- cv.put(EDITED, CARRIER_EDITED);
- } else { // if (oldVersion == 14) -- if db had user_edited column
- if (cv.getAsInteger(EDITED) == USER_EDITED) {
- cv.put(EDITED, CARRIER_EDITED);
- }
+ int userEditedColumnIdx = c.getColumnIndex("user_edited");
+ if (userEditedColumnIdx != -1) {
+ String user_edited = c.getString(userEditedColumnIdx);
+ if (!TextUtils.isEmpty(user_edited)) {
+ cv.put(EDITED_STATUS, new Integer(user_edited));
}
+ } else {
+ cv.put(EDITED_STATUS, CARRIER_EDITED);
+ }
- break;
+ // New EDITED column. Default value (UNEDITED) will
+ // be used for all rows except for non-mvno entries for plmns indicated
+ // by resource: those will be set to CARRIER_EDITED to preserve
+ // their current values
+ val = c.getString(c.getColumnIndex(NUMERIC));
+ for (String s : persistApnsForPlmns) {
+ if (!TextUtils.isEmpty(val) && val.equals(s) &&
+ (!cv.containsKey(MVNO_TYPE) ||
+ TextUtils.isEmpty(cv.getAsString(MVNO_TYPE)))) {
+ if (userEditedColumnIdx == -1) {
+ cv.put(EDITED_STATUS, CARRIER_EDITED);
+ } else { // if (oldVersion == 14) -- if db had user_edited column
+ if (cv.getAsInteger(EDITED_STATUS) == USER_EDITED) {
+ cv.put(EDITED_STATUS, CARRIER_EDITED);
+ }
+ }
+
+ break;
+ }
+ }
+
+ try {
+ db.insertWithOnConflict(CARRIERS_TABLE_TMP, null, cv,
+ SQLiteDatabase.CONFLICT_ABORT);
+ if (VDBG) {
+ log("dbh.copyPreservedApnsToNewTable: db.insert returned >= 0; " +
+ "insert successful for cv " + cv);
+ }
+ } catch (SQLException e) {
+ if (VDBG)
+ log("dbh.copyPreservedApnsToNewTable insertWithOnConflict exception " +
+ e + " for cv " + cv);
+ // Insertion failed which could be due to a conflict. Check if that is
+ // the case and merge the entries
+ Cursor oldRow = DatabaseHelper.selectConflictingRow(db,
+ CARRIERS_TABLE_TMP, cv);
+ if (oldRow != null) {
+ ContentValues mergedValues = new ContentValues();
+ mergeFieldsAndUpdateDb(db, CARRIERS_TABLE_TMP, oldRow, cv,
+ mergedValues, true, mContext);
+ oldRow.close();
+ }
}
}
-
- try {
- db.insertWithOnConflict(CARRIERS_TABLE_TMP, null, cv,
- SQLiteDatabase.CONFLICT_ABORT);
- if (VDBG) {
- log("dbh.copyPreservedApnsToNewTable: db.insert returned >= 0; " +
- "insert successful for cv " + cv);
- }
- } catch (SQLException e) {
- if (VDBG)
- log("dbh.copyPreservedApnsToNewTable insertWithOnConflict exception " +
- e + " for cv " + cv);
- // Insertion failed which could be due to a conflict. Check if that is
- // the case and merge the entries
- Cursor oldRow = DatabaseHelper.selectConflictingRow(db,
- CARRIERS_TABLE_TMP, cv);
- if (oldRow != null) {
- ContentValues mergedValues = new ContentValues();
- mergeFieldsAndUpdateDb(db, CARRIERS_TABLE_TMP, oldRow, cv,
- mergedValues, true, mContext);
- oldRow.close();
- }
- }
+ } catch (Resources.NotFoundException e) {
+ loge("array.persist_apns_for_plmn is not found");
+ return;
}
}
}
@@ -1621,15 +1906,16 @@
addIntAttribute(parser, "authtype", map, AUTH_TYPE);
addIntAttribute(parser, "bearer", map, BEARER);
addIntAttribute(parser, "profile_id", map, PROFILE_ID);
- addIntAttribute(parser, "max_conns", map, MAX_CONNS);
- addIntAttribute(parser, "wait_time", map, WAIT_TIME);
- addIntAttribute(parser, "max_conns_time", map, MAX_CONNS_TIME);
+ addIntAttribute(parser, "max_conns", map, MAX_CONNECTIONS);
+ addIntAttribute(parser, "wait_time", map, WAIT_TIME_RETRY);
+ addIntAttribute(parser, "max_conns_time", map, TIME_LIMIT_FOR_MAX_CONNECTIONS);
addIntAttribute(parser, "mtu", map, MTU);
addIntAttribute(parser, "apn_set_id", map, APN_SET_ID);
-
+ addIntAttribute(parser, "carrier_id", map, CARRIER_ID);
+ addIntAttribute(parser, "skip_464xlat", map, SKIP_464XLAT);
addBoolAttribute(parser, "carrier_enabled", map, CARRIER_ENABLED);
- addBoolAttribute(parser, "modem_cognitive", map, MODEM_COGNITIVE);
+ addBoolAttribute(parser, "modem_cognitive", map, MODEM_PERSIST);
addBoolAttribute(parser, "user_visible", map, USER_VISIBLE);
addBoolAttribute(parser, "user_editable", map, USER_EDITABLE);
@@ -1751,7 +2037,7 @@
if (oldRow != null) {
// Update the row
ContentValues mergedValues = new ContentValues();
- int edited = oldRow.getInt(oldRow.getColumnIndex(EDITED));
+ int edited = oldRow.getInt(oldRow.getColumnIndex(EDITED_STATUS));
int old_edited = edited;
if (edited != UNEDITED) {
if (edited == USER_DELETED) {
@@ -1763,7 +2049,7 @@
// by user but present in apn xml file.
edited = CARRIER_DELETED_BUT_PRESENT_IN_XML;
}
- mergedValues.put(EDITED, edited);
+ mergedValues.put(EDITED_STATUS, edited);
}
mergeFieldsAndUpdateDb(db, CARRIERS_TABLE, oldRow, row, mergedValues, false,
@@ -1860,16 +2146,16 @@
if (!onUpgrade) {
// Do not overwrite a carrier or user edit with EDITED=UNEDITED
- if (newRow.containsKey(EDITED)) {
- int oldEdited = oldRow.getInt(oldRow.getColumnIndex(EDITED));
- int newEdited = newRow.getAsInteger(EDITED);
+ if (newRow.containsKey(EDITED_STATUS)) {
+ int oldEdited = oldRow.getInt(oldRow.getColumnIndex(EDITED_STATUS));
+ int newEdited = newRow.getAsInteger(EDITED_STATUS);
if (newEdited == UNEDITED && (oldEdited == CARRIER_EDITED
|| oldEdited == CARRIER_DELETED
|| oldEdited == CARRIER_DELETED_BUT_PRESENT_IN_XML
|| oldEdited == USER_EDITED
|| oldEdited == USER_DELETED
|| oldEdited == USER_DELETED_BUT_PRESENT_IN_XML)) {
- newRow.remove(EDITED);
+ newRow.remove(EDITED_STATUS);
}
}
mergedValues.putAll(newRow);
@@ -1893,13 +2179,17 @@
boolean match = false;
// Check if APN falls under persist_apns_for_plmn
- String[] persistApnsForPlmns = context.getResources().getStringArray(
- R.array.persist_apns_for_plmn);
- for (String s : persistApnsForPlmns) {
- if (s.equalsIgnoreCase(newRow.getAsString(NUMERIC))) {
- match = true;
- break;
+ if (context.getResources() != null) {
+ String[] persistApnsForPlmns = context.getResources().getStringArray(
+ R.array.persist_apns_for_plmn);
+ for (String s : persistApnsForPlmns) {
+ if (s.equalsIgnoreCase(newRow.getAsString(NUMERIC))) {
+ match = true;
+ break;
+ }
}
+ } else {
+ loge("separateRowsNeeded: resources=null");
}
if (!match) return false;
@@ -1986,7 +2276,7 @@
String[] columns = { "_id",
TYPE,
- EDITED,
+ EDITED_STATUS,
BEARER_BITMASK,
NETWORK_TYPE_BITMASK,
PROFILE_ID };
@@ -1994,15 +2284,16 @@
int i = 0;
String[] selectionArgs = new String[CARRIERS_UNIQUE_FIELDS.size()];
for (String field : CARRIERS_UNIQUE_FIELDS) {
- if (CARRIER_ENABLED.equals(field)) {
- // for CARRIER_ENABLED we overwrite the value "false" with "0"
- selectionArgs[i++] = row.containsKey(CARRIER_ENABLED) &&
- (row.getAsString(CARRIER_ENABLED).equals("0") ||
- row.getAsString(CARRIER_ENABLED).equals("false")) ?
- "0" : CARRIERS_UNIQUE_FIELDS_DEFAULTS.get(CARRIER_ENABLED);
+ if (!row.containsKey(field)) {
+ selectionArgs[i++] = CARRIERS_UNIQUE_FIELDS_DEFAULTS.get(field);
} else {
- selectionArgs[i++] = row.containsKey(field) ?
- row.getAsString(field) : CARRIERS_UNIQUE_FIELDS_DEFAULTS.get(field);
+ if (CARRIERS_BOOLEAN_FIELDS.contains(field)) {
+ // for boolean fields we overwrite the strings "true" and "false" with "1"
+ // and "0"
+ selectionArgs[i++] = convertStringToIntString(row.getAsString(field));
+ } else {
+ selectionArgs[i++] = row.getAsString(field);
+ }
}
}
@@ -2032,6 +2323,24 @@
}
/**
+ * Convert "true" and "false" to "1" and "0".
+ * If the passed in string is already "1" or "0" returns the passed in string.
+ */
+ private static String convertStringToIntString(String boolString) {
+ if ("0".equals(boolString) || "false".equalsIgnoreCase(boolString)) return "0";
+ return "1";
+ }
+
+ /**
+ * Convert "1" and "0" to "true" and "false".
+ * If the passed in string is already "true" or "false" returns the passed in string.
+ */
+ private static String convertStringToBoolString(String intString) {
+ if ("0".equals(intString) || "false".equalsIgnoreCase(intString)) return "false";
+ return "true";
+ }
+
+ /**
* These methods can be overridden in a subclass for testing TelephonyProvider using an
* in-memory database.
*/
@@ -2067,7 +2376,7 @@
return s_apnSourceServiceExists;
}
- private void restoreApnsWithService() {
+ private void restoreApnsWithService(int subId) {
Context context = getContext();
Resources r = context.getResources();
ServiceConnection connection = new ServiceConnection() {
@@ -2095,7 +2404,8 @@
r.getString(R.string.apn_source_service)));
log("binding to service to restore apns, intent=" + intent);
try {
- if (context.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
+ if (context.bindService(intent, connection, Context.BIND_IMPORTANT |
+ Context.BIND_AUTO_CREATE)) {
synchronized (mLock) {
while (mIApnSourceService == null) {
try {
@@ -2105,7 +2415,7 @@
}
}
try {
- ContentValues[] values = mIApnSourceService.getApns();
+ ContentValues[] values = mIApnSourceService.getApns(subId);
if (values != null) {
// we use the unsynchronized insert because this function is called
// within the syncrhonized function delete()
@@ -2241,7 +2551,7 @@
try {
return Integer.parseInt(sp.getString(APN_SET_ID + subId, null));
} catch (NumberFormatException e) {
- return NO_SET_SET;
+ return NO_APN_SET_ID;
}
}
@@ -2304,7 +2614,7 @@
private long getPreferredApnIdFromApn(int subId) {
log("getPreferredApnIdFromApn: for subId " + subId);
- SQLiteDatabase db = getWritableDatabase();
+ SQLiteDatabase db = getReadableDatabase();
String where = TextUtils.join("=? and ", CARRIERS_UNIQUE_FIELDS) + "=?";
String[] whereArgs = new String[CARRIERS_UNIQUE_FIELDS.size()];
SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_FULL_APN,
@@ -2365,7 +2675,7 @@
public synchronized Cursor query(Uri url, String[] projectionIn, String selection,
String[] selectionArgs, String sort) {
if (VDBG) log("query: url=" + url + ", projectionIn=" + projectionIn + ", selection="
- + selection + "selectionArgs=" + selectionArgs + ", sort=" + sort);
+ + selection + "selectionArgs=" + selectionArgs + ", sort=" + sort);
TelephonyManager mTelephonyManager =
(TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
int subId = SubscriptionManager.getDefaultSubscriptionId();
@@ -2377,6 +2687,7 @@
List<String> constraints = new ArrayList<String>();
int match = s_urlMatcher.match(url);
+ checkQueryPermission(match, projectionIn);
switch (match) {
case URL_TELEPHONY_USING_SUBID: {
subIdString = url.getLastPathSegment();
@@ -2459,7 +2770,7 @@
// intentional fall through from above case
case URL_PREFERAPNSET: {
final int set = getPreferredApnSetId(subId);
- if (set != NO_SET_SET) {
+ if (set != NO_APN_SET_ID) {
constraints.add(APN_SET_ID + "=" + set);
}
break;
@@ -2472,12 +2783,25 @@
break;
}
- case URL_FILTERED_ID: {
- constraints.add("_id = " + url.getLastPathSegment());
+ case URL_FILTERED_ID:
+ case URL_FILTERED_USING_SUBID: {
+ String idString = url.getLastPathSegment();
+ if (match == URL_FILTERED_ID) {
+ constraints.add("_id = " + idString);
+ } else {
+ try {
+ subId = Integer.parseInt(idString);
+ // TODO b/74213956 turn this back on once insertion includes correct sub id
+ // constraints.add(SUBSCRIPTION_ID + "=" + subIdString);
+ } catch (NumberFormatException e) {
+ loge("NumberFormatException" + e);
+ return null;
+ }
+ }
}
//intentional fall through from above case
case URL_FILTERED: {
- if(isManagedApnEnforced()) {
+ if (isManagedApnEnforced()) {
// If enforced, return DPC records only.
constraints.add(IS_OWNED_BY_DPC);
} else {
@@ -2499,6 +2823,43 @@
qb.setTables(SIMINFO_TABLE);
break;
}
+ case URL_SIM_APN_LIST_ID: {
+ subIdString = url.getLastPathSegment();
+ try {
+ subId = Integer.parseInt(subIdString);
+ } catch (NumberFormatException e) {
+ loge("NumberFormatException" + e);
+ return null;
+ }
+ }
+ //intentional fall through from above case
+ case URL_SIM_APN_LIST: {
+ qb.appendWhere(IS_NOT_OWNED_BY_DPC);
+ return getSubscriptionMatchingAPNList(qb, projectionIn, selection, selectionArgs,
+ sort, subId);
+ }
+
+ case URL_SIM_APN_LIST_FILTERED_ID: {
+ subIdString = url.getLastPathSegment();
+ try {
+ subId = Integer.parseInt(subIdString);
+ } catch (NumberFormatException e) {
+ loge("NumberFormatException" + e);
+ return null;
+ }
+ }
+ //intentional fall through from above case
+ case URL_SIM_APN_LIST_FILTERED: {
+ if (isManagedApnEnforced()) {
+ // If enforced, return DPC records only.
+ qb.appendWhereStandalone(IS_OWNED_BY_DPC);
+ } else {
+ // Otherwise return non-DPC records only.
+ qb.appendWhereStandalone(IS_NOT_OWNED_BY_DPC);
+ }
+ return getSubscriptionMatchingAPNList(qb, projectionIn, selection, selectionArgs,
+ sort, subId);
+ }
default: {
return null;
@@ -2510,26 +2871,6 @@
qb.appendWhere(TextUtils.join(" AND ", constraints));
}
- if (match != URL_SIMINFO) {
- if (projectionIn != null) {
- for (String column : projectionIn) {
- if (TYPE.equals(column) ||
- MMSC.equals(column) ||
- MMSPROXY.equals(column) ||
- MMSPORT.equals(column) ||
- APN.equals(column)) {
- // noop
- } else {
- checkPermission();
- break;
- }
- }
- } else {
- // null returns all columns, so need permission check
- checkPermission();
- }
- }
-
SQLiteDatabase db = getReadableDatabase();
Cursor ret = null;
try {
@@ -2555,6 +2896,110 @@
return ret;
}
+ private void checkQueryPermission(int match, String[] projectionIn) {
+ if (match != URL_SIMINFO) {
+ if (projectionIn != null) {
+ for (String column : projectionIn) {
+ if (TYPE.equals(column) ||
+ MMSC.equals(column) ||
+ MMSPROXY.equals(column) ||
+ MMSPORT.equals(column) ||
+ MVNO_TYPE.equals(column) ||
+ MVNO_MATCH_DATA.equals(column) ||
+ APN.equals(column)) {
+ // noop
+ } else {
+ checkPermission();
+ break;
+ }
+ }
+ } else {
+ // null returns all columns, so need permission check
+ checkPermission();
+ }
+ }
+ }
+
+ /**
+ * To find the current sim APN. Query APN based on {MCC, MNC, MVNO} to support backward
+ * compatibility but will move to carrier id in the future.
+ *
+ * There has three steps:
+ * 1. Query the APN based on { MCC, MNC, MVNO }.
+ * 2. If can't find the current APN, then query the parent APN. Query based on { MCC, MNC }.
+ * 3. else return empty cursor
+ *
+ */
+ private Cursor getSubscriptionMatchingAPNList(SQLiteQueryBuilder qb, String[] projectionIn,
+ String selection, String[] selectionArgs, String sort, int subId) {
+ Cursor ret;
+ final TelephonyManager tm = ((TelephonyManager)
+ getContext().getSystemService(Context.TELEPHONY_SERVICE))
+ .createForSubscriptionId(subId);
+ SQLiteDatabase db = getReadableDatabase();
+ String mccmnc = tm.getSimOperator();
+
+ // For query db one time, append step 1 and step 2 condition in one selection and
+ // separate results after the query is completed. Because IMSI has special match rule,
+ // so just query the MCC / MNC and filter the MVNO by ourselves
+ qb.appendWhereStandalone(NUMERIC + " = '" + mccmnc + "' ");
+
+ ret = qb.query(db, null, selection, selectionArgs, null, null, sort);
+ if (ret == null) {
+ loge("query current APN but cursor is null.");
+ return null;
+ }
+
+ if (DBG) log("match current APN size: " + ret.getCount());
+
+ String[] coulmnNames = projectionIn != null ? projectionIn : ret.getColumnNames();
+ MatrixCursor currentCursor = new MatrixCursor(coulmnNames);
+ MatrixCursor parentCursor = new MatrixCursor(coulmnNames);
+
+ int numericIndex = ret.getColumnIndex(NUMERIC);
+ int mvnoIndex = ret.getColumnIndex(MVNO_TYPE);
+ int mvnoDataIndex = ret.getColumnIndex(MVNO_MATCH_DATA);
+
+ IccRecords iccRecords = UiccController.getInstance().getIccRecords(
+ SubscriptionManager.getPhoneId(subId), UiccController.APP_FAM_3GPP);
+ if (iccRecords == null) {
+ loge("iccRecords is null");
+ return null;
+ }
+
+ //Separate the result into MatrixCursor
+ while (ret.moveToNext()) {
+ List<String> data = new ArrayList<>();
+ for (String column : coulmnNames) {
+ data.add(ret.getString(ret.getColumnIndex(column)));
+ }
+
+ if (!TextUtils.isEmpty(ret.getString(numericIndex)) &&
+ ApnSettingUtils.mvnoMatches(iccRecords,
+ ApnSetting.getMvnoTypeIntFromString(ret.getString(mvnoIndex)),
+ ret.getString(mvnoDataIndex))) {
+ // 1. APN query result based on legacy SIM MCC/MCC and MVNO
+ currentCursor.addRow(data);
+ } else if (!TextUtils.isEmpty(ret.getString(numericIndex)) &&
+ TextUtils.isEmpty(ret.getString(mvnoIndex))) {
+ // 2. APN query result based on SIM MCC/MNC
+ parentCursor.addRow(data);
+ }
+ }
+ ret.close();
+
+ if (currentCursor.getCount() > 0) {
+ if (DBG) log("match Carrier Id APN: " + currentCursor.getCount());
+ return currentCursor;
+ } else if (parentCursor.getCount() > 0) {
+ if (DBG) log("match MNO Carrier ID APN: " + parentCursor.getCount());
+ return parentCursor;
+ } else {
+ if (DBG) log("APN no match");
+ return new MatrixCursor(coulmnNames);
+ }
+ }
+
@Override
public String getType(Uri url)
{
@@ -2565,6 +3010,7 @@
case URL_ID:
case URL_FILTERED_ID:
+ case URL_FILTERED_USING_SUBID:
return "vnd.android.cursor.item/telephony-carrier";
case URL_PREFERAPN_USING_SUBID:
@@ -2693,8 +3139,8 @@
}
values = DatabaseHelper.setDefaultValue(values);
- if (!values.containsKey(EDITED)) {
- values.put(EDITED, CARRIER_EDITED);
+ if (!values.containsKey(EDITED_STATUS)) {
+ values.put(EDITED_STATUS, CARRIER_EDITED);
}
// Owned_by should be others if inserted via general uri.
values.put(OWNED_BY, OWNED_BY_OTHERS);
@@ -2812,7 +3258,7 @@
IS_NOT_CARRIER_EDITED + ")";
String unedited = ") and " + IS_UNEDITED;
ContentValues cv = new ContentValues();
- cv.put(EDITED, USER_DELETED);
+ cv.put(EDITED_STATUS, USER_DELETED);
checkPermission();
@@ -2999,8 +3445,8 @@
case URL_TELEPHONY:
{
- if (!values.containsKey(EDITED)) {
- values.put(EDITED, CARRIER_EDITED);
+ if (!values.containsKey(EDITED_STATUS)) {
+ values.put(EDITED_STATUS, CARRIER_EDITED);
}
// Replace on conflict so that if same APN is present in db with edited
@@ -3028,8 +3474,8 @@
case URL_CURRENT:
{
- if (!values.containsKey(EDITED)) {
- values.put(EDITED, CARRIER_EDITED);
+ if (!values.containsKey(EDITED_STATUS)) {
+ values.put(EDITED_STATUS, CARRIER_EDITED);
}
// Replace on conflict so that if same APN is present in db with edited
// as UNEDITED or USER/CARRIER_DELETED, it is replaced with
@@ -3047,8 +3493,8 @@
throw new UnsupportedOperationException(
"Cannot update URL " + url + " with a where clause");
}
- if (!values.containsKey(EDITED)) {
- values.put(EDITED, CARRIER_EDITED);
+ if (!values.containsKey(EDITED_STATUS)) {
+ values.put(EDITED_STATUS, CARRIER_EDITED);
}
try {
@@ -3126,6 +3572,24 @@
break;
}
+ case URL_SIMINFO_USING_SUBID:
+ String subIdString = url.getLastPathSegment();
+ try {
+ subId = Integer.parseInt(subIdString);
+ } catch (NumberFormatException e) {
+ loge("NumberFormatException" + e);
+ throw new IllegalArgumentException("Invalid subId " + url);
+ }
+ if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
+ if (where != null || whereArgs != null) {
+ throw new UnsupportedOperationException(
+ "Cannot update URL " + url + " with a where clause");
+ }
+ count = db.update(SIMINFO_TABLE, values, _ID + "=?",
+ new String[] { subIdString});
+ uriType = URL_SIMINFO_USING_SUBID;
+ break;
+
case URL_SIMINFO: {
count = db.update(SIMINFO_TABLE, values, where, whereArgs);
uriType = URL_SIMINFO;
@@ -3138,10 +3602,52 @@
}
if (count > 0) {
+ boolean usingSubId = false;
switch (uriType) {
+ case URL_SIMINFO_USING_SUBID:
+ usingSubId = true;
+ // intentional fall through from above case
case URL_SIMINFO:
+ // skip notifying descendant URLs to avoid unneccessary wake up.
+ // If not set, any change to SIMINFO will notify observers which listens to
+ // specific field of SIMINFO.
getContext().getContentResolver().notifyChange(
- SubscriptionManager.CONTENT_URI, null, true, UserHandle.USER_ALL);
+ SubscriptionManager.CONTENT_URI, null,
+ ContentResolver.NOTIFY_SYNC_TO_NETWORK
+ | ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS,
+ UserHandle.USER_ALL);
+ // notify observers on specific user settings changes.
+ if (values.containsKey(SubscriptionManager.WFC_IMS_ENABLED)) {
+ getContext().getContentResolver().notifyChange(
+ getNotifyContentUri(SubscriptionManager.WFC_ENABLED_CONTENT_URI,
+ usingSubId, subId), null, true, UserHandle.USER_ALL);
+ }
+ if (values.containsKey(SubscriptionManager.ENHANCED_4G_MODE_ENABLED)) {
+ getContext().getContentResolver().notifyChange(
+ getNotifyContentUri(SubscriptionManager
+ .ADVANCED_CALLING_ENABLED_CONTENT_URI,
+ usingSubId, subId), null, true, UserHandle.USER_ALL);
+ }
+ if (values.containsKey(SubscriptionManager.VT_IMS_ENABLED)) {
+ getContext().getContentResolver().notifyChange(
+ getNotifyContentUri(SubscriptionManager.VT_ENABLED_CONTENT_URI,
+ usingSubId, subId), null, true, UserHandle.USER_ALL);
+ }
+ if (values.containsKey(SubscriptionManager.WFC_IMS_MODE)) {
+ getContext().getContentResolver().notifyChange(
+ getNotifyContentUri(SubscriptionManager.WFC_MODE_CONTENT_URI,
+ usingSubId, subId), null, true, UserHandle.USER_ALL);
+ }
+ if (values.containsKey(SubscriptionManager.WFC_IMS_ROAMING_MODE)) {
+ getContext().getContentResolver().notifyChange(getNotifyContentUri(
+ SubscriptionManager.WFC_ROAMING_MODE_CONTENT_URI,
+ usingSubId, subId), null, true, UserHandle.USER_ALL);
+ }
+ if (values.containsKey(SubscriptionManager.WFC_IMS_ROAMING_ENABLED)) {
+ getContext().getContentResolver().notifyChange(getNotifyContentUri(
+ SubscriptionManager.WFC_ROAMING_ENABLED_CONTENT_URI,
+ usingSubId, subId), null, true, UserHandle.USER_ALL);
+ }
break;
default:
getContext().getContentResolver().notifyChange(
@@ -3152,6 +3658,10 @@
return count;
}
+ private static Uri getNotifyContentUri(Uri uri, boolean usingSubId, int subId) {
+ return (usingSubId) ? Uri.withAppendedPath(uri, "" + subId) : uri;
+ }
+
private void checkPermission() {
int status = getContext().checkCallingOrSelfPermission(
"android.permission.WRITE_APN_SETTINGS");
@@ -3165,7 +3675,7 @@
TelephonyManager telephonyManager =
(TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
for (String pkg : packages) {
- if (telephonyManager.checkCarrierPrivilegesForPackage(pkg) ==
+ if (telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(pkg) ==
TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
return;
}
@@ -3209,7 +3719,7 @@
editorApn.apply();
if (apnSourceServiceExists(getContext())) {
- restoreApnsWithService();
+ restoreApnsWithService(subId);
} else {
initDatabaseWithDatabaseHelper(db);
}
@@ -3233,7 +3743,8 @@
String mvnoType = cursor.getString(0 /* MVNO_TYPE index */);
String mvnoMatchData = cursor.getString(1 /* MVNO_MATCH_DATA index */);
if (!TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData)
- && ApnSetting.mvnoMatches(iccRecords, mvnoType, mvnoMatchData)) {
+ && ApnSettingUtils.mvnoMatches(iccRecords,
+ ApnSetting.getMvnoTypeIntFromString(mvnoType), mvnoMatchData)) {
where = NUMERIC + "='" + simOperator + "'"
+ " AND " + MVNO_TYPE + "='" + mvnoType + "'"
+ " AND " + MVNO_MATCH_DATA + "='" + mvnoMatchData + "'"
@@ -3289,12 +3800,60 @@
initDatabaseWithDatabaseHelper(db);
- // Notify listereners of DB change since DB has been updated
+ // Notify listeners of DB change since DB has been updated
getContext().getContentResolver().notifyChange(
CONTENT_URI, null, true, UserHandle.USER_ALL);
}
+ public static void fillInMccMncStringAtCursor(Context context, SQLiteDatabase db, Cursor c) {
+ int mcc, mnc;
+ String subId;
+ try {
+ mcc = c.getInt(c.getColumnIndexOrThrow(SubscriptionManager.MCC));
+ mnc = c.getInt(c.getColumnIndexOrThrow(SubscriptionManager.MNC));
+ subId = c.getString(c.getColumnIndexOrThrow(
+ SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Possible database corruption -- some columns not found.");
+ return;
+ }
+
+ String mccString = String.format(Locale.getDefault(), "%03d", mcc);
+ String mncString = getBestStringMnc(context, mccString, mnc);
+ ContentValues cv = new ContentValues(2);
+ cv.put(SubscriptionManager.MCC_STRING, mccString);
+ cv.put(SubscriptionManager.MNC_STRING, mncString);
+ db.update(SIMINFO_TABLE, cv,
+ SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
+ new String[]{subId});
+ }
+
+ /*
+ * Find the best string-form mnc by looking up possibilities in the carrier id db.
+ * Default to the three-digit version if neither/both are valid.
+ */
+ private static String getBestStringMnc(Context context, String mcc, int mnc) {
+ if (mnc >= 100 && mnc <= 999) {
+ return String.valueOf(mnc);
+ }
+ String twoDigitMnc = String.format(Locale.getDefault(), "%02d", mnc);
+ String threeDigitMnc = "0" + twoDigitMnc;
+
+ try (
+ Cursor twoDigitMncCursor = context.getContentResolver().query(
+ Telephony.CarrierId.All.CONTENT_URI,
+ /* projection */ null,
+ /* selection */ Telephony.CarrierId.All.MCCMNC + "=?",
+ /* selectionArgs */ new String[]{mcc + twoDigitMnc}, null)
+ ) {
+ if (twoDigitMncCursor.getCount() > 0) {
+ return twoDigitMnc;
+ }
+ return threeDigitMnc;
+ }
+ }
+
/**
* Sync the bearer bitmask and network type bitmask when inserting and updating.
* Since bearerBitmask is deprecating, map the networkTypeBitmask to bearerBitmask if
diff --git a/tests/Android.bp b/tests/Android.bp
new file mode 100644
index 0000000..801a40c
--- /dev/null
+++ b/tests/Android.bp
@@ -0,0 +1,20 @@
+android_test {
+ name: "TelephonyProviderTests",
+ static_libs: [
+ "mockito-target",
+ "compatibility-device-util",
+ "android-support-test",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.runner",
+ "telephony-common",
+ "android.test.base",
+ "android.test.mock",
+ ],
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ certificate: "platform",
+ instrumentation_for: "TelephonyProvider",
+}
diff --git a/tests/Android.mk b/tests/Android.mk
deleted file mode 100644
index 14f528f..0000000
--- a/tests/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target \
- compatibility-device-util \
- android-support-test
-
-LOCAL_JAVA_LIBRARIES := \
- android.test.runner \
- telephony-common \
- android.test.base \
- android.test.mock \
-
-
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-
-LOCAL_PACKAGE_NAME := TelephonyProviderTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-
-LOCAL_INSTRUMENTATION_FOR := TelephonyProvider
-
-include $(BUILD_PACKAGE)
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index 129f4d9..ad703de 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -22,7 +22,7 @@
<option name="test-tag" value="TelephonyProviderTests" />
<test class="com.android.tradefed.testtype.InstrumentationTest" >
<option name="package" value="com.android.providers.telephony.tests" />
- <option name="runner" value="android.test.InstrumentationTestRunner" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java b/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java
index 4adcf43..e5dd6e6 100644
--- a/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java
+++ b/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java
@@ -68,6 +68,8 @@
private static final String dummy_apn = "APN_DUMMY";
private static final String dummy_iccid_prefix = "ICCID_PREFIX_DUMMY";
private static final String dummy_name = "NAME_DUMMY";
+ private static final String dummy_access_rule =
+ "B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51465350";
private static final int dummy_cid = 0;
private MockContextWithProvider mContext;
@@ -104,14 +106,10 @@
private class MockContextWithProvider extends MockContext {
private final MockContentResolver mResolver;
- public MockContextWithProvider(CarrierIdProvider carrierIdProvider) {
+ public MockContextWithProvider(CarrierIdProviderTestable carrierIdProvider) {
mResolver = new FakeContentResolver();
- ProviderInfo providerInfo = new ProviderInfo();
- providerInfo.authority = CarrierIdProvider.AUTHORITY;
-
- // Add context to given carrierIdProvider
- carrierIdProvider.attachInfoForTesting(this, providerInfo);
+ carrierIdProvider.initializeForTesting(this);
Log.d(TAG, "MockContextWithProvider: carrierIdProvider.getContext(): "
+ carrierIdProvider.getContext());
@@ -437,6 +435,7 @@
contentValues.put(CarrierId.All.ICCID_PREFIX, dummy_iccid_prefix);
contentValues.put(CarrierId.CARRIER_NAME, dummy_name);
contentValues.put(CarrierId.CARRIER_ID, dummy_cid);
+ contentValues.put(CarrierId.All.PRIVILEGE_ACCESS_RULE, dummy_access_rule);
return contentValues;
}
}
diff --git a/tests/src/com/android/providers/telephony/CarrierIdProviderTestable.java b/tests/src/com/android/providers/telephony/CarrierIdProviderTestable.java
index 8468801..9ecd93a 100644
--- a/tests/src/com/android/providers/telephony/CarrierIdProviderTestable.java
+++ b/tests/src/com/android/providers/telephony/CarrierIdProviderTestable.java
@@ -15,6 +15,8 @@
*/
package com.android.providers.telephony;
+import android.content.Context;
+import android.content.pm.ProviderInfo;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
@@ -51,6 +53,14 @@
return mDbHelper.getWritableDatabase();
}
+ void initializeForTesting(Context context) {
+ ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.authority = CarrierIdProvider.AUTHORITY;
+
+ // Add context to given carrierIdProvider
+ attachInfoForTesting(context, providerInfo);
+ }
+
/**
* An in memory DB for CarrierIdProviderTestable to use
*/
diff --git a/tests/src/com/android/providers/telephony/RcsProviderDeleteTest.java b/tests/src/com/android/providers/telephony/RcsProviderDeleteTest.java
new file mode 100644
index 0000000..77a5a70
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/RcsProviderDeleteTest.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2018 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.providers.telephony;
+
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.GROUP_NAME_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.OWNER_PARTICIPANT_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.CANONICAL_ADDRESS_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_ALIAS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantEventColumns.NEW_ALIAS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.NEW_NAME_COLUMN;
+
+import static com.android.providers.telephony.RcsProvider.RCS_MESSAGE_TABLE;
+import static com.android.providers.telephony.RcsProviderHelper.setup1To1Thread;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsProviderDeleteTest {
+ private MockContentResolver mContentResolver;
+ private RcsProviderTestable mRcsProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRcsProvider = new RcsProviderTestable();
+ RcsProviderTestable.MockContextWithProvider
+ context = new RcsProviderTestable.MockContextWithProvider(mRcsProvider);
+ mContentResolver = context.getContentResolver();
+
+ // insert a participant
+ // first into the MmsSmsProvider
+ mRcsProvider.getWritableDatabase().execSQL(
+ "INSERT INTO canonical_addresses VALUES (1, \"+15551234567\")");
+
+ // then into the RcsProvider
+ ContentValues participantValues = new ContentValues();
+ participantValues.put(RCS_ALIAS_COLUMN, "Bob");
+ participantValues.put(CANONICAL_ADDRESS_ID_COLUMN, 1);
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/participant"),
+ participantValues)).isEqualTo(Uri.parse("content://rcs/participant/1"));
+
+ setup1To1Thread(mContentResolver);
+
+ // insert one group thread
+ ContentValues groupContentValues = new ContentValues();
+ groupContentValues.put(OWNER_PARTICIPANT_COLUMN, 1);
+ groupContentValues.put(GROUP_NAME_COLUMN, "name");
+ Uri groupThreadUri = Uri.parse("content://rcs/group_thread");
+ assertThat(mContentResolver.insert(groupThreadUri, groupContentValues)).isEqualTo(
+ Uri.parse("content://rcs/group_thread/2"));
+
+ Uri addParticipantToGroupThread = Uri.parse("content://rcs/group_thread/2/participant/1");
+ assertThat(mContentResolver.insert(addParticipantToGroupThread, null)).isEqualTo(
+ addParticipantToGroupThread);
+
+ // add incoming and outgoing messages to both threads
+ ContentValues messageValues = new ContentValues();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/p2p_thread/1/incoming_message"),
+ messageValues)).isEqualTo(
+ Uri.parse("content://rcs/p2p_thread/1/incoming_message/1"));
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/p2p_thread/1/outgoing_message"),
+ messageValues)).isEqualTo(
+ Uri.parse("content://rcs/p2p_thread/1/outgoing_message/2"));
+ assertThat(
+ mContentResolver.insert(Uri.parse("content://rcs/group_thread/2/incoming_message"),
+ messageValues)).isEqualTo(
+ Uri.parse("content://rcs/group_thread/2/incoming_message/3"));
+ assertThat(
+ mContentResolver.insert(Uri.parse("content://rcs/group_thread/2/outgoing_message"),
+ messageValues)).isEqualTo(
+ Uri.parse("content://rcs/group_thread/2/outgoing_message/4"));
+
+ // add a file transfer to a message
+ ContentValues fileTransferValues = new ContentValues();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/message/3/file_transfer"),
+ fileTransferValues)).isEqualTo(Uri.parse("content://rcs/file_transfer/1"));
+
+ // insert an alias change event
+ ContentValues eventValues = new ContentValues();
+ eventValues.put(NEW_ALIAS_COLUMN, "new alias");
+ assertThat(
+ mContentResolver.insert(Uri.parse("content://rcs/participant/1/alias_change_event"),
+ eventValues)).isEqualTo(Uri.parse(
+ "content://rcs/participant/1/alias_change_event/1"));
+
+ // create a group name change event
+ eventValues.clear();
+ eventValues.put(NEW_NAME_COLUMN, "new name");
+ assertThat(mContentResolver.insert(
+ Uri.parse("content://rcs/group_thread/2/name_changed_event"),
+ eventValues)).isEqualTo(Uri.parse(
+ "content://rcs/group_thread/2/name_changed_event/1"));
+ }
+
+ @After
+ public void tearDown() {
+ mRcsProvider.tearDown();
+ }
+
+ @Test
+ public void testDelete1To1ThreadWithId() {
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/p2p_thread/1"), null,
+ null)).isEqualTo(1);
+ assertDeletionViaQuery("content://rcs/p2p_thread/1");
+ }
+
+ @Test
+ public void testDelete1To1ThreadWithSelection() {
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/p2p_thread"),
+ "rcs_fallback_thread_id=1", null)).isEqualTo(1);
+ assertDeletionViaQuery("content://rcs/p2p_thread/1");
+ }
+
+ @Test
+ public void testDeleteGroupThreadWithId() {
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/group_thread/2"), null,
+ null)).isEqualTo(1);
+ assertDeletionViaQuery("content://rcs/group_thread/2");
+ }
+
+ @Test
+ public void testDeleteGroupThreadWithSelection() {
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/group_thread"),
+ "group_name=\"name\"", null)).isEqualTo(1);
+ assertDeletionViaQuery("content://rcs/group_thread/1");
+ }
+
+ @Test
+ public void testDeleteParticipantWithIdWhileParticipatingInAThread() {
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/participant/1"), null,
+ null)).isEqualTo(0);
+ }
+
+ @Test
+ public void testDeleteParticipantAfterLeavingThreads() {
+ // leave the first thread
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/group_thread/2/participant/1"),
+ null, null)).isEqualTo(1);
+
+ // try deleting the participant. It should fail
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/participant/1"), null,
+ null)).isEqualTo(0);
+
+ // delete the p2p thread
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/p2p_thread/1"), null,
+ null)).isEqualTo(1);
+
+ // try deleting the participant. It should succeed
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/participant/1"), null,
+ null)).isEqualTo(1);
+ assertDeletionViaQuery("content://rcs/participant/1");
+ }
+
+ @Test
+ public void testDeleteParticipantWithSelectionFails() {
+ assertThat(
+ mContentResolver.delete(Uri.parse("content://rcs/participant"), "rcs_alias=\"Bob\"",
+ null)).isEqualTo(0);
+ }
+
+ @Test
+ public void testDeleteParticipantFrom1To1ThreadFails() {
+ assertThat(
+ mContentResolver.delete(Uri.parse("content://rcs/p2p_thread/1/participant/1"), null,
+ null)).isEqualTo(0);
+ }
+
+ @Test
+ public void testDeleteParticipantFromGroupThread() {
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/group_thread/2/participant/1"),
+ null, null)).isEqualTo(1);
+ assertDeletionViaQuery("content://rcs/group_thread/2/participant");
+ }
+
+ @Test
+ public void testDeleteParticipantFromGroupThreadWithSelectionFails() {
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/group_thread/2/participant"),
+ "rcs_alias=?", new String[]{"Bob"})).isEqualTo(0);
+ }
+
+ @Test
+ public void testDeleteMessagesUsingUnifiedMessageViewFails() {
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/message/1"), null,
+ null)).isEqualTo(0);
+ }
+
+ @Test
+ public void testDeleteMessagesUsingThreadUrisFails() {
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/p2p_thread/1/message/1"), null,
+ null)).isEqualTo(0);
+ assertThat(
+ mContentResolver.delete(Uri.parse("content://rcs/p2p_thread/1/incoming_message/1"),
+ null, null)).isEqualTo(0);
+ }
+
+ @Test
+ @Ignore // TODO: fix and un-ignore
+ public void testDeleteMessage() {
+ // verify there exists 4 messages
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/message"), null, null,
+ null);
+ assertThat(cursor.getCount()).isEqualTo(4);
+ cursor.close();
+
+ // delete 2 of them
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/incoming_message/1"), null,
+ null)).isEqualTo(1);
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/outgoing_message/4"), null,
+ null)).isEqualTo(1);
+
+ // verify that only 2 messages are left
+ cursor = mContentResolver.query(Uri.parse("content://rcs/message"), null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(2);
+ cursor.close();
+
+ // verify that entries in common table is deleted and only messages with id's 2 and 3 remain
+ SQLiteDatabase db = mRcsProvider.getWritableDatabase();
+ cursor = db.query(RCS_MESSAGE_TABLE, null, null, null, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(2);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(0)).isEqualTo(2);
+ cursor.moveToNext();
+ assertThat(cursor.getInt(0)).isEqualTo(3);
+ cursor.close();
+ }
+
+ @Test
+ public void testDeleteFileTransfer() {
+ assertThat(mContentResolver.delete(Uri.parse("content://rcs/file_transfer/1"), null,
+ null)).isEqualTo(1);
+ assertDeletionViaQuery("content://rcs/file_transfer/1");
+ }
+
+ @Test
+ public void testDeleteParticipantEvent() {
+ assertThat(mContentResolver.delete(Uri.parse(
+ "content://rcs/participant/1/alias_change_event/1"), null, null)).isEqualTo(1);
+
+ // try deleting again and verify nothing is deleted
+ // TODO - convert to query once querying is in place
+ assertThat(mContentResolver.delete(Uri.parse(
+ "content://rcs/participant/1/alias_change_event/1"), null, null)).isEqualTo(0);
+ }
+
+ @Test
+ public void testDeleteGroupThreadEvent() {
+ assertThat(mContentResolver.delete(Uri.parse(
+ "content://rcs/group_thread/2/name_changed_event/1"), null, null)).isEqualTo(1);
+
+ // try deleting again and verify nothing is deleted
+ // TODO - convert to query once querying is in place
+ assertThat(mContentResolver.delete(Uri.parse(
+ "content://rcs/group_thread/2/name_changed_event/1"), null, null)).isEqualTo(0);
+ }
+
+ private void assertDeletionViaQuery(String queryUri) {
+ Cursor cursor = mContentResolver.query(Uri.parse(queryUri), null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(0);
+ cursor.close();
+ }
+}
diff --git a/tests/src/com/android/providers/telephony/RcsProviderHelper.java b/tests/src/com/android/providers/telephony/RcsProviderHelper.java
new file mode 100644
index 0000000..5a7f9a0
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/RcsProviderHelper.java
@@ -0,0 +1,55 @@
+/*
+ * 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.providers.telephony;
+
+import static android.provider.Telephony.RcsColumns.Rcs1To1ThreadColumns.FALLBACK_THREAD_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.net.Uri;
+
+class RcsProviderHelper {
+ /**
+ * Sets up a 1-to-1 thread with a participant ID of 1 and a fallback thread ID of 1.
+ *
+ * @return the {@link Uri} of the newly inserted row
+ */
+ static Uri setup1To1Thread(ContentResolver contentResolver) {
+ return setup1To1Thread(contentResolver, 1, 1);
+ }
+
+ static Uri setup1To1Thread(
+ ContentResolver contentResolver, int participantId, int fallbackThreadId) {
+ ContentValues insertValues = new ContentValues();
+ Uri insertionUri = Uri.parse("content://rcs/p2p_thread");
+ insertValues.put(RCS_PARTICIPANT_ID_COLUMN, participantId);
+
+ Uri rowUri = contentResolver.insert(insertionUri, insertValues);
+ assertThat(rowUri).isNotNull();
+
+ ContentValues updateValues = new ContentValues();
+ updateValues.put(FALLBACK_THREAD_ID_COLUMN, fallbackThreadId);
+ assertThat(contentResolver.update(rowUri, updateValues, null, null))
+ .isEqualTo(1);
+
+ return rowUri;
+ }
+
+}
diff --git a/tests/src/com/android/providers/telephony/RcsProviderInsertTest.java b/tests/src/com/android/providers/telephony/RcsProviderInsertTest.java
new file mode 100644
index 0000000..7e63d7f
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/RcsProviderInsertTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2018 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.providers.telephony;
+
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.CONFERENCE_URI_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.GROUP_ICON_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.GROUP_NAME_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.GLOBAL_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.CANONICAL_ADDRESS_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_ALIAS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantEventColumns.NEW_ALIAS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.NEW_NAME_COLUMN;
+
+import static com.android.providers.telephony.RcsProviderHelper.setup1To1Thread;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsProviderInsertTest {
+ private MockContentResolver mContentResolver;
+ private RcsProviderTestable mRcsProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRcsProvider = new RcsProviderTestable();
+ RcsProviderTestable.MockContextWithProvider
+ context = new RcsProviderTestable.MockContextWithProvider(mRcsProvider);
+ mContentResolver = context.getContentResolver();
+ }
+
+ @After
+ public void tearDown() {
+ mRcsProvider.tearDown();
+ }
+
+ @Test
+ public void testInsertUnifiedThreadFails() {
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/thread"), null)).isNull();
+ }
+
+ @Test
+ public void testDuplicate1To1ThreadInsertion() {
+ Uri uri = setup1To1Thread(mContentResolver);
+
+ assertThat(mContentResolver.insert(uri, null)).isNull();
+ }
+
+ @Test
+ public void testInsertGroupThread() {
+ ContentValues contentValues = new ContentValues(3);
+ contentValues.put(CONFERENCE_URI_COLUMN, "conference uri");
+ contentValues.put(GROUP_NAME_COLUMN, "group name");
+ contentValues.put(GROUP_ICON_COLUMN, "groupIcon");
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/group_thread"),
+ contentValues)).isEqualTo(Uri.parse("content://rcs/group_thread/1"));
+ }
+
+ @Test
+ public void testInsertParticipant() {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(CANONICAL_ADDRESS_ID_COLUMN, 6);
+ contentValues.put(RCS_ALIAS_COLUMN, "Alias");
+
+ Uri uri = mContentResolver.insert(Uri.parse("content://rcs/participant"), contentValues);
+ assertThat(uri).isEqualTo(Uri.parse("content://rcs/participant/1"));
+ }
+
+ @Test
+ public void testInsertParticipantIntoGroupThread() {
+ // create a participant
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(CANONICAL_ADDRESS_ID_COLUMN, 23);
+ mContentResolver.insert(Uri.parse("content://rcs/participant"), contentValues);
+
+ // create a thread
+ ContentValues values = new ContentValues(1);
+ values.put(GROUP_NAME_COLUMN, "Group");
+ mContentResolver.insert(Uri.parse("content://rcs/group_thread"), values);
+
+ // add participant to the thread
+ Uri uri = Uri.parse("content://rcs/group_thread/1/participant/1");
+ assertThat(mContentResolver.insert(uri, null)).isEqualTo(uri);
+
+ // assert that adding again fails
+ assertThat(mContentResolver.insert(uri, null)).isNull();
+ }
+
+ @Test
+ public void testInsertMessageFails() {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(GLOBAL_ID_COLUMN, "global RCS id");
+
+ // try inserting messages without threads
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/message"),
+ contentValues)).isNull();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/message/6"),
+ contentValues)).isNull();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/incoming_message"),
+ contentValues)).isNull();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/incoming_message/12"),
+ contentValues)).isNull();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/outgoing_message"),
+ contentValues)).isNull();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/outgoing_message/18"),
+ contentValues)).isNull();
+
+ // try inserting into unified thread view
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/thread/5/incoming_message"),
+ contentValues)).isNull();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/thread/5/outgoing_message"),
+ contentValues)).isNull();
+ }
+
+ @Test
+ @Ignore // TODO: fix and un-ignore
+ public void testInsertMessageIntoThread() {
+ // create two threads
+ setup1To1Thread(mContentResolver);
+ ContentValues values = new ContentValues();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/group_thread"),
+ values)).isNotNull();
+
+ // add messages to threads
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/p2p_thread/1/incoming_message"),
+ values)).isEqualTo(Uri.parse("content://rcs/p2p_thread/1/incoming_message/1"));
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/p2p_thread/1/outgoing_message"),
+ values)).isEqualTo(Uri.parse("content://rcs/p2p_thread/1/outgoing_message/2"));
+ assertThat(
+ mContentResolver.insert(Uri.parse("content://rcs/group_thread/2/incoming_message"),
+ values)).isEqualTo(
+ Uri.parse("content://rcs/group_thread/2/incoming_message/3"));
+ assertThat(
+ mContentResolver.insert(Uri.parse("content://rcs/group_thread/2/outgoing_message"),
+ values)).isEqualTo(
+ Uri.parse("content://rcs/group_thread/2/outgoing_message/4"));
+
+ // assert that they are accessible in messages table
+ Cursor messageCursor = mContentResolver.query(Uri.parse("content://rcs/message"), null,
+ null, null, null);
+ assertThat(messageCursor.getCount()).isEqualTo(4);
+ }
+
+ @Test
+ public void testInsertMessageDelivery() {
+ setup1To1Thread(mContentResolver);
+
+ ContentValues values = new ContentValues();
+
+ // add an outgoing message to the thread
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/p2p_thread/1/outgoing_message"),
+ values)).isEqualTo(Uri.parse("content://rcs/p2p_thread/1/outgoing_message/1"));
+
+ // add a delivery to the outgoing message
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/outgoing_message/1/delivery/1"),
+ values)).isEqualTo(Uri.parse("content://rcs/outgoing_message/1/delivery/1"));
+ }
+
+ @Test
+ public void testInsertFileTransfer() {
+ setup1To1Thread(mContentResolver);
+
+ ContentValues values = new ContentValues();
+
+ // add an outgoing message to the thread
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/p2p_thread/1/outgoing_message"),
+ values)).isEqualTo(Uri.parse("content://rcs/p2p_thread/1/outgoing_message/1"));
+
+ // add a file transfer to the message
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/message/1/file_transfer"),
+ values)).isEqualTo(Uri.parse("content://rcs/file_transfer/1"));
+ }
+
+ @Test
+ public void testInsertParticipantEvent() {
+ // create a participant
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(CANONICAL_ADDRESS_ID_COLUMN, 23);
+ mContentResolver.insert(Uri.parse("content://rcs/participant"), contentValues);
+
+ // insert an alias change event
+ ContentValues eventValues = new ContentValues();
+ eventValues.put(NEW_ALIAS_COLUMN, "new alias");
+ assertThat(
+ mContentResolver.insert(Uri.parse("content://rcs/participant/1/alias_change_event"),
+ eventValues)).isEqualTo(Uri.parse(
+ "content://rcs/participant/1/alias_change_event/1"));
+ }
+
+ @Test
+ public void testInsertGroupThreadEvent() {
+ // create a group thread
+ ContentValues contentValues = new ContentValues();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/group_thread"),
+ contentValues)).isEqualTo(Uri.parse("content://rcs/group_thread/1"));
+
+ // create a group name change event
+ ContentValues eventValues = new ContentValues();
+ eventValues.put(NEW_NAME_COLUMN, "new name");
+ assertThat(mContentResolver.insert(
+ Uri.parse("content://rcs/group_thread/1/name_changed_event"),
+ eventValues)).isEqualTo(Uri.parse(
+ "content://rcs/group_thread/1/name_changed_event/1"));
+ }
+}
diff --git a/tests/src/com/android/providers/telephony/RcsProviderQueryTest.java b/tests/src/com/android/providers/telephony/RcsProviderQueryTest.java
new file mode 100644
index 0000000..7b0ed62
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/RcsProviderQueryTest.java
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2018 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.providers.telephony;
+
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_JOINED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.FILE_SIZE_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.SESSION_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.GROUP_NAME_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.MESSAGE_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.MESSAGE_TEXT_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.MESSAGE_TYPE_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.ORIGINATION_TIMESTAMP_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.CANONICAL_ADDRESS_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_ALIAS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadColumns.RCS_THREAD_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.DESTINATION_PARTICIPANT_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.EVENT_TYPE_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.NEW_NAME_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.SOURCE_PARTICIPANT_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_COLUMN;
+import static android.telephony.ims.RcsEventQueryParams.EVENT_QUERY_PARAMETERS_KEY;
+import static android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_NAME_CHANGED_EVENT;
+import static android.telephony.ims.RcsMessageQueryParams.MESSAGE_QUERY_PARAMETERS_KEY;
+import static android.telephony.ims.RcsParticipantQueryParams.PARTICIPANT_QUERY_PARAMETERS_KEY;
+import static android.telephony.ims.RcsQueryContinuationToken.QUERY_CONTINUATION_TOKEN;
+import static android.telephony.ims.RcsThreadQueryParams.THREAD_QUERY_PARAMETERS_KEY;
+
+import static com.android.providers.telephony.RcsProviderHelper.setup1To1Thread;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsEventQueryParams;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsMessageQueryParams;
+import android.telephony.ims.RcsParticipantQueryParams;
+import android.telephony.ims.RcsQueryContinuationToken;
+import android.telephony.ims.RcsThreadQueryParams;
+import android.test.mock.MockContentResolver;
+
+import com.android.providers.telephony.RcsProviderTestable.MockContextWithProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsProviderQueryTest {
+ private MockContentResolver mContentResolver;
+ private RcsProviderTestable mRcsProvider;
+
+ private static final String GROUP_NAME = "group name";
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRcsProvider = new RcsProviderTestable();
+ MockContextWithProvider context = new MockContextWithProvider(mRcsProvider);
+ mContentResolver = context.getContentResolver();
+
+ // insert two participants
+ Uri participantUri = Uri.parse("content://rcs/participant");
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(CANONICAL_ADDRESS_ID_COLUMN, 99);
+ contentValues.put(RCS_ALIAS_COLUMN, "Some alias");
+ mContentResolver.insert(participantUri, contentValues);
+
+ contentValues.clear();
+ contentValues.put(CANONICAL_ADDRESS_ID_COLUMN, 100);
+ contentValues.put(RCS_ALIAS_COLUMN, "Some other alias");
+ mContentResolver.insert(participantUri, contentValues);
+
+ // insert two 1 to 1 threads
+ setup1To1Thread(mContentResolver, 1, 1);
+ setup1To1Thread(mContentResolver, 2, 2);
+
+ // insert one group thread
+ ContentValues groupContentValues = new ContentValues(1);
+ groupContentValues.put(GROUP_NAME_COLUMN, GROUP_NAME);
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/group_thread"),
+ groupContentValues)).isEqualTo(Uri.parse("content://rcs/group_thread/3"));
+
+ // put participants into the group
+ mContentResolver.insert(Uri.parse("content://rcs/group_thread/3/participant/1"), null);
+ mContentResolver.insert(Uri.parse("content://rcs/group_thread/3/participant/2"), null);
+
+ // insert two messages into first thread, leave the second one empty, insert one into group
+ // thread
+ ContentValues messageValues = new ContentValues();
+
+ messageValues.put(ORIGINATION_TIMESTAMP_COLUMN, 300);
+ messageValues.put(MESSAGE_TEXT_COLUMN, "Old message");
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/p2p_thread/1/incoming_message"),
+ messageValues)).isEqualTo(
+ Uri.parse("content://rcs/p2p_thread/1/incoming_message/1"));
+
+ messageValues.clear();
+ messageValues.put(ORIGINATION_TIMESTAMP_COLUMN, 400);
+ messageValues.put(MESSAGE_TEXT_COLUMN, "New message");
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/p2p_thread/1/outgoing_message"),
+ messageValues)).isEqualTo(
+ Uri.parse("content://rcs/p2p_thread/1/outgoing_message/2"));
+
+ messageValues.clear();
+ messageValues.put(ORIGINATION_TIMESTAMP_COLUMN, 200);
+ messageValues.put(MESSAGE_TEXT_COLUMN, "Group message");
+ assertThat(
+ mContentResolver.insert(Uri.parse("content://rcs/group_thread/3/incoming_message"),
+ messageValues)).isEqualTo(
+ Uri.parse("content://rcs/group_thread/3/incoming_message/3"));
+
+ // Add two events to the group thread
+ ContentValues eventValues = new ContentValues();
+ eventValues.put(NEW_NAME_COLUMN, "New group name");
+ assertThat(mContentResolver.insert(
+ Uri.parse("content://rcs/group_thread/3/name_changed_event"),
+ eventValues)).isEqualTo(
+ Uri.parse("content://rcs/group_thread/3/name_changed_event/1"));
+
+ eventValues.clear();
+ eventValues.put(SOURCE_PARTICIPANT_ID_COLUMN, 1);
+ eventValues.put(DESTINATION_PARTICIPANT_ID_COLUMN, 2);
+ assertThat(mContentResolver.insert(
+ Uri.parse("content://rcs/group_thread/3/participant_joined_event"),
+ eventValues)).isEqualTo(
+ Uri.parse("content://rcs/group_thread/3/participant_joined_event/2"));
+ }
+
+ @After
+ public void tearDown() {
+ mRcsProvider.tearDown();
+ }
+
+ @Test
+ public void testCanQueryUnifiedThreads() {
+ RcsThreadQueryParams queryParameters = new RcsThreadQueryParams.Builder().build();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(THREAD_QUERY_PARAMETERS_KEY, queryParameters);
+
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/thread"),
+ null, bundle, null);
+ assertThat(cursor.getCount()).isEqualTo(3);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(THREAD_TYPE_COLUMN))).isEqualTo(1);
+ assertThat(cursor.getString(cursor.getColumnIndex(GROUP_NAME_COLUMN))).isEqualTo(
+ GROUP_NAME);
+ assertThat(cursor.getInt(cursor.getColumnIndex(ORIGINATION_TIMESTAMP_COLUMN))).isEqualTo(
+ 200);
+ assertThat(cursor.getString(cursor.getColumnIndex(MESSAGE_TEXT_COLUMN))).isEqualTo(
+ "Group message");
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(THREAD_TYPE_COLUMN))).isEqualTo(0);
+ assertThat(cursor.getString(cursor.getColumnIndex(GROUP_NAME_COLUMN))).isEqualTo(null);
+ assertThat(cursor.getInt(cursor.getColumnIndex(ORIGINATION_TIMESTAMP_COLUMN))).isEqualTo(0);
+ assertThat(cursor.getString(cursor.getColumnIndex(MESSAGE_TEXT_COLUMN))).isEqualTo(null);
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(THREAD_TYPE_COLUMN))).isEqualTo(0);
+ assertThat(cursor.getString(cursor.getColumnIndex(GROUP_NAME_COLUMN))).isEqualTo(null);
+ assertThat(cursor.getInt(cursor.getColumnIndex(ORIGINATION_TIMESTAMP_COLUMN))).isEqualTo(
+ 400);
+ assertThat(cursor.getString(cursor.getColumnIndex(MESSAGE_TEXT_COLUMN))).isEqualTo(
+ "New message");
+ }
+
+ @Test
+ public void testCanQueryUnifiedThreadsWithLimitAndSorting() {
+ RcsThreadQueryParams queryParameters = new RcsThreadQueryParams.Builder()
+ .setThreadType(RcsThreadQueryParams.THREAD_TYPE_1_TO_1).setResultLimit(1)
+ .setSortProperty(RcsThreadQueryParams.SORT_BY_TIMESTAMP).setSortDirection(true)
+ .build();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(THREAD_QUERY_PARAMETERS_KEY, queryParameters);
+
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/thread"),
+ null, bundle, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(THREAD_TYPE_COLUMN))).isEqualTo(0);
+ assertThat(cursor.getString(cursor.getColumnIndex(GROUP_NAME_COLUMN))).isEqualTo(null);
+ assertThat(cursor.getInt(cursor.getColumnIndex(ORIGINATION_TIMESTAMP_COLUMN))).isEqualTo(0);
+ assertThat(cursor.getString(cursor.getColumnIndex(MESSAGE_TEXT_COLUMN))).isEqualTo(null);
+ }
+
+ @Test
+ public void testCanContinueThreadQuery() {
+ // Limit results to 1.
+ RcsThreadQueryParams queryParameters =
+ new RcsThreadQueryParams.Builder().setResultLimit(1).build();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(THREAD_QUERY_PARAMETERS_KEY, queryParameters);
+
+ // Perform an initial query, verify first thread is returned
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/thread"), null, bundle,
+ null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(THREAD_TYPE_COLUMN))).isEqualTo(1);
+ assertThat(cursor.getString(cursor.getColumnIndex(GROUP_NAME_COLUMN))).isEqualTo(
+ GROUP_NAME);
+ assertThat(cursor.getInt(cursor.getColumnIndex(ORIGINATION_TIMESTAMP_COLUMN))).isEqualTo(
+ 200);
+ assertThat(cursor.getString(cursor.getColumnIndex(MESSAGE_TEXT_COLUMN))).isEqualTo(
+ "Group message");
+
+ // Put the continuation token in the bundle to do a follow up query
+ RcsQueryContinuationToken continuationToken = cursor.getExtras().getParcelable(
+ QUERY_CONTINUATION_TOKEN);
+ bundle.clear();
+ bundle.putParcelable(QUERY_CONTINUATION_TOKEN, continuationToken);
+ cursor.close();
+
+ cursor = mContentResolver.query(Uri.parse("content://rcs/thread"), null, bundle,
+ null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(THREAD_TYPE_COLUMN))).isEqualTo(0);
+ assertThat(cursor.getString(cursor.getColumnIndex(GROUP_NAME_COLUMN))).isEqualTo(null);
+ assertThat(cursor.getInt(cursor.getColumnIndex(ORIGINATION_TIMESTAMP_COLUMN))).isEqualTo(0);
+ assertThat(cursor.getString(cursor.getColumnIndex(MESSAGE_TEXT_COLUMN))).isEqualTo(null);
+ cursor.close();
+
+ // Put the continuation token in the bundle to do a follow up query again, verify third
+ // thread is returned
+ continuationToken = cursor.getExtras().getParcelable(QUERY_CONTINUATION_TOKEN);
+ bundle.clear();
+ bundle.putParcelable(QUERY_CONTINUATION_TOKEN, continuationToken);
+ cursor = mContentResolver.query(Uri.parse("content://rcs/thread"), null, bundle,
+ null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(THREAD_TYPE_COLUMN))).isEqualTo(0);
+ assertThat(cursor.getString(cursor.getColumnIndex(GROUP_NAME_COLUMN))).isEqualTo(null);
+ assertThat(cursor.getInt(cursor.getColumnIndex(ORIGINATION_TIMESTAMP_COLUMN))).isEqualTo(
+ 400);
+ assertThat(cursor.getString(cursor.getColumnIndex(MESSAGE_TEXT_COLUMN))).isEqualTo(
+ "New message");
+ cursor.close();
+ }
+
+ @Test
+ public void testQuery1To1Threads() {
+ // verify two threads are returned in the query
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/p2p_thread"),
+ new String[]{RCS_THREAD_ID_COLUMN}, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(2);
+ }
+
+ @Test
+ public void testQueryGroupThreads() {
+ // verify one thread is returned in the query
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/group_thread"),
+ new String[]{GROUP_NAME_COLUMN}, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getString(0)).isEqualTo(GROUP_NAME);
+ }
+
+ @Test
+ public void testQueryParticipant() {
+ RcsParticipantQueryParams queryParameters = new RcsParticipantQueryParams.Builder()
+ .build();
+
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(PARTICIPANT_QUERY_PARAMETERS_KEY, queryParameters);
+
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/participant"), null, bundle,
+ null);
+ assertThat(cursor.getCount()).isEqualTo(2);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(RCS_PARTICIPANT_ID_COLUMN))).isEqualTo(2);
+ assertThat(cursor.getInt(cursor.getColumnIndex(CANONICAL_ADDRESS_ID_COLUMN))).isEqualTo(
+ 100);
+ assertThat(cursor.getString(cursor.getColumnIndex(RCS_ALIAS_COLUMN))).isEqualTo(
+ "Some other alias");
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(RCS_PARTICIPANT_ID_COLUMN))).isEqualTo(1);
+ assertThat(cursor.getInt(cursor.getColumnIndex(CANONICAL_ADDRESS_ID_COLUMN))).isEqualTo(99);
+ assertThat(cursor.getString(cursor.getColumnIndex(RCS_ALIAS_COLUMN))).isEqualTo(
+ "Some alias");
+ }
+
+ @Test
+ public void testQueryParticipantWithContinuation() {
+ Uri participantUri = Uri.parse("content://rcs/participant");
+
+ // Perform the initial query
+ RcsParticipantQueryParams queryParameters =
+ new RcsParticipantQueryParams.Builder().setAliasLike("%ali%").setSortProperty(
+ RcsParticipantQueryParams.SORT_BY_ALIAS).setSortDirection(true)
+ .setResultLimit(1).build();
+
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(PARTICIPANT_QUERY_PARAMETERS_KEY, queryParameters);
+
+ Cursor cursor = mContentResolver.query(participantUri, null, bundle, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(0)).isEqualTo(1);
+ assertThat(cursor.getInt(1)).isEqualTo(99);
+ assertThat(cursor.getString(2)).isEqualTo("Some alias");
+
+ // Perform the continuation query
+ RcsQueryContinuationToken continuationToken = cursor.getExtras().getParcelable(
+ QUERY_CONTINUATION_TOKEN);
+ bundle.clear();
+ bundle.putParcelable(QUERY_CONTINUATION_TOKEN, continuationToken);
+
+ cursor = mContentResolver.query(participantUri, null, bundle, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(0)).isEqualTo(2);
+ assertThat(cursor.getInt(1)).isEqualTo(100);
+ assertThat(cursor.getString(2)).isEqualTo("Some other alias");
+
+ // Perform the continuation query to verify no entries left
+ continuationToken = cursor.getExtras().getParcelable(QUERY_CONTINUATION_TOKEN);
+ bundle.clear();
+ bundle.putParcelable(QUERY_CONTINUATION_TOKEN, continuationToken);
+
+ cursor = mContentResolver.query(participantUri, null, bundle, null);
+ assertThat(cursor.getCount()).isEqualTo(0);
+ continuationToken = cursor.getExtras().getParcelable(QUERY_CONTINUATION_TOKEN);
+ assertThat(continuationToken).isNull();
+ }
+
+ @Test
+ public void testQueryGroupParticipants() {
+ // TODO - implement
+ }
+
+ @Test
+ @Ignore // TODO: fix and un-ignore
+ public void testQueryEvents() {
+ RcsEventQueryParams queryParameters = new RcsEventQueryParams.Builder().build();
+
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EVENT_QUERY_PARAMETERS_KEY, queryParameters);
+
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/event"), null, bundle,
+ null);
+ assertThat(cursor.getCount()).isEqualTo(2);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(EVENT_TYPE_COLUMN))).isEqualTo(
+ PARTICIPANT_JOINED_EVENT_TYPE);
+ assertThat(cursor.getInt(cursor.getColumnIndex(SOURCE_PARTICIPANT_ID_COLUMN))).isEqualTo(
+ 1);
+ assertThat(cursor.getInt(cursor.getColumnIndex(DESTINATION_PARTICIPANT_ID_COLUMN))).isEqualTo(
+ 2);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(EVENT_TYPE_COLUMN))).isEqualTo(
+ GROUP_THREAD_NAME_CHANGED_EVENT);
+ assertThat(cursor.getString(cursor.getColumnIndex(NEW_NAME_COLUMN))).isEqualTo(
+ "New group name");
+ }
+
+ @Test
+ @Ignore // TODO: fix and un-ignore
+ public void testQueryEventsWithContinuation() {
+ RcsEventQueryParams queryParameters =
+ new RcsEventQueryParams.Builder().setResultLimit(1).setSortDirection(true)
+ .build();
+
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EVENT_QUERY_PARAMETERS_KEY, queryParameters);
+
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/event"), null, bundle,
+ null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(EVENT_TYPE_COLUMN))).isEqualTo(
+ GROUP_THREAD_NAME_CHANGED_EVENT);
+ assertThat(cursor.getString(cursor.getColumnIndex(NEW_NAME_COLUMN))).isEqualTo(
+ "New group name");
+ }
+
+ @Test
+ @Ignore // TODO: fix and un-ignore
+ public void testQueryEventsWithTypeLimitation() {
+ RcsEventQueryParams queryParameters =
+ new RcsEventQueryParams.Builder().setEventType(
+ GROUP_THREAD_NAME_CHANGED_EVENT).build();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EVENT_QUERY_PARAMETERS_KEY, queryParameters);
+
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/event"), null, bundle,
+ null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(EVENT_TYPE_COLUMN))).isEqualTo(
+ GROUP_THREAD_NAME_CHANGED_EVENT);
+ assertThat(cursor.getString(cursor.getColumnIndex(NEW_NAME_COLUMN))).isEqualTo(
+ "New group name");
+ }
+
+ @Test
+ @Ignore // TODO: fix and un-ignore
+ public void testQueryMessages() {
+ RcsMessageQueryParams queryParameters = new RcsMessageQueryParams.Builder().build();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(MESSAGE_QUERY_PARAMETERS_KEY, queryParameters);
+
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/message"), null, bundle,
+ null);
+
+ assertThat(cursor.getCount()).isEqualTo(3);
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(MESSAGE_ID_COLUMN))).isEqualTo(3);
+ assertThat(cursor.getInt(cursor.getColumnIndex(RCS_THREAD_ID_COLUMN))).isEqualTo(3);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(MESSAGE_ID_COLUMN))).isEqualTo(2);
+ assertThat(cursor.getInt(cursor.getColumnIndex(RCS_THREAD_ID_COLUMN))).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(MESSAGE_ID_COLUMN))).isEqualTo(1);
+ assertThat(cursor.getInt(cursor.getColumnIndex(RCS_THREAD_ID_COLUMN))).isEqualTo(1);
+ }
+
+ @Test
+ @Ignore // TODO: fix and un-ignore
+ public void testQueryMessagesWithContinuation() {
+ RcsMessageQueryParams queryParameters =
+ new RcsMessageQueryParams.Builder().setMessageLike("%o%message").setResultLimit(
+ 1).setSortProperty(RcsMessageQueryParams.SORT_BY_TIMESTAMP)
+ .setSortDirection(true).build();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(MESSAGE_QUERY_PARAMETERS_KEY, queryParameters);
+
+ // Perform the initial query
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/message"), null, bundle,
+ null);
+
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(MESSAGE_ID_COLUMN))).isEqualTo(3);
+ assertThat(cursor.getInt(cursor.getColumnIndex(RCS_THREAD_ID_COLUMN))).isEqualTo(3);
+ assertThat(cursor.getInt(cursor.getColumnIndex(ORIGINATION_TIMESTAMP_COLUMN))).isEqualTo(
+ 200);
+ assertThat(cursor.getString(cursor.getColumnIndex(MESSAGE_TYPE_COLUMN))).isEqualTo(
+ "Group message");
+
+ // Perform the continuation query
+ RcsQueryContinuationToken continuationToken = cursor.getExtras().getParcelable(
+ QUERY_CONTINUATION_TOKEN);
+ assertThat(continuationToken).isNotNull();
+ bundle.clear();
+ bundle.putParcelable(QUERY_CONTINUATION_TOKEN, continuationToken);
+
+ cursor = mContentResolver.query(Uri.parse("content://rcs/message"), null, bundle, null);
+
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(MESSAGE_ID_COLUMN))).isEqualTo(1);
+ assertThat(cursor.getInt(cursor.getColumnIndex(RCS_THREAD_ID_COLUMN))).isEqualTo(1);
+ assertThat(cursor.getInt(cursor.getColumnIndex(ORIGINATION_TIMESTAMP_COLUMN))).isEqualTo(
+ 300);
+ assertThat(cursor.getString(cursor.getColumnIndex(MESSAGE_TEXT_COLUMN))).isEqualTo(
+ "Old message");
+ }
+
+ @Test
+ @Ignore // TODO: fix and un-ignore
+ public void testQueryMessagesWithThreadFilter() {
+ RcsMessageQueryParams queryParameters =
+ new RcsMessageQueryParams.Builder().setThread(new RcsGroupThread(3))
+ .build();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(MESSAGE_QUERY_PARAMETERS_KEY, queryParameters);
+
+ // Perform the initial query
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/message"), null, bundle,
+ null);
+
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+ assertThat(cursor.getInt(cursor.getColumnIndex(MESSAGE_ID_COLUMN))).isEqualTo(3);
+ assertThat(cursor.getInt(cursor.getColumnIndex(RCS_THREAD_ID_COLUMN))).isEqualTo(3);
+ assertThat(cursor.getInt(cursor.getColumnIndex(ORIGINATION_TIMESTAMP_COLUMN))).isEqualTo(
+ 200);
+ assertThat(cursor.getString(cursor.getColumnIndex(MESSAGE_TEXT_COLUMN))).isEqualTo(
+ "Group message");
+
+ }
+
+ @Test
+ public void testQueryParticipantOf1To1Thread() {
+ // query the participant back
+ Uri queryUri = Uri.parse("content://rcs/p2p_thread/1/participant");
+ Cursor cursor = mContentResolver.query(queryUri, null, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+
+ assertThat(cursor.getInt(1)).isEqualTo(99);
+ assertThat(cursor.getString(2)).isEqualTo("Some alias");
+ }
+
+ @Test
+ public void testQueryParticipantOfGroupThread() {
+ // query all the participants in this thread
+ Uri queryUri = Uri.parse("content://rcs/group_thread/3/participant");
+ Cursor cursor = mContentResolver.query(queryUri, null, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+
+ assertThat(cursor.getInt(cursor.getColumnIndex(RCS_PARTICIPANT_ID_COLUMN))).isEqualTo(1);
+ assertThat(cursor.getInt(cursor.getColumnIndex(CANONICAL_ADDRESS_ID_COLUMN))).isEqualTo(99);
+ assertThat(cursor.getString(cursor.getColumnIndex(RCS_ALIAS_COLUMN))).isEqualTo(
+ "Some alias");
+ }
+
+ @Test
+ public void testQueryParticipantOfGroupThreadWithId() {
+ Cursor cursor = mContentResolver.query(
+ Uri.parse("content://rcs/group_thread/3/participant/1"), null, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+
+ assertThat(cursor.getInt(cursor.getColumnIndex(RCS_PARTICIPANT_ID_COLUMN))).isEqualTo(1);
+ assertThat(cursor.getInt(cursor.getColumnIndex(CANONICAL_ADDRESS_ID_COLUMN))).isEqualTo(99);
+ assertThat(cursor.getString(cursor.getColumnIndex(RCS_ALIAS_COLUMN))).isEqualTo(
+ "Some alias");
+ }
+
+ @Test
+ public void testQueryFileTransfer() {
+ ContentValues values = new ContentValues();
+ // add an incoming message to the thread 2
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/p2p_thread/2/incoming_message"),
+ values)).isEqualTo(Uri.parse("content://rcs/p2p_thread/2/incoming_message/4"));
+
+ // add a file transfer
+ values.put(SESSION_ID_COLUMN, "session_id");
+ values.put(FILE_SIZE_COLUMN, 1234567890);
+ assertThat(
+ mContentResolver.insert(Uri.parse("content://rcs/message/4/file_transfer"),
+ values)).isEqualTo(Uri.parse("content://rcs/file_transfer/1"));
+
+ // query the file transfer back
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/file_transfer/1"), null,
+ null, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(0)).isEqualTo(1);
+ assertThat(cursor.getInt(1)).isEqualTo(4);
+ assertThat(cursor.getString(2)).isEqualTo("session_id");
+ assertThat(cursor.getLong(5)).isEqualTo(1234567890);
+ }
+}
diff --git a/tests/src/com/android/providers/telephony/RcsProviderTestable.java b/tests/src/com/android/providers/telephony/RcsProviderTestable.java
new file mode 100644
index 0000000..569fcdb
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/RcsProviderTestable.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 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.providers.telephony;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+
+import org.mockito.Mockito;
+
+/**
+ * A subclass of RcsProvider used for testing on an in-memory database
+ */
+public class RcsProviderTestable extends RcsProvider {
+ private MockContextWithProvider mockContextWithProvider;
+
+ @Override
+ public boolean onCreate() {
+ mockContextWithProvider = new MockContextWithProvider(this);
+ mDbOpenHelper = new InMemoryRcsDatabase();
+ mParticipantHelper = new RcsProviderParticipantHelper(mDbOpenHelper);
+ mThreadHelper = new RcsProviderThreadHelper(mDbOpenHelper);
+ mMessageHelper = new RcsProviderMessageHelper(mDbOpenHelper);
+ mEventHelper = new RcsProviderEventHelper(mDbOpenHelper);
+ return true;
+ }
+
+ protected void tearDown() {
+ mDbOpenHelper.close();
+ }
+
+ public SQLiteDatabase getWritableDatabase() {
+ return mDbOpenHelper.getWritableDatabase();
+ }
+
+ class InMemoryRcsDatabase extends SQLiteOpenHelper {
+ InMemoryRcsDatabase() {
+ super(null, // no context is needed for in-memory db
+ null, // db file name is null for in-memory db
+ null, // CursorFactory is null by default
+ 1); // db version is no-op for tests
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ MmsSmsDatabaseHelper mmsSmsDatabaseHelper = new MmsSmsDatabaseHelper(
+ mockContextWithProvider, null);
+ mmsSmsDatabaseHelper.createMmsTables(db);
+ mmsSmsDatabaseHelper.createSmsTables(db);
+ mmsSmsDatabaseHelper.createCommonTables(db);
+
+ RcsProviderThreadHelper.createThreadTables(db);
+ RcsProviderParticipantHelper.createParticipantTables(db);
+ RcsProviderMessageHelper.createRcsMessageTables(db);
+ RcsProviderEventHelper.createRcsEventTables(db);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // no-op
+ }
+ }
+
+ static class MockContextWithProvider extends MockContext {
+ private final MockContentResolver mResolver;
+
+ MockContextWithProvider(RcsProvider rcsProvider) {
+ mResolver = new MockContentResolver();
+
+ // Add authority="rcs" to given smsProvider
+ ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.authority = RcsProvider.AUTHORITY;
+ rcsProvider.attachInfoForTesting(this, providerInfo);
+ mResolver.addProvider(RcsProvider.AUTHORITY, rcsProvider);
+ }
+
+ @Override
+ public MockContentResolver getContentResolver() {
+ return mResolver;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return Mockito.mock(PackageManager.class);
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case Context.APP_OPS_SERVICE:
+ return Mockito.mock(AppOpsManager.class);
+ case Context.TELEPHONY_SERVICE:
+ return Mockito.mock(TelephonyManager.class);
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isCredentialProtectedStorage() {
+ return false;
+ }
+ }
+}
diff --git a/tests/src/com/android/providers/telephony/RcsProviderUpdateTest.java b/tests/src/com/android/providers/telephony/RcsProviderUpdateTest.java
new file mode 100644
index 0000000..45ab0ca
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/RcsProviderUpdateTest.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2018 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.providers.telephony;
+
+import static android.provider.Telephony.RcsColumns.Rcs1To1ThreadColumns.FALLBACK_THREAD_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.HEIGHT_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.WIDTH_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.GROUP_NAME_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.OWNER_PARTICIPANT_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns.ARRIVAL_TIMESTAMP_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageColumns.ORIGINATION_TIMESTAMP_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsMessageDeliveryColumns.DELIVERED_TIMESTAMP_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.CANONICAL_ADDRESS_ID_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_ALIAS_COLUMN;
+import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN;
+
+import static com.android.providers.telephony.RcsProviderHelper.setup1To1Thread;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Telephony.RcsColumns.RcsMessageDeliveryColumns;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsProviderUpdateTest {
+ private MockContentResolver mContentResolver;
+ private RcsProviderTestable mRcsProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRcsProvider = new RcsProviderTestable();
+ RcsProviderTestable.MockContextWithProvider
+ context = new RcsProviderTestable.MockContextWithProvider(mRcsProvider);
+ mContentResolver = context.getContentResolver();
+
+ // insert a participant
+ // first into the MmsSmsProvider
+ mRcsProvider.getWritableDatabase().execSQL(
+ "INSERT INTO canonical_addresses VALUES (1, \"+15551234567\")");
+
+ // then into the RcsProvider
+ ContentValues participantValues = new ContentValues();
+ participantValues.put(RCS_ALIAS_COLUMN, "Bob");
+ participantValues.put(CANONICAL_ADDRESS_ID_COLUMN, 1);
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/participant"),
+ participantValues)).isEqualTo(Uri.parse("content://rcs/participant/1"));
+
+ // insert fallback threads
+ mRcsProvider.getWritableDatabase().execSQL("INSERT INTO threads(_id) VALUES (1)");
+ mRcsProvider.getWritableDatabase().execSQL("INSERT INTO threads(_id) VALUES (2)");
+
+ setup1To1Thread(mContentResolver);
+
+ // insert one group thread
+ ContentValues groupContentValues = new ContentValues();
+ groupContentValues.put(OWNER_PARTICIPANT_COLUMN, 1);
+ groupContentValues.put(GROUP_NAME_COLUMN, "Name");
+ Uri groupThreadUri = Uri.parse("content://rcs/group_thread");
+ assertThat(mContentResolver.insert(groupThreadUri, groupContentValues)).isEqualTo(
+ Uri.parse("content://rcs/group_thread/2"));
+
+ Uri groupInsertionUri = Uri.parse("content://rcs/group_thread/2/participant/1");
+ assertThat(mContentResolver.insert(groupInsertionUri, null)).isEqualTo(groupInsertionUri);
+
+ // add incoming and outgoing messages to both threads
+ ContentValues messageValues = new ContentValues();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/p2p_thread/1/incoming_message"),
+ messageValues)).isEqualTo(
+ Uri.parse("content://rcs/p2p_thread/1/incoming_message/1"));
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/p2p_thread/1/outgoing_message"),
+ messageValues)).isEqualTo(
+ Uri.parse("content://rcs/p2p_thread/1/outgoing_message/2"));
+ assertThat(
+ mContentResolver.insert(Uri.parse("content://rcs/group_thread/2/incoming_message"),
+ messageValues)).isEqualTo(
+ Uri.parse("content://rcs/group_thread/2/incoming_message/3"));
+ assertThat(
+ mContentResolver.insert(Uri.parse("content://rcs/group_thread/2/outgoing_message"),
+ messageValues)).isEqualTo(
+ Uri.parse("content://rcs/group_thread/2/outgoing_message/4"));
+
+ // add message delivery to the outgoing messages
+ ContentValues deliveryValues = new ContentValues();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/outgoing_message/2/delivery/1"),
+ deliveryValues)).isEqualTo(
+ Uri.parse("content://rcs/outgoing_message/2/delivery/1"));
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/outgoing_message/4/delivery/1"),
+ deliveryValues)).isEqualTo(
+ Uri.parse("content://rcs/outgoing_message/4/delivery/1"));
+
+ // add a file transfer to an incoming message
+ ContentValues fileTransferValues = new ContentValues();
+ assertThat(mContentResolver.insert(Uri.parse("content://rcs/message/3/file_transfer"),
+ fileTransferValues)).isEqualTo(Uri.parse("content://rcs/file_transfer/1"));
+ }
+
+ @After
+ public void tearDown() {
+ mRcsProvider.tearDown();
+ }
+
+ @Test
+ public void testUpdate1To1ThreadWithSelection() {
+ // update the fallback thread id
+ ContentValues contentValues = new ContentValues(1);
+ contentValues.put(FALLBACK_THREAD_ID_COLUMN, 2);
+ Uri p2pThreadUri = Uri.parse("content://rcs/p2p_thread");
+
+ assertThat(mContentResolver.update(p2pThreadUri, contentValues, "rcs_fallback_thread_id=1",
+ null)).isEqualTo(1);
+
+ // verify the thread is actually updated
+ Cursor cursor = mContentResolver.query(p2pThreadUri,
+ new String[]{FALLBACK_THREAD_ID_COLUMN}, "rcs_fallback_thread_id=2", null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(0)).isEqualTo(2);
+ }
+
+ @Test
+ public void testUpdate1To1ThreadWithId() {
+ // update the fallback thread id
+ ContentValues contentValues = new ContentValues(1);
+ contentValues.put(FALLBACK_THREAD_ID_COLUMN, 2);
+ Uri p2pThreadUri = Uri.parse("content://rcs/p2p_thread/1");
+ assertThat(mContentResolver.update(p2pThreadUri, contentValues, null, null)).isEqualTo(1);
+
+ // verify the thread is actually updated
+ Cursor cursor = mContentResolver.query(p2pThreadUri,
+ new String[]{FALLBACK_THREAD_ID_COLUMN}, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getInt(0)).isEqualTo(2);
+ }
+
+ @Test
+ public void testUpdateGroupThreadWithSelection() {
+ // update the group name
+ ContentValues contentValues = new ContentValues(1);
+ contentValues.put(GROUP_NAME_COLUMN, "New name");
+ Uri groupThreadUri = Uri.parse("content://rcs/group_thread");
+ assertThat(mContentResolver.update(groupThreadUri, contentValues, "group_name=\"Name\"",
+ null)).isEqualTo(1);
+
+ // verify the thread is actually updated
+ Cursor cursor = mContentResolver.query(groupThreadUri, new String[]{GROUP_NAME_COLUMN},
+ "group_name=\"New name\"", null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToNext();
+ assertThat(cursor.getString(0)).isEqualTo("New name");
+ }
+
+ @Test
+ public void testUpdateGroupThreadWithId() {
+ // update the group name
+ ContentValues contentValues = new ContentValues(1);
+ contentValues.put(GROUP_NAME_COLUMN, "New name");
+ Uri groupThreadUri = Uri.parse("content://rcs/group_thread/2");
+ assertThat(mContentResolver.update(groupThreadUri, contentValues, null, null)).isEqualTo(1);
+
+ // verify the thread is actually updated
+ Cursor cursor = mContentResolver.query(groupThreadUri, new String[]{GROUP_NAME_COLUMN},
+ null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+ assertThat(cursor.getString(0)).isEqualTo("New name");
+ }
+
+ @Test
+ public void testUpdateParticipantWithId() {
+ // change the participant name from Bob to Bobby
+ ContentValues contentValues = new ContentValues(1);
+ contentValues.put(RCS_ALIAS_COLUMN, "Bobby");
+
+ Uri participantUri = Uri.parse("content://rcs/participant/1");
+
+ assertThat(mContentResolver.update(participantUri, contentValues, null, null)).isEqualTo(1);
+
+ // verify participant is actually updated
+ Cursor cursor = mContentResolver.query(participantUri, new String[]{RCS_ALIAS_COLUMN}, null,
+ null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+ assertThat(cursor.getString(0)).isEqualTo("Bobby");
+ }
+
+ @Test
+ public void testUpdate1To1ThreadParticipantFails() {
+ assertThat(
+ mContentResolver.update(Uri.parse("content://rcs/p2p_thread/1/participant/1"), null,
+ null, null)).isEqualTo(0);
+ }
+
+ @Test
+ public void testUpdateGroupParticipantFails() {
+ assertThat(mContentResolver.update(Uri.parse("content://rcs/group_thread/2/participant/1"),
+ null, null, null)).isEqualTo(0);
+ }
+
+ @Test
+ public void testUpdateUnifiedMessageViewFails() {
+ ContentValues updateValues = new ContentValues();
+ updateValues.put(ORIGINATION_TIMESTAMP_COLUMN, 1234567890);
+
+ assertThat(mContentResolver.update(Uri.parse("content://rcs/message"), updateValues, null,
+ null)).isEqualTo(0);
+ assertThat(mContentResolver.update(Uri.parse("content://rcs/message/1"), updateValues, null,
+ null)).isEqualTo(0);
+ }
+
+ @Test
+ public void testUpdateMessageOnThreadFails() {
+ ContentValues updateValues = new ContentValues();
+ updateValues.put(ORIGINATION_TIMESTAMP_COLUMN, 1234567890);
+
+ assertThat(mContentResolver.update(Uri.parse("content://rcs/p2p_thread/1/incoming_message"),
+ updateValues, null, null)).isEqualTo(0);
+ assertThat(
+ mContentResolver.update(Uri.parse("content://rcs/p2p_thread/1/incoming_message/1"),
+ updateValues, null, null)).isEqualTo(0);
+ assertThat(
+ mContentResolver.update(Uri.parse("content://rcs/group_thread/2/outgoing_message"),
+ updateValues, null, null)).isEqualTo(0);
+ assertThat(mContentResolver.update(
+ Uri.parse("content://rcs/groupp_thread/2/outgoing_message/1"), updateValues, null,
+ null)).isEqualTo(0);
+ }
+
+ @Test
+ public void testUpdateMessage() {
+ // update the message
+ ContentValues updateValues = new ContentValues(1);
+ updateValues.put(ORIGINATION_TIMESTAMP_COLUMN, 1234567890);
+ assertThat(
+ mContentResolver.update(Uri.parse("content://rcs/outgoing_message/2"), updateValues,
+ null, null)).isEqualTo(1);
+
+ // verify the value is actually updated
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/outgoing_message/2"), null,
+ null, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+ assertThat(cursor.getLong(5)).isEqualTo(1234567890);
+ cursor.close();
+ }
+
+ @Test
+ @Ignore // TODO: fix and un-ignore
+ public void testUpdateIncomingMessageSpecificColumn() {
+ // update the message
+ ContentValues updateValues = new ContentValues(1);
+ updateValues.put(ARRIVAL_TIMESTAMP_COLUMN, 987654321);
+ assertThat(
+ mContentResolver.update(Uri.parse("content://rcs/incoming_message/3"), updateValues,
+ null, null)).isEqualTo(1);
+
+ // verify the value is actually updated
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/incoming_message/3"), null,
+ null, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+ assertThat(cursor.getLong(7)).isEqualTo(987654321);
+ cursor.close();
+ }
+
+ @Test
+ public void testUpdateMessageDelivery() {
+ ContentValues updateValues = new ContentValues();
+ updateValues.put(DELIVERED_TIMESTAMP_COLUMN, 12345);
+ updateValues.put(RcsMessageDeliveryColumns.SEEN_TIMESTAMP_COLUMN, 54321);
+
+ assertThat(mContentResolver.update(Uri.parse("content://rcs/outgoing_message/2/delivery/1"),
+ updateValues, null, null)).isEqualTo(1);
+
+ // verify the value is actually updated
+ Cursor cursor = mContentResolver.query(
+ Uri.parse("content://rcs/outgoing_message/2/delivery"), null, null, null, null,
+ null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+ assertThat(cursor.getInt(0)).isEqualTo(2);
+ assertThat(cursor.getInt(1)).isEqualTo(1);
+ assertThat(cursor.getLong(2)).isEqualTo(12345);
+ assertThat(cursor.getLong(3)).isEqualTo(54321);
+ }
+
+ @Test
+ public void testUpdateFileTransfer() {
+ ContentValues updateValues = new ContentValues();
+ updateValues.put(WIDTH_COLUMN, 640);
+ updateValues.put(HEIGHT_COLUMN, 480);
+
+ assertThat(mContentResolver.update(Uri.parse("content://rcs/file_transfer/1"), updateValues,
+ null, null)).isEqualTo(1);
+
+ // verify that the values are actually updated
+ Cursor cursor = mContentResolver.query(Uri.parse("content://rcs/file_transfer/1"), null,
+ null, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToNext();
+ assertThat(cursor.getInt(8)).isEqualTo(640);
+ assertThat(cursor.getInt(9)).isEqualTo(480);
+ }
+}
diff --git a/tests/src/com/android/providers/telephony/ServiceStateProviderTest.java b/tests/src/com/android/providers/telephony/ServiceStateProviderTest.java
index fd15d01..c5ad5a0 100644
--- a/tests/src/com/android/providers/telephony/ServiceStateProviderTest.java
+++ b/tests/src/com/android/providers/telephony/ServiceStateProviderTest.java
@@ -87,7 +87,6 @@
ServiceStateTable.CDMA_ERI_ICON_INDEX,
ServiceStateTable.CDMA_ERI_ICON_MODE,
ServiceStateTable.IS_EMERGENCY_ONLY,
- ServiceStateTable.IS_DATA_ROAMING_FROM_REGISTRATION,
ServiceStateTable.IS_USING_CARRIER_AGGREGATION,
};
@@ -188,7 +187,6 @@
final int cdmaEriIconIndex = ss.getCdmaEriIconIndex();
final int cdmaEriIconMode = ss.getCdmaEriIconMode();
final int isEmergencyOnly = (ss.isEmergencyOnly()) ? 1 : 0;
- final int isDataRoamingFromRegistration = (ss.getDataRoamingFromRegistration()) ? 1 : 0;
final int isUsingCarrierAggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
assertEquals(voiceRegState, cursor.getInt(0));
@@ -210,8 +208,7 @@
assertEquals(cdmaEriIconIndex, cursor.getInt(16));
assertEquals(cdmaEriIconMode, cursor.getInt(17));
assertEquals(isEmergencyOnly, cursor.getInt(18));
- assertEquals(isDataRoamingFromRegistration, cursor.getInt(19));
- assertEquals(isUsingCarrierAggregation, cursor.getInt(20));
+ assertEquals(isUsingCarrierAggregation, cursor.getInt(19));
}
/**
diff --git a/tests/src/com/android/providers/telephony/SmsProviderTest.java b/tests/src/com/android/providers/telephony/SmsProviderTest.java
index ba63203..2bc5f0f 100644
--- a/tests/src/com/android/providers/telephony/SmsProviderTest.java
+++ b/tests/src/com/android/providers/telephony/SmsProviderTest.java
@@ -52,7 +52,7 @@
* --test-method testInsertUri
*/
public class SmsProviderTest extends TestCase {
- private static final String TAG = "TelephonyProviderTest";
+ private static final String TAG = "SmsProviderTest";
private MockContextWithProvider mContext;
private MockContentResolver mContentResolver;
diff --git a/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
new file mode 100644
index 0000000..cf534e2
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2018 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.providers.telephony;
+
+import static android.provider.Telephony.Carriers;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.support.test.InstrumentationRegistry;
+import android.telephony.SubscriptionManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * To run this test, run the following from the dir: packages/providers/TelephonyProvider
+ * atest TelephonyProviderTests:TelephonyDatabaseHelperTest
+ * Or
+ * runtest --path tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
+ */
+@RunWith(JUnit4.class)
+public final class TelephonyDatabaseHelperTest {
+
+ private final static String TAG = TelephonyDatabaseHelperTest.class.getSimpleName();
+
+ private Context mContext;
+ private TelephonyProvider.DatabaseHelper mHelper; // the actual class being tested
+ private SQLiteOpenHelper mInMemoryDbHelper; // used to give us an in-memory db
+
+ @Before
+ public void setUp() {
+ Log.d(TAG, "setUp() +");
+ mContext = InstrumentationRegistry.getContext();
+ mHelper = new TelephonyProvider.DatabaseHelper(mContext);
+ mInMemoryDbHelper = new InMemoryTelephonyProviderV5DbHelper();
+ Log.d(TAG, "setUp() -");
+ }
+
+ @Test
+ public void databaseHelperOnUpgrade_hasApnSetIdField() {
+ Log.d(TAG, "databaseHelperOnUpgrade_hasApnSetIdField");
+ // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
+ SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+ mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.DatabaseHelper.getVersion(mContext));
+
+ // the upgraded db must have the APN_SET_ID field
+ Cursor cursor = db.query("carriers", null, null, null, null, null, null);
+ String[] upgradedColumns = cursor.getColumnNames();
+ Log.d(TAG, "carriers columns: " + Arrays.toString(upgradedColumns));
+
+ assertTrue(Arrays.asList(upgradedColumns).contains(Carriers.APN_SET_ID));
+ }
+
+ @Test
+ public void databaseHelperOnUpgrade_hasCarrierIdField() {
+ Log.d(TAG, "databaseHelperOnUpgrade_hasSubscriptionTypeField");
+ // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
+ SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+ mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.DatabaseHelper.getVersion(mContext));
+
+ // the upgraded db must have the Telephony.Carriers.CARRIER_ID field
+ Cursor cursor = db.query("carriers", null, null, null, null, null, null);
+ String[] upgradedColumns = cursor.getColumnNames();
+ Log.d(TAG, "carriers columns: " + Arrays.toString(upgradedColumns));
+ assertTrue(Arrays.asList(upgradedColumns).contains(Carriers.CARRIER_ID));
+ }
+
+ @Test
+ public void databaseHelperOnUpgrade_hasCountryIsoField() {
+ Log.d(TAG, "databaseHelperOnUpgrade_hasCountryIsoField");
+ SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+ mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.DatabaseHelper.getVersion(mContext));
+
+ // the upgraded db must have the Telephony.Carriers.CARRIER_ID field
+ Cursor cursor = db.query("simInfo", null, null, null, null, null, null);
+ String[] upgradedColumns = cursor.getColumnNames();
+ Log.d(TAG, "iso columns: " + Arrays.toString(upgradedColumns));
+ assertTrue(Arrays.asList(upgradedColumns).contains(SubscriptionManager.ISO_COUNTRY_CODE));
+ }
+
+ @Test
+ public void databaseHelperOnUpgrade_hasProfileClassField() {
+ Log.d(TAG, "databaseHelperOnUpgrade_hasProfileClassField");
+ SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+ mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.DatabaseHelper.getVersion(mContext));
+
+ // the upgraded db must have the PROFILE_CLASS field
+ Cursor cursor = db.query("siminfo", null, null, null, null, null, null);
+ String[] upgradedColumns = cursor.getColumnNames();
+ Log.d(TAG, "profile class columns: " + Arrays.toString(upgradedColumns));
+ assertTrue(Arrays.asList(upgradedColumns).contains(SubscriptionManager.PROFILE_CLASS));
+ }
+
+ @Test
+ public void databaseHelperOnUpgrade_hasSkip464XlatField() {
+ Log.d(TAG, "databaseHelperOnUpgrade_hasSkip464XlatField");
+ // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
+ SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+ mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.DatabaseHelper.getVersion(mContext));
+
+ // the upgraded db must have the Telephony.Carriers.CARRIER_ID field
+ Cursor cursor = db.query("carriers", null, null, null, null, null, null);
+ String[] upgradedColumns = cursor.getColumnNames();
+ Log.d(TAG, "carriers columns: " + Arrays.toString(upgradedColumns));
+ assertTrue(Arrays.asList(upgradedColumns).contains(Carriers.SKIP_464XLAT));
+ }
+
+ @Test
+ public void databaseHelperOnUpgrade_columnsMatchNewlyCreatedDb() {
+ Log.d(TAG, "databaseHelperOnUpgrade_columnsMatchNewlyCreatedDb");
+ // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
+ SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+ mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.DatabaseHelper.getVersion(mContext));
+
+ // compare upgraded carriers table to a carriers table created from scratch
+ db.execSQL(TelephonyProvider.getStringForCarrierTableCreation("carriers_full"));
+
+ Cursor cursor = db.query("carriers", null, null, null, null, null, null);
+ String[] upgradedColumns = cursor.getColumnNames();
+ Log.d(TAG, "carriers columns: " + Arrays.toString(upgradedColumns));
+
+ cursor = db.query("carriers_full", null, null, null, null, null, null);
+ String[] fullColumns = cursor.getColumnNames();
+ Log.d(TAG, "carriers_full colunmns: " + Arrays.toString(fullColumns));
+
+ assertArrayEquals("Carriers table from onUpgrade doesn't match full table",
+ fullColumns, upgradedColumns);
+
+ // compare upgraded siminfo table to siminfo table created from scratch
+ db.execSQL(TelephonyProvider.getStringForSimInfoTableCreation("siminfo_full"));
+
+ cursor = db.query("siminfo", null, null, null, null, null, null);
+ upgradedColumns = cursor.getColumnNames();
+ Log.d(TAG, "siminfo columns: " + Arrays.toString(upgradedColumns));
+
+ cursor = db.query("siminfo_full", null, null, null, null, null, null);
+ fullColumns = cursor.getColumnNames();
+ Log.d(TAG, "siminfo_full colunmns: " + Arrays.toString(fullColumns));
+
+ assertArrayEquals("Siminfo table from onUpgrade doesn't match full table",
+ fullColumns, upgradedColumns);
+ }
+
+ @Test
+ public void databaseHelperOnUpgrade_hasSubscriptionTypeField() {
+ Log.d(TAG, "databaseHelperOnUpgrade_hasSubscriptionTypeField");
+ // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
+ SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+ mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.DatabaseHelper.getVersion(mContext));
+
+ // the upgraded db must have the SubscriptionManager.SUBSCRIPTION_TYPE field
+ Cursor cursor = db.query("siminfo", null, null, null, null, null, null);
+ String[] upgradedColumns = cursor.getColumnNames();
+ Log.d(TAG, "siminfo columns: " + Arrays.toString(upgradedColumns));
+
+ assertTrue(Arrays.asList(upgradedColumns).contains(SubscriptionManager.SUBSCRIPTION_TYPE));
+ }
+
+ /**
+ * Helper for an in memory DB used to test the TelephonyProvider#DatabaseHelper.
+ *
+ * We pass this in-memory db to DatabaseHelper#onUpgrade so we can use the actual function
+ * without using the actual telephony db.
+ */
+ private static class InMemoryTelephonyProviderV5DbHelper extends SQLiteOpenHelper {
+
+ public InMemoryTelephonyProviderV5DbHelper() {
+ super(InstrumentationRegistry.getContext(),
+ null, // db file name is null for in-memory db
+ null, // CursorFactory is null by default
+ 1); // in-memory db version doesn't seem to matter
+ Log.d(TAG, "InMemoryTelephonyProviderV5DbHelper creating in-memory database");
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ // Set up the carriers table without any fields added in onUpgrade
+ // since these are the initial fields, there is no need to update this test fixture in
+ // the future
+ List<String> originalUniqueFields = new ArrayList<String>();
+ originalUniqueFields.add(Carriers.NUMERIC);
+ originalUniqueFields.add(Carriers.MCC);
+ originalUniqueFields.add(Carriers.MNC);
+ originalUniqueFields.add(Carriers.APN);
+ originalUniqueFields.add(Carriers.PROXY);
+ originalUniqueFields.add(Carriers.PORT);
+ originalUniqueFields.add(Carriers.MMSPROXY);
+ originalUniqueFields.add(Carriers.MMSPORT);
+ originalUniqueFields.add(Carriers.MMSC);
+ Log.d(TAG, "InMemoryTelephonyProviderV5DbHelper onCreate creating the carriers table");
+ db.execSQL(
+ "CREATE TABLE carriers" +
+ "(_id INTEGER PRIMARY KEY," +
+ Carriers.NAME + " TEXT DEFAULT ''," +
+ Carriers.NUMERIC + " TEXT DEFAULT ''," +
+ Carriers.MCC + " TEXT DEFAULT ''," +
+ Carriers.MNC + " TEXT DEFAULT ''," +
+ Carriers.APN + " TEXT DEFAULT ''," +
+ Carriers.USER + " TEXT DEFAULT ''," +
+ Carriers.SERVER + " TEXT DEFAULT ''," +
+ Carriers.PASSWORD + " TEXT DEFAULT ''," +
+ Carriers.PROXY + " TEXT DEFAULT ''," +
+ Carriers.PORT + " TEXT DEFAULT ''," +
+ Carriers.MMSPROXY + " TEXT DEFAULT ''," +
+ Carriers.MMSPORT + " TEXT DEFAULT ''," +
+ Carriers.MMSC + " TEXT DEFAULT ''," +
+ Carriers.TYPE + " TEXT DEFAULT ''," +
+ Carriers.CURRENT + " INTEGER," +
+ "UNIQUE (" + TextUtils.join(", ", originalUniqueFields) + "));");
+
+ // set up the siminfo table without any fields added in onUpgrade
+ // since these are the initial fields, there is no need to update this test fixture in
+ // the future
+ Log.d(TAG, "InMemoryTelephonyProviderV5DbHelper onCreate creating the siminfo table");
+ db.execSQL(
+ "CREATE TABLE siminfo ("
+ + SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SubscriptionManager.ICC_ID + " TEXT NOT NULL,"
+ + SubscriptionManager.SIM_SLOT_INDEX
+ + " INTEGER DEFAULT " + SubscriptionManager.SIM_NOT_INSERTED + ","
+ + SubscriptionManager.DISPLAY_NAME + " TEXT,"
+ + SubscriptionManager.NAME_SOURCE
+ + " INTEGER DEFAULT " + SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE + ","
+ + SubscriptionManager.COLOR
+ + " INTEGER DEFAULT " + SubscriptionManager.COLOR_DEFAULT + ","
+ + SubscriptionManager.NUMBER + " TEXT,"
+ + SubscriptionManager.DISPLAY_NUMBER_FORMAT + " INTEGER NOT NULL"
+ + " DEFAULT " + SubscriptionManager.DISPLAY_NUMBER_DEFAULT + ","
+ + SubscriptionManager.DATA_ROAMING
+ + " INTEGER DEFAULT " + SubscriptionManager.DATA_ROAMING_DEFAULT + ","
+ + SubscriptionManager.CARD_ID + " TEXT NOT NULL"
+ + ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.d(TAG, "InMemoryTelephonyProviderV5DbHelper onUpgrade doing nothing");
+ return;
+ }
+ }
+}
diff --git a/tests/src/com/android/providers/telephony/TelephonyProviderTest.java b/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
index dde9e5c..5723227 100644
--- a/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
@@ -16,60 +16,46 @@
package com.android.providers.telephony;
-import android.annotation.TargetApi;
-import android.content.ContentProvider;
-import android.content.ContentResolver;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.Manifest;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
-import android.content.SharedPreferences;
-import android.database.Cursor;
import android.database.ContentObserver;
-import android.database.DatabaseErrorHandler;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.database.Cursor;
import android.net.Uri;
-import android.os.Build;
-import android.os.FileUtils;
import android.os.Process;
+import android.provider.Telephony;
import android.provider.Telephony.Carriers;
import android.support.test.InstrumentationRegistry;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.test.AndroidTestCase;
-import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.SmallTest;
-import android.text.TextUtils;
import android.util.Log;
-import com.android.providers.telephony.TelephonyProvider;
+import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.UiccController;
import junit.framework.TestCase;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
-import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-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 java.util.Map;
-import java.util.Set;
-
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.IntStream;
/**
* Tests for testing CRUD operations of TelephonyProvider.
@@ -90,13 +76,23 @@
private MockContentResolver mContentResolver;
private TelephonyProviderTestable mTelephonyProviderTestable;
+ @Mock
+ private UiccController mUiccController;
+
+ @Mock
+ private IccRecords mIcRecords;
+
private int notifyChangeCount;
private int notifyChangeRestoreCount;
+ private int notifyWfcCount;
+ private int notifyWfcCountWithTestSubId;
private static final String TEST_SUBID = "1";
private static final String TEST_OPERATOR = "123456";
private static final String TEST_MCC = "123";
private static final String TEST_MNC = "456";
+ private static final String TEST_SPN = TelephonyProviderTestable.TEST_SPN;
+
// Used to test the path for URL_TELEPHONY_USING_SUBID with subid 1
private static final Uri CONTENT_URI_WITH_SUBID = Uri.parse(
"content://telephony/carriers/subId/" + TEST_SUBID);
@@ -107,6 +103,10 @@
// Used to test the preferred apn
private static final Uri URL_PREFERAPN_USING_SUBID = Uri.parse(
"content://telephony/carriers/preferapn/subId/" + TEST_SUBID);
+ private static final Uri URL_WFC_ENABLED_USING_SUBID = Uri.parse(
+ "content://telephony/siminfo/" + TEST_SUBID);
+ private static final Uri URL_SIM_APN_LIST = Uri.parse(
+ "content://telephony/carriers/sim_apn_list");
private static final String COLUMN_APN_ID = "apn_id";
@@ -126,6 +126,10 @@
private final MockContentResolver mResolver;
private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
+ private final List<String> GRANTED_PERMISSIONS = Arrays.asList(
+ Manifest.permission.MODIFY_PHONE_STATE, Manifest.permission.WRITE_APN_SETTINGS,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
public MockContextWithProvider(TelephonyProvider telephonyProvider) {
mResolver = new MockContentResolver() {
@Override
@@ -134,6 +138,10 @@
notifyChangeCount++;
if (URL_RESTOREAPN_USING_SUBID.equals(uri)) {
notifyChangeRestoreCount++;
+ } else if (SubscriptionManager.WFC_ENABLED_CONTENT_URI.equals(uri)) {
+ notifyWfcCount++;
+ } else if (URL_WFC_ENABLED_USING_SUBID.equals(uri)) {
+ notifyWfcCountWithTestSubId++;
}
}
};
@@ -141,6 +149,12 @@
// return test subId 0 for all operators
doReturn(TEST_OPERATOR).when(mTelephonyManager).getSimOperator(anyInt());
+ doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+ doReturn(TEST_OPERATOR).when(mTelephonyManager).getSimOperator();
+ doReturn(mIcRecords).when(mUiccController).getIccRecords(anyInt(),
+ ArgumentMatchers.eq(UiccController.APP_FAM_3GPP));
+ doReturn(TEST_SPN).when(mIcRecords).getServiceProviderName();
+
// Add authority="telephony" to given telephonyProvider
ProviderInfo providerInfo = new ProviderInfo();
providerInfo.authority = "telephony";
@@ -186,7 +200,7 @@
// Gives permission to write to the APN table within the MockContext
@Override
public int checkCallingOrSelfPermission(String permission) {
- if (TextUtils.equals(permission, "android.permission.WRITE_APN_SETTINGS")) {
+ if (GRANTED_PERMISSIONS.contains(permission)) {
Log.d(TAG, "checkCallingOrSelfPermission: permission=" + permission
+ ", returning PackageManager.PERMISSION_GRANTED");
return PackageManager.PERMISSION_GRANTED;
@@ -201,9 +215,11 @@
@Override
protected void setUp() throws Exception {
super.setUp();
+ MockitoAnnotations.initMocks(this);
mTelephonyProviderTestable = new TelephonyProviderTestable();
mContext = new MockContextWithProvider(mTelephonyProviderTestable);
mContentResolver = (MockContentResolver) mContext.getContentResolver();
+ replaceInstance(UiccController.class, "mInstance", null, mUiccController);
notifyChangeCount = 0;
notifyChangeRestoreCount = 0;
}
@@ -287,6 +303,64 @@
}
/**
+ * Test migrating int-based MCC/MNCs over to Strings in the sim info table
+ */
+ @Test
+ @SmallTest
+ public void testMccMncMigration() {
+ CarrierIdProviderTestable carrierIdProvider = new CarrierIdProviderTestable();
+ carrierIdProvider.initializeForTesting(mContext);
+ mContentResolver.addProvider(Telephony.CarrierId.All.CONTENT_URI.getAuthority(),
+ carrierIdProvider);
+ // Insert a few values into the carrier ID db
+ List<String> mccMncs = Arrays.asList("99910", "999110", "999060", "99905");
+ ContentValues[] carrierIdMccMncs = mccMncs.stream()
+ .map((mccMnc) -> {
+ ContentValues cv = new ContentValues(1);
+ cv.put(Telephony.CarrierId.All.MCCMNC, mccMnc);
+ return cv;
+ }).toArray(ContentValues[]::new);
+ mContentResolver.bulkInsert(Telephony.CarrierId.All.CONTENT_URI, carrierIdMccMncs);
+
+ // Populate the sim info db with int-format entries
+ ContentValues[] existingSimInfoEntries = IntStream.range(0, mccMncs.size())
+ .mapToObj((idx) -> {
+ int mcc = Integer.valueOf(mccMncs.get(idx).substring(0, 3));
+ int mnc = Integer.valueOf(mccMncs.get(idx).substring(3));
+ ContentValues cv = new ContentValues(4);
+ cv.put(SubscriptionManager.MCC, mcc);
+ cv.put(SubscriptionManager.MNC, mnc);
+ cv.put(SubscriptionManager.ICC_ID, String.valueOf(idx));
+ cv.put(SubscriptionManager.CARD_ID, String.valueOf(idx));
+ return cv;
+ }).toArray(ContentValues[]::new);
+
+ mContentResolver.bulkInsert(SubscriptionManager.CONTENT_URI, existingSimInfoEntries);
+
+ // Run the upgrade helper on all the sim info entries.
+ String[] proj = {SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
+ SubscriptionManager.MCC, SubscriptionManager.MNC,
+ SubscriptionManager.MCC_STRING, SubscriptionManager.MNC_STRING};
+ try (Cursor c = mContentResolver.query(SubscriptionManager.CONTENT_URI, proj,
+ null, null, null)) {
+ while (c.moveToNext()) {
+ TelephonyProvider.fillInMccMncStringAtCursor(mContext,
+ mTelephonyProviderTestable.getWritableDatabase(), c);
+ }
+ }
+
+ // Loop through and make sure that everything got filled in correctly.
+ try (Cursor c = mContentResolver.query(SubscriptionManager.CONTENT_URI, proj,
+ null, null, null)) {
+ while (c.moveToNext()) {
+ String mcc = c.getString(c.getColumnIndexOrThrow(SubscriptionManager.MCC_STRING));
+ String mnc = c.getString(c.getColumnIndexOrThrow(SubscriptionManager.MNC_STRING));
+ assertTrue(mccMncs.contains(mcc + mnc));
+ }
+ }
+ }
+
+ /**
* Test updating values in carriers table. Verify that when update hits a conflict using URL_ID
* we merge the rows.
*/
@@ -470,11 +544,13 @@
final String insertCarrierName = "exampleCarrierName";
final String insertIccId = "exampleIccId";
final String insertCardId = "exampleCardId";
+ final int insertProfileClass = SubscriptionManager.PROFILE_CLASS_DEFAULT;
contentValues.put(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID, insertSubId);
contentValues.put(SubscriptionManager.DISPLAY_NAME, insertDisplayName);
contentValues.put(SubscriptionManager.CARRIER_NAME, insertCarrierName);
contentValues.put(SubscriptionManager.ICC_ID, insertIccId);
contentValues.put(SubscriptionManager.CARD_ID, insertCardId);
+ contentValues.put(SubscriptionManager.PROFILE_CLASS, insertProfileClass);
Log.d(TAG, "testSimTable Inserting contentValues: " + contentValues);
mContentResolver.insert(SubscriptionManager.CONTENT_URI, contentValues);
@@ -485,6 +561,7 @@
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
SubscriptionManager.CARRIER_NAME,
SubscriptionManager.CARD_ID,
+ SubscriptionManager.PROFILE_CLASS,
};
final String selection = SubscriptionManager.DISPLAY_NAME + "=?";
String[] selectionArgs = { insertDisplayName };
@@ -500,9 +577,11 @@
final int resultSubId = cursor.getInt(0);
final String resultCarrierName = cursor.getString(1);
final String resultCardId = cursor.getString(2);
+ final int resultProfileClass = cursor.getInt(3);
assertEquals(insertSubId, resultSubId);
assertEquals(insertCarrierName, resultCarrierName);
assertEquals(insertCardId, resultCardId);
+ assertEquals(insertProfileClass, resultProfileClass);
// delete test content
final String selectionToDelete = SubscriptionManager.DISPLAY_NAME + "=?";
@@ -546,7 +625,7 @@
mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
final int current = 1;
- final String numeric = "123456789";
+ final String numeric = TEST_OPERATOR;
// Insert DPC record.
final String dpcRecordApn = "exampleApnNameDPC";
@@ -668,7 +747,7 @@
mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
final int current = 1;
- final String numeric = "123456789";
+ final String numeric = TEST_OPERATOR;
final String selection = Carriers.NUMERIC + "=?";
final String[] selectionArgs = { numeric };
@@ -746,7 +825,7 @@
mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
final int current = 1;
- final String numeric = "123456789";
+ final String numeric = TEST_OPERATOR;
// Insert DPC record.
final String dpcRecordApn = "exampleApnNameDPC";
@@ -837,7 +916,7 @@
mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
final int current = 1;
- final String numeric = "123456789";
+ final String numeric = TEST_OPERATOR;
// Insert DPC record 1.
final String dpcRecordApn1 = "exampleApnNameDPC";
@@ -1017,7 +1096,7 @@
editedValue.put(Carriers.NUMERIC, numeric1);
editedValue.put(Carriers.MCC, mcc1);
editedValue.put(Carriers.MNC, mnc1);
- editedValue.put(Carriers.EDITED, value);
+ editedValue.put(Carriers.EDITED_STATUS, value);
assertNotNull(mContentResolver.insert(URI_TELEPHONY, editedValue));
Cursor cur = mContentResolver.query(URI_TELEPHONY, null, null, null, null);
@@ -1030,13 +1109,13 @@
values.put(Carriers.NUMERIC, numeric1);
values.put(Carriers.MCC, mcc1);
values.put(Carriers.MNC, mnc1);
- values.put(Carriers.EDITED, Carriers.UNEDITED);
+ values.put(Carriers.EDITED_STATUS, Carriers.UNEDITED);
mContentResolver.insert(URI_TELEPHONY, values);
String[] testProjection = {
Carriers.NAME,
Carriers.APN,
- Carriers.EDITED,
+ Carriers.EDITED_STATUS,
Carriers.TYPE,
Carriers.PROTOCOL,
Carriers.BEARER_BITMASK,
@@ -1064,7 +1143,7 @@
editedValue.put(Carriers.NUMERIC, numeric1);
editedValue.put(Carriers.MCC, mcc1);
editedValue.put(Carriers.MNC, mnc1);
- editedValue.put(Carriers.EDITED, value);
+ editedValue.put(Carriers.EDITED_STATUS, value);
assertNotNull(mContentResolver.insert(URI_TELEPHONY, editedValue));
// insert APN that conflicts with edited APN
@@ -1074,13 +1153,13 @@
values.put(Carriers.NUMERIC, numeric1);
values.put(Carriers.MCC, mcc1);
values.put(Carriers.MNC, mnc1);
- values.put(Carriers.EDITED, Carriers.UNEDITED);
+ values.put(Carriers.EDITED_STATUS, Carriers.UNEDITED);
mContentResolver.insert(URI_TELEPHONY, values);
String[] testProjection = {
Carriers.NAME,
Carriers.APN,
- Carriers.EDITED,
+ Carriers.EDITED_STATUS,
Carriers.TYPE,
Carriers.PROTOCOL,
Carriers.BEARER_BITMASK,
@@ -1339,4 +1418,129 @@
assertEquals(0, cursor.getCount());
assertEquals(3, notifyChangeRestoreCount);
}
+
+ /**
+ * Test changes to siminfo/WFC_IMS_ENABLED and simInfo/ENHANCED_4G
+ */
+ @Test
+ @SmallTest
+ public void testUpdateWfcEnabled() {
+ // insert test contentValues
+ ContentValues contentValues = new ContentValues();
+ final int insertSubId = 1;
+ final String insertDisplayName = "exampleDisplayName";
+ final String insertCarrierName = "exampleCarrierName";
+ final String insertIccId = "exampleIccId";
+ final String insertCardId = "exampleCardId";
+ contentValues.put(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID, insertSubId);
+ contentValues.put(SubscriptionManager.DISPLAY_NAME, insertDisplayName);
+ contentValues.put(SubscriptionManager.CARRIER_NAME, insertCarrierName);
+ contentValues.put(SubscriptionManager.ICC_ID, insertIccId);
+ contentValues.put(SubscriptionManager.CARD_ID, insertCardId);
+
+ Log.d(TAG, "testSimTable Inserting wfc contentValues: " + contentValues);
+ mContentResolver.insert(SubscriptionManager.CONTENT_URI, contentValues);
+ assertEquals(0, notifyWfcCount);
+
+ // update wfc_enabled
+ ContentValues values = new ContentValues();
+ values.put(SubscriptionManager.WFC_IMS_ENABLED, true);
+ final String selection = SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?";
+ final String[] selectionArgs = { "" + insertSubId };
+ mContentResolver.update(SubscriptionManager.CONTENT_URI, values, selection, selectionArgs);
+ assertEquals(1, notifyWfcCount);
+ assertEquals(0, notifyWfcCountWithTestSubId);
+
+ // update other fields
+ values = new ContentValues();
+ values.put(SubscriptionManager.DISPLAY_NAME, "exampleDisplayNameNew");
+ mContentResolver.update(SubscriptionManager.CONTENT_URI, values, selection, selectionArgs);
+ // expect no change on wfc count
+ assertEquals(1, notifyWfcCount);
+ assertEquals(0, notifyWfcCountWithTestSubId);
+
+ // update WFC using subId
+ values = new ContentValues();
+ values.put(SubscriptionManager.WFC_IMS_ENABLED, false);
+ mContentResolver.update(SubscriptionManager.getUriForSubscriptionId(insertSubId),
+ values, null, null);
+ assertEquals(1, notifyWfcCount);
+ assertEquals(0, notifyWfcCountWithTestSubId);
+ }
+
+ protected 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);
+ }
+
+ @Test
+ @SmallTest
+ public void testSIMAPNLIST_APNMatchTheMCCMNCAndMVNO() {
+ // Test on getCurrentAPNList() step 1
+ final String apnName = "apnName";
+ final String carrierName = "name";
+ final String numeric = TEST_OPERATOR;
+ final String mvnoType = "spn";
+ final String mvnoData = TEST_SPN;
+
+ // Insert the APN and DB only have the MCC/MNC and MVNO APN
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Carriers.APN, apnName);
+ contentValues.put(Carriers.NAME, carrierName);
+ contentValues.put(Carriers.NUMERIC, numeric);
+ contentValues.put(Carriers.MVNO_TYPE, mvnoType);
+ contentValues.put(Carriers.MVNO_MATCH_DATA, mvnoData);
+ mContentResolver.insert(Carriers.CONTENT_URI, contentValues);
+
+ // Query DB
+ final String[] testProjection =
+ {
+ Carriers.APN,
+ Carriers.NAME,
+ Carriers.NUMERIC,
+ Carriers.MVNO_MATCH_DATA
+ };
+ Cursor cursor = mContentResolver.query(URL_SIM_APN_LIST,
+ testProjection, null, null, null);
+
+ cursor.moveToFirst();
+ assertEquals(apnName, cursor.getString(0));
+ assertEquals(carrierName, cursor.getString(1));
+ assertEquals(numeric, cursor.getString(2));
+ assertEquals(mvnoData, cursor.getString(3));
+ }
+
+ @Test
+ @SmallTest
+ public void testSIMAPNLIST_APNMatchTheParentMCCMNC() {
+ // Test on getCurrentAPNList() step 2
+ final String apnName = "apnName";
+ final String carrierName = "name";
+ final String numeric = TEST_OPERATOR;
+
+ // Insert the APN and DB only have the MNO APN
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Carriers.APN, apnName);
+ contentValues.put(Carriers.NAME, carrierName);
+ contentValues.put(Carriers.NUMERIC, numeric);
+ mContentResolver.insert(Carriers.CONTENT_URI, contentValues);
+
+ // Query DB
+ final String[] testProjection =
+ {
+ Carriers.APN,
+ Carriers.NAME,
+ Carriers.NUMERIC,
+ };
+ Cursor cursor = mContentResolver.query(URL_SIM_APN_LIST,
+ testProjection, null, null, null);
+
+ cursor.moveToFirst();
+ assertEquals(apnName, cursor.getString(0));
+ assertEquals(carrierName, cursor.getString(1));
+ assertEquals(numeric, cursor.getString(2));
+ }
}