From 27a53aadd9e973e89b093307b9f8997a36088a9f Mon Sep 17 00:00:00 2001 From: Edwin Tung Date: Mon, 7 Sep 2020 15:26:30 +0800 Subject: [PATCH 1/3] Make Psds server configurable Add configure for Long-Term, Normal and Real-Time Psds servers Test: manual Change-Id: Ic7f4151eb0410baaf1a98e3d40b4a6906e2837d2 --- core/res/res/values/config.xml | 7 ++++ core/res/res/values/symbols.xml | 5 +++ .../location/gnss/GnssConfiguration.java | 38 ++++++++++++++++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 89e348ab57b3..b23e7e7fed61 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1656,6 +1656,13 @@ com.android.location.fused + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/Tethering/AndroidManifestBase.xml b/packages/Tethering/AndroidManifestBase.xml deleted file mode 100644 index 97c3988829fe..000000000000 --- a/packages/Tethering/AndroidManifestBase.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - diff --git a/packages/Tethering/AndroidManifest_InProcess.xml b/packages/Tethering/AndroidManifest_InProcess.xml deleted file mode 100644 index b1f124097c79..000000000000 --- a/packages/Tethering/AndroidManifest_InProcess.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/Tethering/OWNERS b/packages/Tethering/OWNERS deleted file mode 100644 index a6c21fc16bec..000000000000 --- a/packages/Tethering/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -set noparent # while performing migration - b/167962976 -baligh@google.com diff --git a/packages/Tethering/TEST_MAPPING b/packages/Tethering/TEST_MAPPING deleted file mode 100644 index 5617b0c13c1c..000000000000 --- a/packages/Tethering/TEST_MAPPING +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presubmit": [ - { - "name": "TetheringTests" - } - ], - "postsubmit": [ - { - "name": "TetheringIntegrationTests" - } - ] -} diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp deleted file mode 100644 index 05243749f765..000000000000 --- a/packages/Tethering/apex/Android.bp +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -apex { - name: "com.android.tethering", - updatable: true, - min_sdk_version: "current", - java_libs: ["framework-tethering"], - bpfs: ["offload.o"], - apps: ["Tethering"], - manifest: "manifest.json", - key: "com.android.tethering.key", - - androidManifest: "AndroidManifest.xml", -} - -apex_key { - name: "com.android.tethering.key", - public_key: "com.android.tethering.avbpubkey", - private_key: "com.android.tethering.pem", -} - -android_app_certificate { - name: "com.android.tethering.certificate", - certificate: "com.android.tethering", -} - -override_apex { - name: "com.android.tethering.inprocess", - base: "com.android.tethering", - package_name: "com.android.tethering.inprocess", - apps: [ - "InProcessTethering", - ], -} diff --git a/packages/Tethering/apex/AndroidManifest.xml b/packages/Tethering/apex/AndroidManifest.xml deleted file mode 100644 index 4aae3cc3000d..000000000000 --- a/packages/Tethering/apex/AndroidManifest.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - diff --git a/packages/Tethering/apex/com.android.tethering.avbpubkey b/packages/Tethering/apex/com.android.tethering.avbpubkey deleted file mode 100644 index 9a2c0174e4965ecab2061accfe7ca9fa7075d888..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1032 zcmV+j1o!&@01yC431g}F1(T;u&jk|(w~3WByLmi11r;ae6y;Mhxb5TuSDgb04!{i6 z%$6BsYl+c_rh&j{APZInxoI4df-f>xoKNEo7#+f!ejiNek7&}j)$7TcmI>v*BTIm&o!fDCs>8DP}2)s)c5enC6Rt=RIYEjKeqUWJ9o zENFoCZ~YDVOBzzg~Gv> zO|Z+ZazN28A-yCwS%0} zBoL#{8~b&dd`p@an6#lagN$HDY(4gwZf!i?m00>u16BUwb-)c=+%=TXtUa+dhMrZp zOO>RroNJtXgo7zgqI}~3z>aXWkSr!&Y+p9^4=B-j@hj?-(2qC8`-KSTd7&Hc4Y<4~ zSuq-(7#XhAV^F*-vuqQzhU6xHkdQvMV?D{}B56k`HG}I7rP_7!39&qN)vdrEJae1QWvjn>VqKx>3I>N=t+p75C}}W2(@9bo7%Q z=g?^os-BK{h@+mfP%;q}@OLtI%)^(3JH9_o&lZsIUVhX+Z{k$4H_c4rGvn%6h_{pK zikRq0xy8Mq*NxuYV={kIG`VMTk%)=p_JZK@w#UuFjt?H`!KrJE8OQPv>1aslU;fQ# zTK&y4ie{H&z!R$z#n(2^KwD7 z>Q!m8_qtHl2W+w!-}R;TgAHzS?d3!Lzgmc+7&t_4K)Z_`lKdYH`1cs%B7Ai1K7w^7 zgb?T;;N>Pj62U1L7FY*VYS$tgPP2anx`qNnwkS3vC!RY0O3MEBCMO&vSr-gS?S*Cv z_7Jg=vb9O4D+4e6e&8~(W}bRI=4&qOb3-Elkum!i0?Azej5io!C5B?cL@=HhZMo0) z+g*$ufJb0u!U!2R0AA)Jvr)1W|56OAjRhnQom3r{dX`cIbuQ`5lN&!<#TZMFHFnlt=a^WUR800!w> zF`&X%6cIY_)5}8)N2QgMDaMNIJ*{b6d$!;&&6L{^^|FR)f8K{yow|QfHGo-$L)=r$ CV)||X diff --git a/packages/Tethering/apex/com.android.tethering.pem b/packages/Tethering/apex/com.android.tethering.pem deleted file mode 100644 index d4f39abd3bb2..000000000000 --- a/packages/Tethering/apex/com.android.tethering.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKgIBAAKCAgEA+AWTp03PBRMGt4mVNLt5PDoFFSfmFOVTM7jt5AJXnQMIDsAM -1cyWGWRridGIpoHAaCALVgW5aRySgi8yV5xP4w0YHcKbfh9M6I9oz4RUo4GQBZfX -+lFIGaLjb6I3tEJxPuxps4sW26Io63ihwTnKeGyADHdHGWDUs9WU0Ml+QTvKrdjy -qC03M0dehYXILGiA9m+UXwKoKxhWgfDUhWLhDBUtLJLPL4WeqKc9sG9h+zzVqE+8 -LzJsfrodKhTTrLpWOXi6YLRTk8dzsuPz/Nu98sJd1w3fHd20DrmkqsxVhgN1h+nk -zcPpxyGYIP6qYVZCmIXCwZZNtPeb7y/tOs967VHoZ4Qj7p2tE0CAWFMZFGjA/pcZ -7fi6CsIuMOYBbj4+wRlJwpG1g5zSJBCjzhv7dZp8S5oXmLShNYOMYEdsPfaZbm08 -3pVY+k8DVf7idcANXNw1lM+sPbE2hp5VuEuVpK+ca5x8hIMpTqJ84wDAjnC1kCwm -X2xfNvYPKNF58SvqlNCPN8X7hQjoeaEb7w24vCdZMRqeGBmu1GNQvCyzbBO0huQm -f5CQPrZjPcnoImlP879VPxY4YB6tAjsA/ZLiub9VdT108lCjb5r8criMzpMAA/AQ -NqQLWFI3M43xPemGBTiIguTYgpRgGcdRZf7XuTgTY5qzQZZuZMVuwaqSD2cCAwEA -AQKCAgEA0jMvw3BPTrakT7Lb8JgelKt7mUV6WyVMUZ6eh0pw5JIoJxAfEKfWYmjY -NzKNRMjcv6LA2MP7MplTld/YI6ZHkl+Lm9VOISL39HVuV8mIThbFb+gT1INEvu1t -IjRyT2SsQ67rmo377mLNmVtgg7mt3kfecjI44MpPGqad/CF4zmKVUKd4aI4BpYUM -F8+dKf3bpoBEWA2RZwy2bGQmSXHW132vDoLR8y2knL04rCqJ+PrC/WWuULXEe9bS -VtLV3yMBZq3qD4Fk/+7fILLPGvNFVdPi4htQiChYrM4rP9HzfaO63VieYMF0hR70 -pqoOznXj9Q4QVC9FZmUgFCQjQ1+KhqJw3OldIo0SnvpsLdTO/inKkhQWKC5HlPyh -/rqvro2j3pTHWPAziuBr+oQPcdVCOlCBZ+B99L1tO7aGktVPEIVQG7G7jlFMBiJ1 -j/kRGk2RTX8RaPQJTnwUqp8mWUV2fwxHiXNadjejA5ZU3eQT2eAOhXl1w6Lv2jEl -0wMOwPMJGcF77CcqnnWHON8fkxCbAfyy5Uo6Pm9g/Zzecn+ji2sabG7Ge5t0gzdL -LKRcGoyakN2CrbQ8pxlCTgE4HX5oPY+VuqOf8L3AIWIJBsyLbXHVkL1mqQ/Ed2uz -zaaSFYUZw81+m/5bl8JLPaIFNPyikZrXTD0YRer3V06XiyP/kYECggEBAP033xeF -OhgRwkRTjd68hwRJpyHsZDWxHiUqQf6l6yFv5mEE355G2IGI7cZmR2+tUDjQdxLv -tAZIszTK4PFCdVTeWfGVFbVF84eNWLB124pHDMM79GN/AMcuHnQPR756a8IO1hIy -4KxIUE1a1PKN5b9IgE5Lu4TZM96HDpFcUAmCT5urdYDmg3++IWT9PYQlGS7Hhiar -r+Hh646waM8Qx619CwXBqy+Y37+WHVbYqJClr6AcpVMrGA+6cgpskFpZAPLsoy7G -RSsVfyV8pH2JKm/hzk7XCwIpczxeWQSfpJWZ+oOPFHu+zM60Cdj2UrQyKrNHwew8 -+WYe9eCA+MiNBcECggEBAPq/F1vdqROiLv9uzhKb8ybgdL7CmREELiqwK+MvNE9t -W7lQz7lcWzav+b2n0M+VJBxUWB3XClgoIvA/AllgTgsYXfKAxNakhKLSBoMmvKCW -HtWcGr/D3RcmacK+DTMWlVS/LuueAFLuH6UmBIUFKc+qA5x7oQecAFALBFupE3G4 -LtAspLBI6P8gRtRav5p2whs9H8qjYcyf2f6liWpkmFITcXvPvAxFHicR6ZJdwZ/S -PiX2LJQnOpT7L3+2PWnYwzFStb4MkMGlFKcscU9CvS53JcP/J4Asjk0I4zDB2gri -xzFHPlVzCr2IVVGptKCQ3sdYiMIzQKzEXQHCU8h37ycCggEBAJu8aC48Fz3Edlm1 -ldS+2L9vWSaJEBzhoSu0cMBgZVu8SdGzwKDE69XHVI4oS5lI28UFmaaA3JTc07MN -cAmSGT2oP2NQkPhbXGsrKLfm1K6YAiZ1Ulp7OwxFth8lYreo7Wt92nV46yuqkhDx -Y3UGhp39xkPhWiRbvgYHxJLsVqFyjumsK2mq3IeNdVZ6VgJXGsTlnAFeqJ7hZxHs -N5natSRjeosA0PtGJ57agZLvT8Ue0gREef3LzFGoFwmIOcQHZ4kAt2BGOzZDU17H -6Rb4bKxBEbT1l2St/5zKXi90zDHicOvG7Q8qiyY6HrBc1wLSs+ZtpLxZx/3h3tFE -IT6fVUECggEBAMSAQm8Ey76OJ+SXUjk1K50442SnHcs/Cmr7urkEQitImUwl71Pk -87pst/uP6szypOTqmE9yOTIS6iZ6Sn3+QcriIqWrkhZfwW3Tx7S6A7KZUrq15iSH -+thsiw9JXxC9TvOmC8AsBzb2U6hZncsc28JZCxFztSNAduJDb/vhCVLiMxWDFuDr -kmR1R+yc3XDQRpeQFDz6QudYEj9EPOc6xD/16sZLaqP2+oVFvVSt0tJLsdaQECle -gMNGAdhE2eX8MCOUHMc+E6cdlozYAEhMFfO2/cqWR79jq3TlVR3dnOFRDScqHMhc -KnuTvsELjHkUbvGsCSiff7yk+fop7vy4OJsCggEAPemJdItO2rhib8EofrZdY72I -oifX1jhPZ1BWD2GKgcx+eVyJGbONBbJVexvvskTfZBvCcAegmgp+sngP6MO6yZkr -cHMfAJeApYZnshsgXksHGMDtSB50/w1JLrc/nqpxdpy/aTazt0Eu1pLWpze1HFZ/ -Xyu4PcmrU+4P1vN7c396slHMktEvly6QqOn4nfBbGDJ17Ow6X1XFvGjAxQPIDTB+ -6loV14AHymwmqwMrGn84O72rzqyw+41GxW5+oXhOZ4MeXF3u89TBLWvXDpPy/YQU -EiKpodN0YeEn6Ghzplan8rUha+7TP7AYnS5pCszsCHKd03Py0lMLkF+uAfVsDA== ------END RSA PRIVATE KEY----- diff --git a/packages/Tethering/apex/com.android.tethering.pk8 b/packages/Tethering/apex/com.android.tethering.pk8 deleted file mode 100644 index 3b94405945cb586956fdd12a15b4d6746fcd021c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2375 zcmV-N3Apw!f(b(c0RS)!1_>&LNQUwEii%!DFOii0)heo0LJvx1B)JR zRwHD$(^T9UxRL$WgcUSYQKEMkMGP z;A5~!v+~X9ADc2V37wd03@-YCun%TU&6HX^>K4X4D1;S|>THs_@qI-UTi;D8n;cXa zQ#~pz+N!msVo}691A;T?2wxTjJG8#ZOyBGH3}myj*@~ZWGp?tIK*rHAN4es<+(}Br zOFR{no2EM;J!-f_>Sg>17cjjw4mbNy~6b<2GpWL7fjrAU8tCi#lBXAKX=p_t-#vIr}`H_kxYHMduor z%<3XcrG{C=3xQ)<&Vk2_EE1dnF_WYh`$ibXSbt!&^{*|Y{Q?64009Dm0ssalPGf1S zAV`D3y1C1jA1H}KYo-S+-Y!b@r!<-3RV^J<<{It@<~^y-$5e0)TuJo@z^FTjoGL!A zqyuz)l;5k7032K8ft{d0L5vB!`e0oDH;Vh+bQXe8PO_d#jGxXg_RXzHTM}*5W<)U} z2(qyPqtx&Ilx9fVjh3X0F-vGHkyJ~yqf}>cKMxXfK`RrY!yl~*0K_#`tf7X_kub>k zgya@6HpknY(5A4~yXc7DXdcok5iwql#1$W#;mIgiGKkpo!-0^tSIvPKxk!1WT$wSS zAz>a&>C6Y{~=LS}vF-xY|>H0fpo)7|-A%23+ejc5|& zKp z$9Z~1zmfe6qVf_hC}VSaJRzOIq|#j!?{r*fAsDEdY@`>0CK63=!D6fR25n=&|v2t(9OC^*^3D&%XBJV2I$nNP6)Qt1wOJ~5uAvU z!eNd6{?J*ww_75Mhi_)Ru)7BB)~a-n#fl_y&p2gAk<*h01WaM9VatZth?`N>y zUWc7Qw1tl(?33pD`M~a zDlc8)N|9ckR;bW`?LMeeGhw-^0-nViR)xo0mI!b8n(w4 z)qWhA>`b={SV=nf?^w<6=-A&bH`XAfv5y1NkdBEKJ#2anS{JlnryKL7fTg3&26j2+ zi=_BEL<>`|2y;rM5cpyyTMR3}1zLD)MZ7qwiiZyYy{bAzxqx3CVuzZ@rC%yh+VFvn zTqq4t>}Dx#@Cx-;Y-#>3>=Lk%)zZ$EXRQ>qEtN(YG3jslt{3n!UGJvK=8TXfvORQs zzLE$Bbn3)@2Zg)pG~>(cOU!u5s6(ibf9_a;&R6w8??CRbmBS*;#9wz0mPf=SlycN4VMmVZOgq;&>A_h z=P(6iqq!z^Z^DBFzpCNrBJ6jO3gedXBfYuOP`*S?>;T$+?yj5h-3>Eo4jp&x`i=C`Cj^pv?Zi$N1;~CRt_FP1{$tqD zNoh_wUvZ_YTgd=o%7)tB{3&4+?v>ouO_G6T`J8%X*3(a0 zX-TI-x|E!N(iH9ianMzJuT`ZgABejgkNW diff --git a/packages/Tethering/apex/com.android.tethering.x509.pem b/packages/Tethering/apex/com.android.tethering.x509.pem deleted file mode 100644 index a1786e35e854..000000000000 --- a/packages/Tethering/apex/com.android.tethering.x509.pem +++ /dev/null @@ -1,35 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGKTCCBBGgAwIBAgIUNiSs5EMqxCZ31gWWCcRJVp9HffAwDQYJKoZIhvcNAQEL -BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH -DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy -b2lkMR4wHAYDVQQDDBVjb20uYW5kcm9pZC50ZXRoZXJpbmcxIjAgBgkqhkiG9w0B -CQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMjE4MDcwMDQ4WhgPNDc1NzEx -MTMwNzAwNDhaMIGiMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW -MBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UE -CwwHQW5kcm9pZDEeMBwGA1UEAwwVY29tLmFuZHJvaWQudGV0aGVyaW5nMSIwIAYJ -KoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEAxvTUA4seblYjZLfTVNwZuJH914QVNFTj+vD94pWmt5Aq -sH1DVTpBvpXXegc/P5HI2XF/71poSBib1WaQSuXG0fU5K75T18bOGL0qF+fhMtBO -wUyvulcjO0h4XE/xf0txY54exUjAA4JS9ERGJOgb4GOwSbPyzekfmzIyCZ2Yawwu -+oGwD2ZNzZRaPOoWxjwohBWQ6mySuvF9RRRb300qmxxUGFM9Ki3aqrWlYlHEOwOC -M+gIXxYFO7S+yUzf6/gMZLOz2YqfcTOup4hAxtExR7niutxJSsRLPBL237exAJoz -OupoXjtWAlPK4ZwZ/Nl1jdTWauJ+Kv3WqzhHGEb2gn3ZpeO3IdOjJhDgFJ6m1OT/ -kjRbW1LCuKGrKaoqsEDT2X3a7Izfripn65hSNTfR5gNLtgELaI3/vXi8Fmzw1AfH -+qi6ulElZvSwx0qm+S0QiPyGFlxrsdnHoGJl1tzjJW8KdNZRvzRLUQtbphPp+VkL -5i0bNKum+AwbfdUkLkNLfw9XdbujgBkZTZDQbZGsNjgrvyXcPO2KiJee0hVCZRs0 -rhDi5Pfm7BnN/I2vaTRz/W4mdct9H2RWMuqlSH90JvmKtWcND8ahmOJ3sggrvzfO -QNs3k4JTRecamMzqIkylhlnEC4FjWc6Bx4wsEpwBMZOkF/tGGMZYf2C09a8tpP0C -AwEAAaNTMFEwHQYDVR0OBBYEFNP5gIpNWmq0xa411M1GaRPbEijvMB8GA1UdIwQY -MBaAFNP5gIpNWmq0xa411M1GaRPbEijvMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI -hvcNAQELBQADggIBADJGmU3QP4EGbt6eBhVPeo/efsqrHsuB2fvFzvIobJbfkSob -cmvjbzIikOlPAgFWj8lT5SDcIWRorFf1u2JylClJ0nSDcqJMHVKmT7wseV/KtX// -1yUyJFRQVzmjC89dp8OIc00GmItivKLer3NbJdkR3rTUjg7+bNUO27Qp3AFREmiJ -P+M7ouvcQRvByUWbp/LOrJpMdJLysRBO562RwrtwTjltdvufyYswbBZOKEiUh1Jc -Ged+3+SJdhwq3Wy+R3Uj7YE7mUMu1QNbANIMrwF8W93EA53eoL2+cKmuaVU6ZURL -xgSJaY6TrunnSI9XTROLtjsFlJorYWy2tvG7Q5Hw3OkO2Xdz/mm85VTkiusg9DMB -WWTv607YtsIO0FhKmcV4bp3q/EkRj3t/zLvL9uFJrWDGkuShZq6fQvqbCvaokOPY -+M0ZRIwgwa9UpEE0BMklVWqR6BGyap614gOgcOjYM70WRNl59Qne+g128ZN7g9nz -61F70i7kUngV0ZUz1/Fu/NCG+6wGF85ZbFmQl60YHPDw1FtjVUuKyBblaDzdJunx -yQr2t9RUokzFBFK0lGW3+yf0WDQ5fqTMs5h8bz1FCq8/HzWmpdOfqePLe4zsld3b -1nFuSohaIfbn/HDdTNtTBGQPgz8ZswQ6ejJJqTLz9D/odbqn9LeIhDZXcQTf ------END CERTIFICATE----- diff --git a/packages/Tethering/apex/manifest.json b/packages/Tethering/apex/manifest.json deleted file mode 100644 index 11e205d1b7ab..000000000000 --- a/packages/Tethering/apex/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.tethering", - "version": 309999900 -} diff --git a/packages/Tethering/bpf_progs/Android.bp b/packages/Tethering/bpf_progs/Android.bp deleted file mode 100644 index d54f86148665..000000000000 --- a/packages/Tethering/bpf_progs/Android.bp +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -// -// bpf kernel programs -// -bpf { - name: "offload.o", - srcs: ["offload.c"], - cflags: [ - "-Wall", - "-Werror", - ], - include_dirs: [ - // TODO: get rid of system/netd. - "system/netd/bpf_progs", // for bpf_net_helpers.h - "system/netd/libnetdbpf/include", // for bpf_shared.h - "system/netd/libnetdutils/include", // for UidConstants.h - ], -} diff --git a/packages/Tethering/bpf_progs/offload.c b/packages/Tethering/bpf_progs/offload.c deleted file mode 100644 index cc5af3127b02..000000000000 --- a/packages/Tethering/bpf_progs/offload.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include - -#include "bpf_helpers.h" -#include "bpf_net_helpers.h" -#include "netdbpf/bpf_shared.h" - -DEFINE_BPF_MAP_GRW(tether_ingress_map, HASH, TetherIngressKey, TetherIngressValue, 64, - AID_NETWORK_STACK) - -// Tethering stats, indexed by upstream interface. -DEFINE_BPF_MAP_GRW(tether_stats_map, HASH, uint32_t, TetherStatsValue, 16, AID_NETWORK_STACK) - -// Tethering data limit, indexed by upstream interface. -// (tethering allowed when stats[iif].rxBytes + stats[iif].txBytes < limit[iif]) -DEFINE_BPF_MAP_GRW(tether_limit_map, HASH, uint32_t, uint64_t, 16, AID_NETWORK_STACK) - -static inline __always_inline int do_forward(struct __sk_buff* skb, bool is_ethernet) { - int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0; - void* data = (void*)(long)skb->data; - const void* data_end = (void*)(long)skb->data_end; - struct ethhdr* eth = is_ethernet ? data : NULL; // used iff is_ethernet - struct ipv6hdr* ip6 = is_ethernet ? (void*)(eth + 1) : data; - - // Must be meta-ethernet IPv6 frame - if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_OK; - - // Must have (ethernet and) ipv6 header - if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_OK; - - // Ethertype - if present - must be IPv6 - if (is_ethernet && (eth->h_proto != htons(ETH_P_IPV6))) return TC_ACT_OK; - - // IP version must be 6 - if (ip6->version != 6) return TC_ACT_OK; - - // Cannot decrement during forward if already zero or would be zero, - // Let the kernel's stack handle these cases and generate appropriate ICMP errors. - if (ip6->hop_limit <= 1) return TC_ACT_OK; - - // Protect against forwarding packets sourced from ::1 or fe80::/64 or other weirdness. - __be32 src32 = ip6->saddr.s6_addr32[0]; - if (src32 != htonl(0x0064ff9b) && // 64:ff9b:/32 incl. XLAT464 WKP - (src32 & htonl(0xe0000000)) != htonl(0x20000000)) // 2000::/3 Global Unicast - return TC_ACT_OK; - - TetherIngressKey k = { - .iif = skb->ifindex, - .neigh6 = ip6->daddr, - }; - - TetherIngressValue* v = bpf_tether_ingress_map_lookup_elem(&k); - - // If we don't find any offload information then simply let the core stack handle it... - if (!v) return TC_ACT_OK; - - uint32_t stat_and_limit_k = skb->ifindex; - - TetherStatsValue* stat_v = bpf_tether_stats_map_lookup_elem(&stat_and_limit_k); - - // If we don't have anywhere to put stats, then abort... - if (!stat_v) return TC_ACT_OK; - - uint64_t* limit_v = bpf_tether_limit_map_lookup_elem(&stat_and_limit_k); - - // If we don't have a limit, then abort... - if (!limit_v) return TC_ACT_OK; - - // Required IPv6 minimum mtu is 1280, below that not clear what we should do, abort... - const int pmtu = v->pmtu; - if (pmtu < IPV6_MIN_MTU) return TC_ACT_OK; - - // Approximate handling of TCP/IPv6 overhead for incoming LRO/GRO packets: default - // outbound path mtu of 1500 is not necessarily correct, but worst case we simply - // undercount, which is still better then not accounting for this overhead at all. - // Note: this really shouldn't be device/path mtu at all, but rather should be - // derived from this particular connection's mss (ie. from gro segment size). - // This would require a much newer kernel with newer ebpf accessors. - // (This is also blindly assuming 12 bytes of tcp timestamp option in tcp header) - uint64_t packets = 1; - uint64_t bytes = skb->len; - if (bytes > pmtu) { - const int tcp_overhead = sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + 12; - const int mss = pmtu - tcp_overhead; - const uint64_t payload = bytes - tcp_overhead; - packets = (payload + mss - 1) / mss; - bytes = tcp_overhead * packets + payload; - } - - // Are we past the limit? If so, then abort... - // Note: will not overflow since u64 is 936 years even at 5Gbps. - // Do not drop here. Offload is just that, whenever we fail to handle - // a packet we let the core stack deal with things. - // (The core stack needs to handle limits correctly anyway, - // since we don't offload all traffic in both directions) - if (stat_v->rxBytes + stat_v->txBytes + bytes > *limit_v) return TC_ACT_OK; - - if (!is_ethernet) { - is_ethernet = true; - l2_header_size = sizeof(struct ethhdr); - // Try to inject an ethernet header, and simply return if we fail - if (bpf_skb_change_head(skb, l2_header_size, /*flags*/ 0)) { - __sync_fetch_and_add(&stat_v->rxErrors, 1); - return TC_ACT_OK; - } - - // bpf_skb_change_head() invalidates all pointers - reload them - data = (void*)(long)skb->data; - data_end = (void*)(long)skb->data_end; - eth = data; - ip6 = (void*)(eth + 1); - - // I do not believe this can ever happen, but keep the verifier happy... - if (data + l2_header_size + sizeof(*ip6) > data_end) { - __sync_fetch_and_add(&stat_v->rxErrors, 1); - return TC_ACT_SHOT; - } - }; - - // CHECKSUM_COMPLETE is a 16-bit one's complement sum, - // thus corrections for it need to be done in 16-byte chunks at even offsets. - // IPv6 nexthdr is at offset 6, while hop limit is at offset 7 - uint8_t old_hl = ip6->hop_limit; - --ip6->hop_limit; - uint8_t new_hl = ip6->hop_limit; - - // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error - // (-ENOTSUPP) if it isn't. - bpf_csum_update(skb, 0xFFFF - ntohs(old_hl) + ntohs(new_hl)); - - __sync_fetch_and_add(&stat_v->rxPackets, packets); - __sync_fetch_and_add(&stat_v->rxBytes, bytes); - - // Overwrite any mac header with the new one - *eth = v->macHeader; - - // Redirect to forwarded interface. - // - // Note that bpf_redirect() cannot fail unless you pass invalid flags. - // The redirect actually happens after the ebpf program has already terminated, - // and can fail for example for mtu reasons at that point in time, but there's nothing - // we can do about it here. - return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */); -} - -SEC("schedcls/ingress/tether_ether") -int sched_cls_ingress_tether_ether(struct __sk_buff* skb) { - return do_forward(skb, true); -} - -// Note: section names must be unique to prevent programs from appending to each other, -// so instead the bpf loader will strip everything past the final $ symbol when actually -// pinning the program into the filesystem. -// -// bpf_skb_change_head() is only present on 4.14+ and 2 trivial kernel patches are needed: -// ANDROID: net: bpf: Allow TC programs to call BPF_FUNC_skb_change_head -// ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu -// (the first of those has already been upstreamed) -// -// 5.4 kernel support was only added to Android Common Kernel in R, -// and thus a 5.4 kernel always supports this. -// -// Hence, this mandatory (must load successfully) implementation for 5.4+ kernels: -DEFINE_BPF_PROG_KVER("schedcls/ingress/tether_rawip$5_4", AID_ROOT, AID_ROOT, - sched_cls_ingress_tether_rawip_5_4, KVER(5, 4, 0)) -(struct __sk_buff* skb) { - return do_forward(skb, false); -} - -// and this identical optional (may fail to load) implementation for [4.14..5.4) patched kernels: -DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$4_14", AID_ROOT, AID_ROOT, - sched_cls_ingress_tether_rawip_4_14, KVER(4, 14, 0), - KVER(5, 4, 0)) -(struct __sk_buff* skb) { - return do_forward(skb, false); -} - -// and define a no-op stub for [4.9,4.14) and unpatched [4.14,5.4) kernels. -// (if the above real 4.14+ program loaded successfully, then bpfloader will have already pinned -// it at the same location this one would be pinned at and will thus skip loading this stub) -DEFINE_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$stub", AID_ROOT, AID_ROOT, - sched_cls_ingress_tether_rawip_stub, KVER_NONE, KVER(5, 4, 0)) -(struct __sk_buff* skb) { - return TC_ACT_OK; -} - -LICENSE("Apache 2.0"); -CRITICAL("netd"); diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp deleted file mode 100644 index ddb6880753b4..000000000000 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -java_sdk_library { - name: "framework-tethering", - defaults: ["framework-module-defaults"], - impl_library_visibility: [ - "//frameworks/base/packages/Tethering:__subpackages__", - "//packages/modules/Connectivity/Tethering:__subpackages__", - ], - - srcs: [":framework-tethering-srcs"], - - jarjar_rules: "jarjar-rules.txt", - installable: true, - - hostdex: true, // for hiddenapi check - apex_available: ["com.android.tethering"], - permitted_packages: ["android.net"], -} - -filegroup { - name: "framework-tethering-srcs", - srcs: [ - "src/android/net/TetheredClient.aidl", - "src/android/net/TetheredClient.java", - "src/android/net/TetheringManager.java", - "src/android/net/TetheringConstants.java", - "src/android/net/IIntResultListener.aidl", - "src/android/net/ITetheringEventCallback.aidl", - "src/android/net/ITetheringConnector.aidl", - "src/android/net/TetheringCallbackStartedParcel.aidl", - "src/android/net/TetheringConfigurationParcel.aidl", - "src/android/net/TetheringRequestParcel.aidl", - "src/android/net/TetherStatesParcel.aidl", - ], - path: "src" -} diff --git a/packages/Tethering/common/TetheringLib/api/current.txt b/packages/Tethering/common/TetheringLib/api/current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/packages/Tethering/common/TetheringLib/api/current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt deleted file mode 100644 index 6ddb122936e7..000000000000 --- a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt +++ /dev/null @@ -1,41 +0,0 @@ -// Signature format: 2.0 -package android.net { - - public final class TetheringConstants { - field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; - field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; - field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; - field public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; - field public static final String EXTRA_SET_ALARM = "extraSetAlarm"; - } - - public class TetheringManager { - ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier); - method public int getLastTetherError(@NonNull String); - method @NonNull public String[] getTetherableBluetoothRegexs(); - method @NonNull public String[] getTetherableIfaces(); - method @NonNull public String[] getTetherableUsbRegexs(); - method @NonNull public String[] getTetherableWifiRegexs(); - method @NonNull public String[] getTetheredIfaces(); - method @NonNull public String[] getTetheringErroredIfaces(); - method public boolean isTetheringSupported(); - method public boolean isTetheringSupported(@NonNull String); - method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean); - method @Deprecated public int setUsbTethering(boolean); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); - method @Deprecated public int tether(@NonNull String); - method @Deprecated public int untether(@NonNull String); - } - - public static interface TetheringManager.TetheringEventCallback { - method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); - } - - public static class TetheringManager.TetheringInterfaceRegexps { - method @NonNull public java.util.List getTetherableBluetoothRegexs(); - method @NonNull public java.util.List getTetherableUsbRegexs(); - method @NonNull public java.util.List getTetherableWifiRegexs(); - } - -} - diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt b/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/packages/Tethering/common/TetheringLib/api/removed.txt b/packages/Tethering/common/TetheringLib/api/removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/packages/Tethering/common/TetheringLib/api/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/packages/Tethering/common/TetheringLib/api/system-current.txt b/packages/Tethering/common/TetheringLib/api/system-current.txt deleted file mode 100644 index edd1ebb5f751..000000000000 --- a/packages/Tethering/common/TetheringLib/api/system-current.txt +++ /dev/null @@ -1,99 +0,0 @@ -// Signature format: 2.0 -package android.net { - - public final class TetheredClient implements android.os.Parcelable { - ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection, int); - method public int describeContents(); - method @NonNull public java.util.List getAddresses(); - method @NonNull public android.net.MacAddress getMacAddress(); - method public int getTetheringType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; - } - - public static final class TetheredClient.AddressInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public android.net.LinkAddress getAddress(); - method @Nullable public String getHostname(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; - } - - public class TetheringManager { - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering(); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); - field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; - field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; - field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; - field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; - field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; - field public static final int TETHERING_BLUETOOTH = 2; // 0x2 - field public static final int TETHERING_ETHERNET = 5; // 0x5 - field public static final int TETHERING_INVALID = -1; // 0xffffffff - field public static final int TETHERING_NCM = 4; // 0x4 - field public static final int TETHERING_USB = 1; // 0x1 - field public static final int TETHERING_WIFI = 0; // 0x0 - field public static final int TETHERING_WIFI_P2P = 3; // 0x3 - field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc - field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9 - field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8 - field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd - field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa - field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5 - field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf - field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe - field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 - field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb - field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 - field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 - field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 - field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 - field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10 - field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 - field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 - field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2 - field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1 - field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0 - } - - public static interface TetheringManager.OnTetheringEntitlementResultListener { - method public void onTetheringEntitlementResult(int); - } - - public static interface TetheringManager.StartTetheringCallback { - method public default void onTetheringFailed(int); - method public default void onTetheringStarted(); - } - - public static interface TetheringManager.TetheringEventCallback { - method public default void onClientsChanged(@NonNull java.util.Collection); - method public default void onError(@NonNull String, int); - method public default void onOffloadStatusChanged(int); - method public default void onTetherableInterfacesChanged(@NonNull java.util.List); - method public default void onTetheredInterfacesChanged(@NonNull java.util.List); - method public default void onTetheringSupported(boolean); - method public default void onUpstreamChanged(@Nullable android.net.Network); - } - - public static class TetheringManager.TetheringRequest { - method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); - method @Nullable public android.net.LinkAddress getLocalIpv4Address(); - method public boolean getShouldShowEntitlementUi(); - method public int getTetheringType(); - method public boolean isExemptFromEntitlementCheck(); - } - - public static class TetheringManager.TetheringRequest.Builder { - ctor public TetheringManager.TetheringRequest.Builder(int); - method @NonNull public android.net.TetheringManager.TetheringRequest build(); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); - } - -} - diff --git a/packages/Tethering/common/TetheringLib/api/system-removed.txt b/packages/Tethering/common/TetheringLib/api/system-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/packages/Tethering/common/TetheringLib/api/system-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/packages/Tethering/common/TetheringLib/jarjar-rules.txt b/packages/Tethering/common/TetheringLib/jarjar-rules.txt deleted file mode 100644 index e459fad54993..000000000000 --- a/packages/Tethering/common/TetheringLib/jarjar-rules.txt +++ /dev/null @@ -1 +0,0 @@ -# jarjar rules for the bootclasspath tethering framework library here \ No newline at end of file diff --git a/packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl b/packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl deleted file mode 100644 index c3d66ee14526..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -/** - * Listener interface allowing objects to listen to various module event. - * {@hide} - */ -oneway interface IIntResultListener { - void onResult(int resultCode); -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl deleted file mode 100644 index cf094aac2cbe..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net; - -import android.net.IIntResultListener; -import android.net.ITetheringEventCallback; -import android.net.TetheringRequestParcel; -import android.os.ResultReceiver; - -/** @hide */ -oneway interface ITetheringConnector { - void tether(String iface, String callerPkg, String callingAttributionTag, - IIntResultListener receiver); - - void untether(String iface, String callerPkg, String callingAttributionTag, - IIntResultListener receiver); - - void setUsbTethering(boolean enable, String callerPkg, - String callingAttributionTag, IIntResultListener receiver); - - void startTethering(in TetheringRequestParcel request, String callerPkg, - String callingAttributionTag, IIntResultListener receiver); - - void stopTethering(int type, String callerPkg, String callingAttributionTag, - IIntResultListener receiver); - - void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver, - boolean showEntitlementUi, String callerPkg, String callingAttributionTag); - - void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg); - - void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg); - - void isTetheringSupported(String callerPkg, String callingAttributionTag, - IIntResultListener receiver); - - void stopAllTethering(String callerPkg, String callingAttributionTag, - IIntResultListener receiver); -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl deleted file mode 100644 index b4e3ba46791c..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.net.Network; -import android.net.TetheredClient; -import android.net.TetheringConfigurationParcel; -import android.net.TetheringCallbackStartedParcel; -import android.net.TetherStatesParcel; - -/** - * Callback class for receiving tethering changed events. - * @hide - */ -oneway interface ITetheringEventCallback -{ - /** Called immediately after the callbacks are registered */ - void onCallbackStarted(in TetheringCallbackStartedParcel parcel); - void onCallbackStopped(int errorCode); - void onUpstreamChanged(in Network network); - void onConfigurationChanged(in TetheringConfigurationParcel config); - void onTetherStatesChanged(in TetherStatesParcel states); - void onTetherClientsChanged(in List clients); - void onOffloadStatusChanged(int status); -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl deleted file mode 100644 index 3d842b337428..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -/** - * Status details for tethering downstream interfaces. - * {@hide} - */ -parcelable TetherStatesParcel { - String[] availableList; - String[] tetheredList; - String[] localOnlyList; - String[] erroredIfaceList; - // List of Last error code corresponding to each errored iface in erroredIfaceList. */ - // TODO: Improve this as b/143122247. - int[] lastErrorList; -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl deleted file mode 100644 index 0b279b882367..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net; - -@JavaOnlyStableParcelable parcelable TetheredClient; \ No newline at end of file diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java deleted file mode 100644 index 0b223f42b954..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Objects; - -/** - * Information on a tethered downstream client. - * @hide - */ -@SystemApi -public final class TetheredClient implements Parcelable { - @NonNull - private final MacAddress mMacAddress; - @NonNull - private final List mAddresses; - // TODO: use an @IntDef here - private final int mTetheringType; - - public TetheredClient(@NonNull MacAddress macAddress, - @NonNull Collection addresses, int tetheringType) { - mMacAddress = macAddress; - mAddresses = new ArrayList<>(addresses); - mTetheringType = tetheringType; - } - - private TetheredClient(@NonNull Parcel in) { - this(in.readParcelable(null), in.createTypedArrayList(AddressInfo.CREATOR), in.readInt()); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mMacAddress, flags); - dest.writeTypedList(mAddresses); - dest.writeInt(mTetheringType); - } - - /** - * Get the MAC address used to identify the client. - */ - @NonNull - public MacAddress getMacAddress() { - return mMacAddress; - } - - /** - * Get information on the list of addresses that are associated with the client. - */ - @NonNull - public List getAddresses() { - return new ArrayList<>(mAddresses); - } - - /** - * Get the type of tethering used by the client. - * @return one of the {@code TetheringManager#TETHERING_*} constants. - */ - public int getTetheringType() { - return mTetheringType; - } - - /** - * Return a new {@link TetheredClient} that has all the attributes of this instance, plus the - * {@link AddressInfo} of the provided {@link TetheredClient}. - * - *

Duplicate addresses are removed. - * @hide - */ - public TetheredClient addAddresses(@NonNull TetheredClient other) { - final LinkedHashSet newAddresses = new LinkedHashSet<>( - mAddresses.size() + other.mAddresses.size()); - newAddresses.addAll(mAddresses); - newAddresses.addAll(other.mAddresses); - return new TetheredClient(mMacAddress, newAddresses, mTetheringType); - } - - @Override - public int hashCode() { - return Objects.hash(mMacAddress, mAddresses, mTetheringType); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof TetheredClient)) return false; - final TetheredClient other = (TetheredClient) obj; - return mMacAddress.equals(other.mMacAddress) - && mAddresses.equals(other.mAddresses) - && mTetheringType == other.mTetheringType; - } - - /** - * Information on an lease assigned to a tethered client. - */ - public static final class AddressInfo implements Parcelable { - @NonNull - private final LinkAddress mAddress; - @Nullable - private final String mHostname; - - /** @hide */ - public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) { - this.mAddress = address; - this.mHostname = hostname; - } - - private AddressInfo(Parcel in) { - this(in.readParcelable(null), in.readString()); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mAddress, flags); - dest.writeString(mHostname); - } - - /** - * Get the link address (including prefix length and lifetime) used by the client. - * - * This may be an IPv4 or IPv6 address. - */ - @NonNull - public LinkAddress getAddress() { - return mAddress; - } - - /** - * Get the hostname that was advertised by the client when obtaining its address, if any. - */ - @Nullable - public String getHostname() { - return mHostname; - } - - /** - * Get the expiration time of the address assigned to the client. - * @hide - */ - public long getExpirationTime() { - return mAddress.getExpirationTime(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public int hashCode() { - return Objects.hash(mAddress, mHostname); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof AddressInfo)) return false; - final AddressInfo other = (AddressInfo) obj; - // Use .equals() for addresses as all changes, including address expiry changes, - // should be included. - return other.mAddress.equals(mAddress) - && Objects.equals(mHostname, other.mHostname); - } - - @NonNull - public static final Creator CREATOR = new Creator() { - @NonNull - @Override - public AddressInfo createFromParcel(@NonNull Parcel in) { - return new AddressInfo(in); - } - - @NonNull - @Override - public AddressInfo[] newArray(int size) { - return new AddressInfo[size]; - } - }; - - @NonNull - @Override - public String toString() { - return "AddressInfo {" - + mAddress - + (mHostname != null ? ", hostname " + mHostname : "") - + "}"; - } - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - public static final Creator CREATOR = new Creator() { - @NonNull - @Override - public TetheredClient createFromParcel(@NonNull Parcel in) { - return new TetheredClient(in); - } - - @NonNull - @Override - public TetheredClient[] newArray(int size) { - return new TetheredClient[size]; - } - }; - - @NonNull - @Override - public String toString() { - return "TetheredClient {hwAddr " + mMacAddress - + ", addresses " + mAddresses - + ", tetheringType " + mTetheringType - + "}"; - } -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl deleted file mode 100644 index 253eacbd23e7..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.net.Network; -import android.net.TetheredClient; -import android.net.TetheringConfigurationParcel; -import android.net.TetherStatesParcel; - -/** - * Initial information reported by tethering upon callback registration. - * @hide - */ -parcelable TetheringCallbackStartedParcel { - boolean tetheringSupported; - Network upstreamNetwork; - TetheringConfigurationParcel config; - TetherStatesParcel states; - List tetheredClients; - int offloadStatus; -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl deleted file mode 100644 index 89f38132ffad..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -/** - * Configuration details for tethering. - * @hide - */ -parcelable TetheringConfigurationParcel { - int subId; - String[] tetherableUsbRegexs; - String[] tetherableWifiRegexs; - String[] tetherableBluetoothRegexs; - boolean isDunRequired; - boolean chooseUpstreamAutomatically; - int[] preferredUpstreamIfaceTypes; - String[] legacyDhcpRanges; - String[] defaultIPv4DNS; - boolean enableLegacyDhcpServer; - String[] provisioningApp; - String provisioningAppNoUi; - int provisioningCheckPeriod; -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java deleted file mode 100644 index f14def6a3a02..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; - -import android.annotation.SystemApi; -import android.os.ResultReceiver; - -/** - * Collections of constants for internal tethering usage. - * - *

These hidden constants are not in TetheringManager as they are not part of the API stubs - * generated for TetheringManager, which prevents the tethering module from linking them at - * build time. - * TODO: investigate changing the tethering build rules so that Tethering can reference hidden - * symbols from framework-tethering even when they are in a non-hidden class. - * @hide - */ -@SystemApi(client = MODULE_LIBRARIES) -public final class TetheringConstants { - /** An explicit private class to avoid exposing constructor.*/ - private TetheringConstants() { } - - /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Includes the type of tethering to enable if any. - */ - public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; - /** - * Extra used for communicating with the TetherService. Includes the type of tethering for - * which to cancel provisioning. - */ - public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; - /** - * Extra used for communicating with the TetherService. True to schedule a recheck of tether - * provisioning. - */ - public static final String EXTRA_SET_ALARM = "extraSetAlarm"; - /** - * Tells the TetherService to run a provision check now. - */ - public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; - /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Contains the {@link ResultReceiver} which will receive provisioning results. - * Can not be empty. - */ - public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; - - /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Contains the subId of current active cellular upstream. - * @hide - */ - public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID"; - - /** - * Extra used for telling TetherProvisioningActivity the entitlement package name and class - * name to start UI entitlement check. - * @hide - */ - public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME = - "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME"; - - /** - * Extra used for telling TetherService the intent action to start silent entitlement check. - * @hide - */ - public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION = - "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION"; - - /** - * Extra used for TetherService to receive the response of provisioning check. - * @hide - */ - public static final String EXTRA_TETHER_PROVISIONING_RESPONSE = - "android.net.extra.TETHER_PROVISIONING_RESPONSE"; -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java deleted file mode 100644 index 13b05a841d24..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ /dev/null @@ -1,1364 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net; - -import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; - -import android.Manifest; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SystemApi; -import android.content.Context; -import android.os.Bundle; -import android.os.ConditionVariable; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Supplier; - -/** - * This class provides the APIs to control the tethering service. - *

The primary responsibilities of this class are to provide the APIs for applications to - * start tethering, stop tethering, query configuration and query status. - * - * @hide - */ -@SystemApi -public class TetheringManager { - private static final String TAG = TetheringManager.class.getSimpleName(); - private static final int DEFAULT_TIMEOUT_MS = 60_000; - private static final long CONNECTOR_POLL_INTERVAL_MILLIS = 200L; - - @GuardedBy("mConnectorWaitQueue") - @Nullable - private ITetheringConnector mConnector; - @GuardedBy("mConnectorWaitQueue") - @NonNull - private final List mConnectorWaitQueue = new ArrayList<>(); - private final Supplier mConnectorSupplier; - - private final TetheringCallbackInternal mCallback; - private final Context mContext; - private final ArrayMap - mTetheringEventCallbacks = new ArrayMap<>(); - - private volatile TetheringConfigurationParcel mTetheringConfiguration; - private volatile TetherStatesParcel mTetherStatesParcel; - - /** - * Broadcast Action: A tetherable connection has come or gone. - * Uses {@code TetheringManager.EXTRA_AVAILABLE_TETHER}, - * {@code TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY}, - * {@code TetheringManager.EXTRA_ACTIVE_TETHER}, and - * {@code TetheringManager.EXTRA_ERRORED_TETHER} to indicate - * the current state of tethering. Each include a list of - * interface names in that state (may be empty). - */ - public static final String ACTION_TETHER_STATE_CHANGED = - "android.net.conn.TETHER_STATE_CHANGED"; - - /** - * gives a String[] listing all the interfaces configured for - * tethering and currently available for tethering. - */ - public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; - - /** - * gives a String[] listing all the interfaces currently in local-only - * mode (ie, has DHCPv4+IPv6-ULA support and no packet forwarding) - */ - public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; - - /** - * gives a String[] listing all the interfaces currently tethered - * (ie, has DHCPv4 support and packets potentially forwarded/NATed) - */ - public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; - - /** - * gives a String[] listing all the interfaces we tried to tether and - * failed. Use {@link #getLastTetherError} to find the error code - * for any interfaces listed here. - */ - public static final String EXTRA_ERRORED_TETHER = "erroredArray"; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = false, value = { - TETHERING_WIFI, - TETHERING_USB, - TETHERING_BLUETOOTH, - TETHERING_WIFI_P2P, - TETHERING_NCM, - TETHERING_ETHERNET, - }) - public @interface TetheringType { - } - - /** - * Invalid tethering type. - * @see #startTethering. - */ - public static final int TETHERING_INVALID = -1; - - /** - * Wifi tethering type. - * @see #startTethering. - */ - public static final int TETHERING_WIFI = 0; - - /** - * USB tethering type. - * @see #startTethering. - */ - public static final int TETHERING_USB = 1; - - /** - * Bluetooth tethering type. - * @see #startTethering. - */ - public static final int TETHERING_BLUETOOTH = 2; - - /** - * Wifi P2p tethering type. - * Wifi P2p tethering is set through events automatically, and don't - * need to start from #startTethering. - */ - public static final int TETHERING_WIFI_P2P = 3; - - /** - * Ncm local tethering type. - * @see #startTethering(TetheringRequest, Executor, StartTetheringCallback) - */ - public static final int TETHERING_NCM = 4; - - /** - * Ethernet tethering type. - * @see #startTethering(TetheringRequest, Executor, StartTetheringCallback) - */ - public static final int TETHERING_ETHERNET = 5; - - /** - * WIGIG tethering type. Use a separate type to prevent - * conflicts with TETHERING_WIFI - * This type is only used internally by the tethering module - * @hide - */ - public static final int TETHERING_WIGIG = 6; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - TETHER_ERROR_NO_ERROR, - TETHER_ERROR_PROVISIONING_FAILED, - TETHER_ERROR_ENTITLEMENT_UNKNOWN, - }) - public @interface EntitlementResult { - } - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - TETHER_ERROR_NO_ERROR, - TETHER_ERROR_UNKNOWN_IFACE, - TETHER_ERROR_SERVICE_UNAVAIL, - TETHER_ERROR_INTERNAL_ERROR, - TETHER_ERROR_TETHER_IFACE_ERROR, - TETHER_ERROR_ENABLE_FORWARDING_ERROR, - TETHER_ERROR_DISABLE_FORWARDING_ERROR, - TETHER_ERROR_IFACE_CFG_ERROR, - TETHER_ERROR_DHCPSERVER_ERROR, - }) - public @interface TetheringIfaceError { - } - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - TETHER_ERROR_SERVICE_UNAVAIL, - TETHER_ERROR_INTERNAL_ERROR, - TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, - TETHER_ERROR_UNKNOWN_TYPE, - }) - public @interface StartTetheringError { - } - - public static final int TETHER_ERROR_NO_ERROR = 0; - public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; - public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; - public static final int TETHER_ERROR_UNSUPPORTED = 3; - public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; - public static final int TETHER_ERROR_INTERNAL_ERROR = 5; - public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; - public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; - public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; - public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; - public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; - public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; - public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; - public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; - public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; - public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; - public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = false, value = { - TETHER_HARDWARE_OFFLOAD_STOPPED, - TETHER_HARDWARE_OFFLOAD_STARTED, - TETHER_HARDWARE_OFFLOAD_FAILED, - }) - public @interface TetherOffloadStatus { - } - - /** Tethering offload status is stopped. */ - public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; - /** Tethering offload status is started. */ - public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; - /** Fail to start tethering offload. */ - public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; - - /** - * Create a TetheringManager object for interacting with the tethering service. - * - * @param context Context for the manager. - * @param connectorSupplier Supplier for the manager connector; may return null while the - * service is not connected. - * {@hide} - */ - @SystemApi(client = MODULE_LIBRARIES) - public TetheringManager(@NonNull final Context context, - @NonNull Supplier connectorSupplier) { - mContext = context; - mCallback = new TetheringCallbackInternal(); - mConnectorSupplier = connectorSupplier; - - final String pkgName = mContext.getOpPackageName(); - - final IBinder connector = mConnectorSupplier.get(); - // If the connector is available on start, do not start a polling thread. This introduces - // differences in the thread that sends the oneway binder calls to the service between the - // first few seconds after boot and later, but it avoids always having differences between - // the first usage of TetheringManager from a process and subsequent usages (so the - // difference is only on boot). On boot binder calls may be queued until the service comes - // up and be sent from a worker thread; later, they are always sent from the caller thread. - // Considering that it's just oneway binder calls, and ordering is preserved, this seems - // better than inconsistent behavior persisting after boot. - if (connector != null) { - mConnector = ITetheringConnector.Stub.asInterface(connector); - } else { - startPollingForConnector(); - } - - Log.i(TAG, "registerTetheringEventCallback:" + pkgName); - getConnector(c -> c.registerTetheringEventCallback(mCallback, pkgName)); - } - - private void startPollingForConnector() { - new Thread(() -> { - while (true) { - try { - Thread.sleep(CONNECTOR_POLL_INTERVAL_MILLIS); - } catch (InterruptedException e) { - // Not much to do here, the system needs to wait for the connector - } - - final IBinder connector = mConnectorSupplier.get(); - if (connector != null) { - onTetheringConnected(ITetheringConnector.Stub.asInterface(connector)); - return; - } - } - }).start(); - } - - private interface ConnectorConsumer { - void onConnectorAvailable(ITetheringConnector connector) throws RemoteException; - } - - private void onTetheringConnected(ITetheringConnector connector) { - // Process the connector wait queue in order, including any items that are added - // while processing. - // - // 1. Copy the queue to a local variable under lock. - // 2. Drain the local queue with the lock released (otherwise, enqueuing future commands - // would block on the lock). - // 3. Acquire the lock again. If any new tasks were queued during step 2, goto 1. - // If not, set mConnector to non-null so future tasks are run immediately, not queued. - // - // For this to work, all calls to the tethering service must use getConnector(), which - // ensures that tasks are added to the queue with the lock held. - // - // Once mConnector is set to non-null, it will never be null again. If the network stack - // process crashes, no recovery is possible. - // TODO: evaluate whether it is possible to recover from network stack process crashes - // (though in most cases the system will have crashed when the network stack process - // crashes). - do { - final List localWaitQueue; - synchronized (mConnectorWaitQueue) { - localWaitQueue = new ArrayList<>(mConnectorWaitQueue); - mConnectorWaitQueue.clear(); - } - - // Allow more tasks to be added at the end without blocking while draining the queue. - for (ConnectorConsumer task : localWaitQueue) { - try { - task.onConnectorAvailable(connector); - } catch (RemoteException e) { - // Most likely the network stack process crashed, which is likely to crash the - // system. Keep processing other requests but report the error loudly. - Log.wtf(TAG, "Error processing request for the tethering connector", e); - } - } - - synchronized (mConnectorWaitQueue) { - if (mConnectorWaitQueue.size() == 0) { - mConnector = connector; - return; - } - } - } while (true); - } - - /** - * Asynchronously get the ITetheringConnector to execute some operation. - * - *

If the connector is already available, the operation will be executed on the caller's - * thread. Otherwise it will be queued and executed on a worker thread. The operation should be - * limited to performing oneway binder calls to minimize differences due to threading. - */ - private void getConnector(ConnectorConsumer consumer) { - final ITetheringConnector connector; - synchronized (mConnectorWaitQueue) { - connector = mConnector; - if (connector == null) { - mConnectorWaitQueue.add(consumer); - return; - } - } - - try { - consumer.onConnectorAvailable(connector); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - } - - private interface RequestHelper { - void runRequest(ITetheringConnector connector, IIntResultListener listener); - } - - // Used to dispatch legacy ConnectivityManager methods that expect tethering to be able to - // return results and perform operations synchronously. - // TODO: remove once there are no callers of these legacy methods. - private class RequestDispatcher { - private final ConditionVariable mWaiting; - public volatile int mRemoteResult; - - private final IIntResultListener mListener = new IIntResultListener.Stub() { - @Override - public void onResult(final int resultCode) { - mRemoteResult = resultCode; - mWaiting.open(); - } - }; - - RequestDispatcher() { - mWaiting = new ConditionVariable(); - } - - int waitForResult(final RequestHelper request) { - getConnector(c -> request.runRequest(c, mListener)); - if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) { - throw new IllegalStateException("Callback timeout"); - } - - throwIfPermissionFailure(mRemoteResult); - - return mRemoteResult; - } - } - - private void throwIfPermissionFailure(final int errorCode) { - switch (errorCode) { - case TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION: - throw new SecurityException("No android.permission.TETHER_PRIVILEGED" - + " or android.permission.WRITE_SETTINGS permission"); - case TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION: - throw new SecurityException( - "No android.permission.ACCESS_NETWORK_STATE permission"); - } - } - - private class TetheringCallbackInternal extends ITetheringEventCallback.Stub { - private volatile int mError = TETHER_ERROR_NO_ERROR; - private final ConditionVariable mWaitForCallback = new ConditionVariable(); - - @Override - public void onCallbackStarted(TetheringCallbackStartedParcel parcel) { - mTetheringConfiguration = parcel.config; - mTetherStatesParcel = parcel.states; - mWaitForCallback.open(); - } - - @Override - public void onCallbackStopped(int errorCode) { - mError = errorCode; - mWaitForCallback.open(); - } - - @Override - public void onUpstreamChanged(Network network) { } - - @Override - public void onConfigurationChanged(TetheringConfigurationParcel config) { - mTetheringConfiguration = config; - } - - @Override - public void onTetherStatesChanged(TetherStatesParcel states) { - mTetherStatesParcel = states; - } - - @Override - public void onTetherClientsChanged(List clients) { } - - @Override - public void onOffloadStatusChanged(int status) { } - - public void waitForStarted() { - mWaitForCallback.block(DEFAULT_TIMEOUT_MS); - throwIfPermissionFailure(mError); - } - } - - /** - * Attempt to tether the named interface. This will setup a dhcp server - * on the interface, forward and NAT IP v4 packets and forward DNS requests - * to the best active upstream network interface. Note that if no upstream - * IP network interface is available, dhcp will still run and traffic will be - * allowed between the tethered devices and this device, though upstream net - * access will of course fail until an upstream network interface becomes - * active. - * - * @deprecated The only usages is PanService. It uses this for legacy reasons - * and will migrate away as soon as possible. - * - * @param iface the interface name to tether. - * @return error a {@code TETHER_ERROR} value indicating success or failure type - * - * {@hide} - */ - @Deprecated - @SystemApi(client = MODULE_LIBRARIES) - public int tether(@NonNull final String iface) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "tether caller:" + callerPkg); - final RequestDispatcher dispatcher = new RequestDispatcher(); - - return dispatcher.waitForResult((connector, listener) -> { - try { - connector.tether(iface, callerPkg, getAttributionTag(), listener); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - }); - } - - /** - * @return the context's attribution tag - */ - private @Nullable String getAttributionTag() { - return mContext.getAttributionTag(); - } - - /** - * Stop tethering the named interface. - * - * @deprecated The only usages is PanService. It uses this for legacy reasons - * and will migrate away as soon as possible. - * - * {@hide} - */ - @Deprecated - @SystemApi(client = MODULE_LIBRARIES) - public int untether(@NonNull final String iface) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "untether caller:" + callerPkg); - - final RequestDispatcher dispatcher = new RequestDispatcher(); - - return dispatcher.waitForResult((connector, listener) -> { - try { - connector.untether(iface, callerPkg, getAttributionTag(), listener); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - }); - } - - /** - * Attempt to both alter the mode of USB and Tethering of USB. - * - * @deprecated New client should not use this API anymore. All clients should use - * #startTethering or #stopTethering which encapsulate proper entitlement logic. If the API is - * used and an entitlement check is needed, downstream USB tethering will be enabled but will - * not have any upstream. - * - * {@hide} - */ - @Deprecated - @SystemApi(client = MODULE_LIBRARIES) - public int setUsbTethering(final boolean enable) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "setUsbTethering caller:" + callerPkg); - - final RequestDispatcher dispatcher = new RequestDispatcher(); - - return dispatcher.waitForResult((connector, listener) -> { - try { - connector.setUsbTethering(enable, callerPkg, getAttributionTag(), - listener); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - }); - } - - /** - * Use with {@link #startTethering} to specify additional parameters when starting tethering. - */ - public static class TetheringRequest { - /** A configuration set for TetheringRequest. */ - private final TetheringRequestParcel mRequestParcel; - - private TetheringRequest(final TetheringRequestParcel request) { - mRequestParcel = request; - } - - /** Builder used to create TetheringRequest. */ - public static class Builder { - private final TetheringRequestParcel mBuilderParcel; - - /** Default constructor of Builder. */ - public Builder(@TetheringType final int type) { - mBuilderParcel = new TetheringRequestParcel(); - mBuilderParcel.tetheringType = type; - mBuilderParcel.localIPv4Address = null; - mBuilderParcel.staticClientAddress = null; - mBuilderParcel.exemptFromEntitlementCheck = false; - mBuilderParcel.showProvisioningUi = true; - } - - /** - * Configure tethering with static IPv4 assignment. - * - * A DHCP server will be started, but will only be able to offer the client address. - * The two addresses must be in the same prefix. - * - * @param localIPv4Address The preferred local IPv4 link address to use. - * @param clientAddress The static client address. - */ - @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) - @NonNull - public Builder setStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address, - @NonNull final LinkAddress clientAddress) { - Objects.requireNonNull(localIPv4Address); - Objects.requireNonNull(clientAddress); - if (!checkStaticAddressConfiguration(localIPv4Address, clientAddress)) { - throw new IllegalArgumentException("Invalid server or client addresses"); - } - - mBuilderParcel.localIPv4Address = localIPv4Address; - mBuilderParcel.staticClientAddress = clientAddress; - return this; - } - - /** Start tethering without entitlement checks. */ - @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) - @NonNull - public Builder setExemptFromEntitlementCheck(boolean exempt) { - mBuilderParcel.exemptFromEntitlementCheck = exempt; - return this; - } - - /** - * If an entitlement check is needed, sets whether to show the entitlement UI or to - * perform a silent entitlement check. By default, the entitlement UI is shown. - */ - @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) - @NonNull - public Builder setShouldShowEntitlementUi(boolean showUi) { - mBuilderParcel.showProvisioningUi = showUi; - return this; - } - - /** Build {@link TetheringRequest] with the currently set configuration. */ - @NonNull - public TetheringRequest build() { - return new TetheringRequest(mBuilderParcel); - } - } - - /** - * Get the local IPv4 address, if one was configured with - * {@link Builder#setStaticIpv4Addresses}. - */ - @Nullable - public LinkAddress getLocalIpv4Address() { - return mRequestParcel.localIPv4Address; - } - - /** - * Get the static IPv4 address of the client, if one was configured with - * {@link Builder#setStaticIpv4Addresses}. - */ - @Nullable - public LinkAddress getClientStaticIpv4Address() { - return mRequestParcel.staticClientAddress; - } - - /** Get tethering type. */ - @TetheringType - public int getTetheringType() { - return mRequestParcel.tetheringType; - } - - /** Check if exempt from entitlement check. */ - public boolean isExemptFromEntitlementCheck() { - return mRequestParcel.exemptFromEntitlementCheck; - } - - /** Check if show entitlement ui. */ - public boolean getShouldShowEntitlementUi() { - return mRequestParcel.showProvisioningUi; - } - - /** - * Check whether the two addresses are ipv4 and in the same prefix. - * @hide - */ - public static boolean checkStaticAddressConfiguration( - @NonNull final LinkAddress localIPv4Address, - @NonNull final LinkAddress clientAddress) { - return localIPv4Address.getPrefixLength() == clientAddress.getPrefixLength() - && localIPv4Address.isIpv4() && clientAddress.isIpv4() - && new IpPrefix(localIPv4Address.toString()).equals( - new IpPrefix(clientAddress.toString())); - } - - /** - * Get a TetheringRequestParcel from the configuration - * @hide - */ - public TetheringRequestParcel getParcel() { - return mRequestParcel; - } - - /** String of TetheringRequest detail. */ - public String toString() { - return "TetheringRequest [ type= " + mRequestParcel.tetheringType - + ", localIPv4Address= " + mRequestParcel.localIPv4Address - + ", staticClientAddress= " + mRequestParcel.staticClientAddress - + ", exemptFromEntitlementCheck= " - + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= " - + mRequestParcel.showProvisioningUi + " ]"; - } - } - - /** - * Callback for use with {@link #startTethering} to find out whether tethering succeeded. - */ - public interface StartTetheringCallback { - /** - * Called when tethering has been successfully started. - */ - default void onTetheringStarted() {} - - /** - * Called when starting tethering failed. - * - * @param error The error that caused the failure. - */ - default void onTetheringFailed(@StartTetheringError final int error) {} - } - - /** - * Starts tethering and runs tether provisioning for the given type if needed. If provisioning - * fails, stopTethering will be called automatically. - * - *

Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will - * fail if a tethering entitlement check is required. - * - * @param request a {@link TetheringRequest} which can specify the preferred configuration. - * @param executor {@link Executor} to specify the thread upon which the callback of - * TetheringRequest will be invoked. - * @param callback A callback that will be called to indicate the success status of the - * tethering start request. - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.TETHER_PRIVILEGED, - android.Manifest.permission.WRITE_SETTINGS - }) - public void startTethering(@NonNull final TetheringRequest request, - @NonNull final Executor executor, @NonNull final StartTetheringCallback callback) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "startTethering caller:" + callerPkg); - - final IIntResultListener listener = new IIntResultListener.Stub() { - @Override - public void onResult(final int resultCode) { - executor.execute(() -> { - if (resultCode == TETHER_ERROR_NO_ERROR) { - callback.onTetheringStarted(); - } else { - callback.onTetheringFailed(resultCode); - } - }); - } - }; - getConnector(c -> c.startTethering(request.getParcel(), callerPkg, - getAttributionTag(), listener)); - } - - /** - * Starts tethering and runs tether provisioning for the given type if needed. If provisioning - * fails, stopTethering will be called automatically. - * - *

Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will - * fail if a tethering entitlement check is required. - * - * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants. - * @param executor {@link Executor} to specify the thread upon which the callback of - * TetheringRequest will be invoked. - * @hide - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.TETHER_PRIVILEGED, - android.Manifest.permission.WRITE_SETTINGS - }) - @SystemApi(client = MODULE_LIBRARIES) - public void startTethering(int type, @NonNull final Executor executor, - @NonNull final StartTetheringCallback callback) { - startTethering(new TetheringRequest.Builder(type).build(), executor, callback); - } - - /** - * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if - * applicable. - * - *

Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will - * fail if a tethering entitlement check is required. - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.TETHER_PRIVILEGED, - android.Manifest.permission.WRITE_SETTINGS - }) - public void stopTethering(@TetheringType final int type) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "stopTethering caller:" + callerPkg); - - getConnector(c -> c.stopTethering(type, callerPkg, getAttributionTag(), - new IIntResultListener.Stub() { - @Override - public void onResult(int resultCode) { - // TODO: provide an API to obtain result - // This has never been possible as stopTethering has always been void and never - // taken a callback object. The only indication that callers have is if the call - // results in a TETHER_STATE_CHANGE broadcast. - } - })); - } - - /** - * Callback for use with {@link #getLatestTetheringEntitlementResult} to find out whether - * entitlement succeeded. - */ - public interface OnTetheringEntitlementResultListener { - /** - * Called to notify entitlement result. - * - * @param resultCode an int value of entitlement result. It may be one of - * {@link #TETHER_ERROR_NO_ERROR}, - * {@link #TETHER_ERROR_PROVISIONING_FAILED}, or - * {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN}. - */ - void onTetheringEntitlementResult(@EntitlementResult int result); - } - - /** - * Request the latest value of the tethering entitlement check. - * - *

This method will only return the latest entitlement result if it is available. If no - * cached entitlement result is available, and {@code showEntitlementUi} is false, - * {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN} will be returned. If {@code showEntitlementUi} is - * true, entitlement will be run. - * - *

Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will - * fail if a tethering entitlement check is required. - * - * @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants. - * @param showEntitlementUi a boolean indicating whether to check result for the UI-based - * entitlement check or the silent entitlement check. - * @param executor the executor on which callback will be invoked. - * @param listener an {@link OnTetheringEntitlementResultListener} which will be called to - * notify the caller of the result of entitlement check. The listener may be called zero - * or one time. - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.TETHER_PRIVILEGED, - android.Manifest.permission.WRITE_SETTINGS - }) - public void requestLatestTetheringEntitlementResult(@TetheringType int type, - boolean showEntitlementUi, - @NonNull Executor executor, - @NonNull final OnTetheringEntitlementResultListener listener) { - if (listener == null) { - throw new IllegalArgumentException( - "OnTetheringEntitlementResultListener cannot be null."); - } - - ResultReceiver wrappedListener = new ResultReceiver(null /* handler */) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - executor.execute(() -> { - listener.onTetheringEntitlementResult(resultCode); - }); - } - }; - - requestLatestTetheringEntitlementResult(type, wrappedListener, - showEntitlementUi); - } - - /** - * Helper function of #requestLatestTetheringEntitlementResult to remain backwards compatible - * with ConnectivityManager#getLatestTetheringEntitlementResult - * - * {@hide} - */ - // TODO: improve the usage of ResultReceiver, b/145096122 - @SystemApi(client = MODULE_LIBRARIES) - public void requestLatestTetheringEntitlementResult(@TetheringType final int type, - @NonNull final ResultReceiver receiver, final boolean showEntitlementUi) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "getLatestTetheringEntitlementResult caller:" + callerPkg); - - getConnector(c -> c.requestLatestTetheringEntitlementResult( - type, receiver, showEntitlementUi, callerPkg, getAttributionTag())); - } - - /** - * Callback for use with {@link registerTetheringEventCallback} to find out tethering - * upstream status. - */ - public interface TetheringEventCallback { - /** - * Called when tethering supported status changed. - * - *

This will be called immediately after the callback is registered, and may be called - * multiple times later upon changes. - * - *

Tethering may be disabled via system properties, device configuration, or device - * policy restrictions. - * - * @param supported The new supported status - */ - default void onTetheringSupported(boolean supported) {} - - /** - * Called when tethering upstream changed. - * - *

This will be called immediately after the callback is registered, and may be called - * multiple times later upon changes. - * - * @param network the {@link Network} of tethering upstream. Null means tethering doesn't - * have any upstream. - */ - default void onUpstreamChanged(@Nullable Network network) {} - - /** - * Called when there was a change in tethering interface regular expressions. - * - *

This will be called immediately after the callback is registered, and may be called - * multiple times later upon changes. - * @param reg The new regular expressions. - * - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - default void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {} - - /** - * Called when there was a change in the list of tetherable interfaces. Tetherable - * interface means this interface is available and can be used for tethering. - * - *

This will be called immediately after the callback is registered, and may be called - * multiple times later upon changes. - * @param interfaces The list of tetherable interface names. - */ - default void onTetherableInterfacesChanged(@NonNull List interfaces) {} - - /** - * Called when there was a change in the list of tethered interfaces. - * - *

This will be called immediately after the callback is registered, and may be called - * multiple times later upon changes. - * @param interfaces The list of 0 or more String of currently tethered interface names. - */ - default void onTetheredInterfacesChanged(@NonNull List interfaces) {} - - /** - * Called when an error occurred configuring tethering. - * - *

This will be called immediately after the callback is registered if the latest status - * on the interface is an error, and may be called multiple times later upon changes. - * @param ifName Name of the interface. - * @param error One of {@code TetheringManager#TETHER_ERROR_*}. - */ - default void onError(@NonNull String ifName, @TetheringIfaceError int error) {} - - /** - * Called when the list of tethered clients changes. - * - *

This callback provides best-effort information on connected clients based on state - * known to the system, however the list cannot be completely accurate (and should not be - * used for security purposes). For example, clients behind a bridge and using static IP - * assignments are not visible to the tethering device; or even when using DHCP, such - * clients may still be reported by this callback after disconnection as the system cannot - * determine if they are still connected. - * @param clients The new set of tethered clients; the collection is not ordered. - */ - default void onClientsChanged(@NonNull Collection clients) {} - - /** - * Called when tethering offload status changes. - * - *

This will be called immediately after the callback is registered. - * @param status The offload status. - */ - default void onOffloadStatusChanged(@TetherOffloadStatus int status) {} - } - - /** - * Regular expressions used to identify tethering interfaces. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public static class TetheringInterfaceRegexps { - private final String[] mTetherableBluetoothRegexs; - private final String[] mTetherableUsbRegexs; - private final String[] mTetherableWifiRegexs; - - /** @hide */ - public TetheringInterfaceRegexps(@NonNull String[] tetherableBluetoothRegexs, - @NonNull String[] tetherableUsbRegexs, @NonNull String[] tetherableWifiRegexs) { - mTetherableBluetoothRegexs = tetherableBluetoothRegexs.clone(); - mTetherableUsbRegexs = tetherableUsbRegexs.clone(); - mTetherableWifiRegexs = tetherableWifiRegexs.clone(); - } - - @NonNull - public List getTetherableBluetoothRegexs() { - return Collections.unmodifiableList(Arrays.asList(mTetherableBluetoothRegexs)); - } - - @NonNull - public List getTetherableUsbRegexs() { - return Collections.unmodifiableList(Arrays.asList(mTetherableUsbRegexs)); - } - - @NonNull - public List getTetherableWifiRegexs() { - return Collections.unmodifiableList(Arrays.asList(mTetherableWifiRegexs)); - } - - @Override - public int hashCode() { - return Objects.hash(mTetherableBluetoothRegexs, mTetherableUsbRegexs, - mTetherableWifiRegexs); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof TetheringInterfaceRegexps)) return false; - final TetheringInterfaceRegexps other = (TetheringInterfaceRegexps) obj; - return Arrays.equals(mTetherableBluetoothRegexs, other.mTetherableBluetoothRegexs) - && Arrays.equals(mTetherableUsbRegexs, other.mTetherableUsbRegexs) - && Arrays.equals(mTetherableWifiRegexs, other.mTetherableWifiRegexs); - } - } - - /** - * Start listening to tethering change events. Any new added callback will receive the last - * tethering status right away. If callback is registered, - * {@link TetheringEventCallback#onUpstreamChanged} will immediately be called. If tethering - * has no upstream or disabled, the argument of callback will be null. The same callback object - * cannot be registered twice. - * - * @param executor the executor on which callback will be invoked. - * @param callback the callback to be called when tethering has change events. - */ - @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) - public void registerTetheringEventCallback(@NonNull Executor executor, - @NonNull TetheringEventCallback callback) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "registerTetheringEventCallback caller:" + callerPkg); - - synchronized (mTetheringEventCallbacks) { - if (mTetheringEventCallbacks.containsKey(callback)) { - throw new IllegalArgumentException("callback was already registered."); - } - final ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() { - // Only accessed with a lock on this object - private final HashMap mErrorStates = new HashMap<>(); - private String[] mLastTetherableInterfaces = null; - private String[] mLastTetheredInterfaces = null; - - @Override - public void onUpstreamChanged(Network network) throws RemoteException { - executor.execute(() -> { - callback.onUpstreamChanged(network); - }); - } - - private synchronized void sendErrorCallbacks(final TetherStatesParcel newStates) { - for (int i = 0; i < newStates.erroredIfaceList.length; i++) { - final String iface = newStates.erroredIfaceList[i]; - final Integer lastError = mErrorStates.get(iface); - final int newError = newStates.lastErrorList[i]; - if (newError != TETHER_ERROR_NO_ERROR - && !Objects.equals(lastError, newError)) { - callback.onError(iface, newError); - } - mErrorStates.put(iface, newError); - } - } - - private synchronized void maybeSendTetherableIfacesChangedCallback( - final TetherStatesParcel newStates) { - if (Arrays.equals(mLastTetherableInterfaces, newStates.availableList)) return; - mLastTetherableInterfaces = newStates.availableList.clone(); - callback.onTetherableInterfacesChanged( - Collections.unmodifiableList(Arrays.asList(mLastTetherableInterfaces))); - } - - private synchronized void maybeSendTetheredIfacesChangedCallback( - final TetherStatesParcel newStates) { - if (Arrays.equals(mLastTetheredInterfaces, newStates.tetheredList)) return; - mLastTetheredInterfaces = newStates.tetheredList.clone(); - callback.onTetheredInterfacesChanged( - Collections.unmodifiableList(Arrays.asList(mLastTetheredInterfaces))); - } - - // Called immediately after the callbacks are registered. - @Override - public void onCallbackStarted(TetheringCallbackStartedParcel parcel) { - executor.execute(() -> { - callback.onTetheringSupported(parcel.tetheringSupported); - callback.onUpstreamChanged(parcel.upstreamNetwork); - sendErrorCallbacks(parcel.states); - sendRegexpsChanged(parcel.config); - maybeSendTetherableIfacesChangedCallback(parcel.states); - maybeSendTetheredIfacesChangedCallback(parcel.states); - callback.onClientsChanged(parcel.tetheredClients); - callback.onOffloadStatusChanged(parcel.offloadStatus); - }); - } - - @Override - public void onCallbackStopped(int errorCode) { - executor.execute(() -> { - throwIfPermissionFailure(errorCode); - }); - } - - private void sendRegexpsChanged(TetheringConfigurationParcel parcel) { - callback.onTetherableInterfaceRegexpsChanged(new TetheringInterfaceRegexps( - parcel.tetherableBluetoothRegexs, - parcel.tetherableUsbRegexs, - parcel.tetherableWifiRegexs)); - } - - @Override - public void onConfigurationChanged(TetheringConfigurationParcel config) { - executor.execute(() -> sendRegexpsChanged(config)); - } - - @Override - public void onTetherStatesChanged(TetherStatesParcel states) { - executor.execute(() -> { - sendErrorCallbacks(states); - maybeSendTetherableIfacesChangedCallback(states); - maybeSendTetheredIfacesChangedCallback(states); - }); - } - - @Override - public void onTetherClientsChanged(final List clients) { - executor.execute(() -> callback.onClientsChanged(clients)); - } - - @Override - public void onOffloadStatusChanged(final int status) { - executor.execute(() -> callback.onOffloadStatusChanged(status)); - } - }; - getConnector(c -> c.registerTetheringEventCallback(remoteCallback, callerPkg)); - mTetheringEventCallbacks.put(callback, remoteCallback); - } - } - - /** - * Remove tethering event callback previously registered with - * {@link #registerTetheringEventCallback}. - * - * @param callback previously registered callback. - */ - @RequiresPermission(anyOf = { - Manifest.permission.TETHER_PRIVILEGED, - Manifest.permission.ACCESS_NETWORK_STATE - }) - public void unregisterTetheringEventCallback(@NonNull final TetheringEventCallback callback) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg); - - synchronized (mTetheringEventCallbacks) { - ITetheringEventCallback remoteCallback = mTetheringEventCallbacks.remove(callback); - if (remoteCallback == null) { - throw new IllegalArgumentException("callback was not registered."); - } - - getConnector(c -> c.unregisterTetheringEventCallback(remoteCallback, callerPkg)); - } - } - - /** - * Get a more detailed error code after a Tethering or Untethering - * request asynchronously failed. - * - * @param iface The name of the interface of interest - * @return error The error code of the last error tethering or untethering the named - * interface - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public int getLastTetherError(@NonNull final String iface) { - mCallback.waitForStarted(); - if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR; - - int i = 0; - for (String errored : mTetherStatesParcel.erroredIfaceList) { - if (iface.equals(errored)) return mTetherStatesParcel.lastErrorList[i]; - - i++; - } - return TETHER_ERROR_NO_ERROR; - } - - /** - * Get the list of regular expressions that define any tetherable - * USB network interfaces. If USB tethering is not supported by the - * device, this list should be empty. - * - * @return an array of 0 or more regular expression Strings defining - * what interfaces are considered tetherable usb interfaces. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetherableUsbRegexs() { - mCallback.waitForStarted(); - return mTetheringConfiguration.tetherableUsbRegexs; - } - - /** - * Get the list of regular expressions that define any tetherable - * Wifi network interfaces. If Wifi tethering is not supported by the - * device, this list should be empty. - * - * @return an array of 0 or more regular expression Strings defining - * what interfaces are considered tetherable wifi interfaces. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetherableWifiRegexs() { - mCallback.waitForStarted(); - return mTetheringConfiguration.tetherableWifiRegexs; - } - - /** - * Get the list of regular expressions that define any tetherable - * Bluetooth network interfaces. If Bluetooth tethering is not supported by the - * device, this list should be empty. - * - * @return an array of 0 or more regular expression Strings defining - * what interfaces are considered tetherable bluetooth interfaces. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetherableBluetoothRegexs() { - mCallback.waitForStarted(); - return mTetheringConfiguration.tetherableBluetoothRegexs; - } - - /** - * Get the set of tetherable, available interfaces. This list is limited by - * device configuration and current interface existence. - * - * @return an array of 0 or more Strings of tetherable interface names. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetherableIfaces() { - mCallback.waitForStarted(); - if (mTetherStatesParcel == null) return new String[0]; - - return mTetherStatesParcel.availableList; - } - - /** - * Get the set of tethered interfaces. - * - * @return an array of 0 or more String of currently tethered interface names. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetheredIfaces() { - mCallback.waitForStarted(); - if (mTetherStatesParcel == null) return new String[0]; - - return mTetherStatesParcel.tetheredList; - } - - /** - * Get the set of interface names which attempted to tether but - * failed. Re-attempting to tether may cause them to reset to the Tethered - * state. Alternatively, causing the interface to be destroyed and recreated - * may cause them to reset to the available state. - * {@link TetheringManager#getLastTetherError} can be used to get more - * information on the cause of the errors. - * - * @return an array of 0 or more String indicating the interface names - * which failed to tether. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetheringErroredIfaces() { - mCallback.waitForStarted(); - if (mTetherStatesParcel == null) return new String[0]; - - return mTetherStatesParcel.erroredIfaceList; - } - - /** - * Get the set of tethered dhcp ranges. - * - * @deprecated This API just return the default value which is not used in DhcpServer. - * @hide - */ - @Deprecated - public @NonNull String[] getTetheredDhcpRanges() { - mCallback.waitForStarted(); - return mTetheringConfiguration.legacyDhcpRanges; - } - - /** - * Check if the device allows for tethering. It may be disabled via - * {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or - * due to device configuration. - * - * @return a boolean - {@code true} indicating Tethering is supported. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public boolean isTetheringSupported() { - final String callerPkg = mContext.getOpPackageName(); - - return isTetheringSupported(callerPkg); - } - - /** - * Check if the device allows for tethering. It may be disabled via {@code ro.tether.denied} - * system property, Settings.TETHER_SUPPORTED or due to device configuration. This is useful - * for system components that query this API on behalf of an app. In particular, Bluetooth - * has @UnsupportedAppUsage calls that will let apps turn on bluetooth tethering if they have - * the right permissions, but such an app needs to know whether it can (permissions as well - * as support from the device) turn on tethering in the first place to show the appropriate UI. - * - * @param callerPkg The caller package name, if it is not matching the calling uid, - * SecurityException would be thrown. - * @return a boolean - {@code true} indicating Tethering is supported. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public boolean isTetheringSupported(@NonNull final String callerPkg) { - - final RequestDispatcher dispatcher = new RequestDispatcher(); - final int ret = dispatcher.waitForResult((connector, listener) -> { - try { - connector.isTetheringSupported(callerPkg, getAttributionTag(), listener); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - }); - - return ret == TETHER_ERROR_NO_ERROR; - } - - /** - * Stop all active tethering. - * - *

Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will - * fail if a tethering entitlement check is required. - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.TETHER_PRIVILEGED, - android.Manifest.permission.WRITE_SETTINGS - }) - public void stopAllTethering() { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "stopAllTethering caller:" + callerPkg); - - getConnector(c -> c.stopAllTethering(callerPkg, getAttributionTag(), - new IIntResultListener.Stub() { - @Override - public void onResult(int resultCode) { - // TODO: add an API parameter to send result to caller. - // This has never been possible as stopAllTethering has always been void - // and never taken a callback object. The only indication that callers have - // is if the call results in a TETHER_STATE_CHANGE broadcast. - } - })); - } -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl deleted file mode 100644 index c0280d3dbfaf..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.net.LinkAddress; - -/** - * Configuration details for requesting tethering. - * @hide - */ -parcelable TetheringRequestParcel { - int tetheringType; - LinkAddress localIPv4Address; - LinkAddress staticClientAddress; - boolean exemptFromEntitlementCheck; - boolean showProvisioningUi; -} diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt deleted file mode 100644 index 591861f5b837..000000000000 --- a/packages/Tethering/jarjar-rules.txt +++ /dev/null @@ -1,11 +0,0 @@ -# These must be kept in sync with the framework-tethering-shared-srcs filegroup. -# Classes from the framework-tethering-shared-srcs filegroup. -# If there are files in that filegroup that are not covered below, the classes in the -# module will be overwritten by the ones in the framework. -rule com.android.internal.util.** com.android.networkstack.tethering.util.@1 -rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1 - -rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1 - -# Classes from net-utils-framework-common -rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1 \ No newline at end of file diff --git a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp deleted file mode 100644 index 94c871d8a366..000000000000 --- a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LOG_TAG "TetheringUtils" -#include - -namespace android { - -static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt); -static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr); -static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type); - -static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) { - sock_filter filter_code[] = { - // Check header is ICMPv6. - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), - - // Check ICMPv6 type. - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1), - - // Accept or reject. - BPF_STMT(BPF_RET | BPF_K, 0xffff), - BPF_STMT(BPF_RET | BPF_K, 0) - }; - - const sock_fprog filter = { - sizeof(filter_code) / sizeof(filter_code[0]), - filter_code, - }; - - int fd = jniGetFDFromFileDescriptor(env, javaFd); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); - } -} - -static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd) -{ - android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT); -} - -static void android_net_util_setupNsSocket(JNIEnv *env, jobject clazz, jobject javaFd) -{ - android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT); -} - -static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, - jint ifIndex) -{ - static const int kLinkLocalHopLimit = 255; - - int fd = jniGetFDFromFileDescriptor(env, javaFd); - - // Set an ICMPv6 filter that only passes Router Solicitations. - struct icmp6_filter rs_only; - ICMP6_FILTER_SETBLOCKALL(&rs_only); - ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only); - socklen_t len = sizeof(rs_only); - if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(ICMP6_FILTER): %s", strerror(errno)); - return; - } - - // Most/all of the rest of these options can be set via Java code, but - // because we're here on account of setting an icmp6_filter go ahead - // and do it all natively for now. - - // Set the multicast hoplimit to 255 (link-local only). - int hops = kLinkLocalHopLimit; - len = sizeof(hops); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno)); - return; - } - - // Set the unicast hoplimit to 255 (link-local only). - hops = kLinkLocalHopLimit; - len = sizeof(hops); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno)); - return; - } - - // Explicitly disable multicast loopback. - int off = 0; - len = sizeof(off); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno)); - return; - } - - // Specify the IPv6 interface to use for outbound multicast. - len = sizeof(ifIndex); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno)); - return; - } - - // Additional options to be considered: - // - IPV6_TCLASS - // - IPV6_RECVPKTINFO - // - IPV6_RECVHOPLIMIT - - // Bind to [::]. - const struct sockaddr_in6 sin6 = { - .sin6_family = AF_INET6, - .sin6_port = 0, - .sin6_flowinfo = 0, - .sin6_addr = IN6ADDR_ANY_INIT, - .sin6_scope_id = 0, - }; - auto sa = reinterpret_cast(&sin6); - len = sizeof(sin6); - if (bind(fd, sa, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "bind(IN6ADDR_ANY): %s", strerror(errno)); - return; - } - - // Join the all-routers multicast group, ff02::2%index. - struct ipv6_mreq all_rtrs = { - .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}}, - .ipv6mr_interface = ifIndex, - }; - len = sizeof(all_rtrs); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno)); - return; - } -} - -/* - * JNI registration. - */ -static const JNINativeMethod gMethods[] = { - /* name, signature, funcPtr */ - { "setupNaSocket", "(Ljava/io/FileDescriptor;)V", - (void*) android_net_util_setupNaSocket }, - { "setupNsSocket", "(Ljava/io/FileDescriptor;)V", - (void*) android_net_util_setupNsSocket }, - { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", - (void*) android_net_util_setupRaSocket }, -}; - -int register_android_net_util_TetheringUtils(JNIEnv* env) { - return jniRegisterNativeMethods(env, - "android/net/util/TetheringUtils", - gMethods, NELEM(gMethods)); -} - -extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { - JNIEnv *env; - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { - __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed"); - return JNI_ERR; - } - - if (register_android_net_util_TetheringUtils(env) < 0) { - return JNI_ERR; - } - - return JNI_VERSION_1_6; -} - -}; // namespace android diff --git a/packages/Tethering/proguard.flags b/packages/Tethering/proguard.flags deleted file mode 100644 index 86b903353cf5..000000000000 --- a/packages/Tethering/proguard.flags +++ /dev/null @@ -1,9 +0,0 @@ -# Keep class's integer static field for MessageUtils to parsing their name. --keep class com.android.networkstack.tethering.Tethering$TetherMainSM { - static final int CMD_*; - static final int EVENT_*; -} - --keepclassmembers class android.net.ip.IpServer { - static final int CMD_*; -} diff --git a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png deleted file mode 100644 index 9451174d65d7c5477f66fcf51010c4a0144c82ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 891 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQVV~v#o=I(~Nj0 zYjej-dU=M4ZThfL$V+nR8x8I$szRxWi&K_P$uKjPo;dYLS_`XNXOP+XF1 zr*r4s4&(Qh-zTM4p8Nc->fib6zaMeEUwKgH^grWV=Wil4C)k)cBh>;F&NFcyW<7f} z=Q_h<1ky-1tl+Y_|p+xrDV1v3`+Y;G2xvMInYKq2AsbS3c{rzLJnE)@}%U=|ga z6~MUT6hrcx2bQi*)eDSy68`?!68=EyxMZ^OcaOBiUdAq}d!7ji|9PM_xA#3$s#*o> znxsM#Pvc)&q3&xsqW>(Y+Z1iH(yn0Tj??QuI?lU$)AVcW>7%9lzR&)Abo0F}&Nhh# zGmcqVnm-Qp3`#-#Sn=~tsPF|rbbGlZR7EUF&}g7{?;*OR5p0-?%h%g zOn0Ist`Q~9MX8A;nfZANA(^?U42Jr~hWdtvdJ?8{fC^MW3Q`hFQmv9fs!|z@3=9o* z4J>ty3_=V|txPPfOiXkQ%&iOzCU~ALMbVI(pOTqYiCe=%k2boFyt=akR{09gog-~a#s diff --git a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png deleted file mode 100644 index 79d5756ae38ed3e6059370fd575023df68759738..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 963 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQk4Ey_J*>>4z&o!quZom6w@~v6b-%cB{cO5#zd)})-#l6pmu`T`X zhwSnM_c;wW%ap?UWFo(YJ$#tD{a1j_L6cX?8#is^ijIgnx~}!h#9K0s8{aH{os=sS zY%y7;`do3}?*B?_kA8jnV-w5gfJa*%9pIJ!eQf6azwgeN+kJDhh<$UY-95l%S;=Z< ziO%bHUZyU4@wP(OgzLfQ8@ho#>~W&O8h1ZOi=-w?ukK9x^K9=0uSfEAnj9>T+9KKy z)kgo#P0fD&_rjW^y-f|hzb<}GN)=izr+3D7>ZYm}AwFS`D(zSCp1XS1!T-@TtM`$G zcXm~}m>gZ_TN>9?yL|Pe*?ZeE{eEiIoA378y?9oG^S8xc6M9|V-3k<9w|T0`uJY!` z%p+10G8$|77)#f`UVQ4hce?p2?{JrxkE#BjtjZMLOa^*>%2%z-*EfWP+a9QMXHARxU9k!^%DVp*cq(xn_4{%I-khfz}#p&)7%5y_!?n&{T zxbK0kX3SphCycYpHW(T`NRZCCqE}>e;6uCvZ-Fdtg(F{Cs=VXh=k7-o&d)lPE64Ns z!mbzJGyPwq9HRlxKsTH+c};#`!PSdy8arx22vo62CQZ)~Vt)x7$D3!r6B|j-u!7Z~WwLHHlyI8?F*tBr#V>6&S22WQ% Jmvv4FO#oIHmizz! diff --git a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png deleted file mode 100644 index cae1bd1b2574c898ade479aacf0e4762093f5cd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 909 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQpKm@< zdh+h^nCA4vviE0h=36}fbMDMh<$Xbm|0m0)H@~-VR7u|Ax+3!Y4F0I*i5H}WSMW@p z(R9E#PC!E_PI5xe-T?7CZ|n9?@mJPm?aI17S!v!%Z4a)H71NKj?W~B_?b&;M*REY8!Q$z+&($*Qm}%W4!ORQdo@Nc4dmdQ%R~aXs-tkjbV%Zn3V`f*DC#l8h z1@C^fWb(RIyOMUt#(68Z{LrU4EB8)A54I1=ccyd)tLFKTe!` zz;v3#eW4Ew8qDho=I9l4*Vg`3o#OibeiDPoi%rvu59sNIl(RfBTprn)$ZO-{{_Rw3 z&NoxZ{T1hI?=e}XTkt!Vz+ZL0 z^m@)$1^H8YIzlYjukyIE=+E4qrXIGxe^2{Aw^7{| zyX{$mbZw7|l4YalH-R5fZ@*f}hK5Tm6<|{9|0gA|RH0abXGPjnC}Q!>*kacg)e zQhE}oK?Y<)aDG}zd16s2gJVj5QmTSmW>IQ+eo=O@f^)EG;nv4yKy?hBu6{1-oD!M< DT$^y< diff --git a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png deleted file mode 100644 index ffe8e8c98232649a7e1a1db66b847e7a3b1a53ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 534 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eW!3HFke0{SLNUQj3FfcGmdAc};Xq-PAw9v~XP-IT=-h=W1JWA4k zSRPMvbrj%XYYsHv2unQ8)*ZjVZLQM3mNea_1X06?me7!t`!g-uV&&iO)lIs^9Q^J~ zLHfD92afcvjop)f`qQEKBbt9S>-X-R=a^Gu%fQZ1bl0%er92?;@YQ#Z7K*$M`I0@y zdXkC>BZFU=YN^WI@SRUY+zGB z$0j$m(}yGv%xhWL_{KdUJtvs8Q!tM0-E7uMP6p=ZfsSQdpSCSp>2^p)NiJbp`_ijs z3zl{S8%Jv;i?oL4>wJHAPS@-7$(;S)E^WU1I^&s&>EuasXW#Uie0u)Qj3FfcIcdAc};Xq-PgN!Q=mQNZ=QO<0(MfJV~_ zhbzer3Rxbi>l7Ol8t*qaJan8cz~Q*S(86QIaRqG-M}@A2DgArq-gGRPy-&eIb#l** z*_+FY)7k&Uo_zK6ci1EYbIzC@cCF{PUl*R_n-=dQ=(APpE|-kTcRL3~qt_eQbr>g1 znGhs(aQVABwi1^md8GN3WOjO6LK=f@LY>Uf>z@VQZ#I}L=_C`kf?0p+hv)mBT|Vz~?!i5$6xJJz zSq_Vt_#3$I73pne|1@M$e{-L1;Fyx1l&avFo0y&&l$w}QS$Hzl2B?U^)78&qol`;+06?tDqyPW_ diff --git a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png deleted file mode 100644 index 65e907565ec15be0fd316350a4103423524c25d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 518 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eW!3HFke0{SLNUQj3FfcH3dAc};Xq>-0VZRr%qd@z9!)T6QJZ?EU zK~YOQxizGyGpF*8aA(aw^W*JTmiM3Xr+cn#b76eEX09N24wJ?l z_6gf1-q*^lV4AySzg2{I!>NyJnIv8>YvABz2>tc>&+=V70asb3%-{9A_wa17fHMrw zH??XX5UB8ElVN0DpyDLEtl8%e@0F0P+&RqUN|)Fc?b&)pD8O4Nf&WY0Jw3I)^mA3; z1g82kUR(4!HbI@iZT@73wWpG9)%U2Me#q-E?bcuS1k;u;OdFIgo%w6`ME;2Q&Z5MN z96Q{4fI*~M;u=wsl30>zm0Xkxq!^403=MP*4RsBSLJSS93{0$yO?3^-tqcq%im$qj zq9HdwB{QuOw}uB^LZC>H1lbUrpH@s5Ba=>(*vG=)G0fH|yGb)dM ze?0f^Gp^Y-&(&@g^eZgt;XUnJS{C!ycv3L8?-Wx7T<=^+d zv3!11^Zf3ma$k6tf4_QTh53<$m)op5@=sXEb7_TTXasF{?=1~85T?sqG)AIG3>uGYUPZN?t06%jJEGR^fC zvqS8^s&6>5VD~-s87k^3R}&9hsz0~ui$ck-=jVa(BU<7bQQ};bnpl#VpQjL#nVZUB zsBdhjZ)m6|VLAt>Koz7QC9x#cDjB3ImBGls&`{UFQrE~J#L(2r#L~*dMAyLF%D`ZP z=h;#e4Y~O#nQ4`{H9Qn4Jqgqx1F|7FKdq!Zu_%?nF(p4KRlzN@D78GlD7#p}IoPyt S>ti#ZItEWyKbLh*2~7ZVw+i(D diff --git a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png deleted file mode 100644 index 065516185ad4896f40945ba3eb4845b1823a7d6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 659 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1rX+877l!}s{b%+Ad7K3vkw8&y zArNM~bhqvgNU*HHF&#*Q^)giWEM3OHz*z3-;uvCadhV3{elmsv?e?=@2(V7inbOqv zjBAQe%L>h=rl8>a+%o!(<)tT#Kd^XAtZ2;ks1!046>_muYMd7PdvSzwZFTa=O}57K zex9|rF}@kN&2;swnr}Pzq)Ts}b@kTegtH6`C)a#Dp}K$OXZ{~Y!uNe{R_yd}VES)3 z^JIh>D=F@0q@0l?Ka(<8CAwHCrbQ* zckj&b$*;d^MJY>_~T*7?@PtCtDnx*R;D z)Z4p5K}?KM^&|U_P>zXznpzhmC}(~u)|nC_7B+3t{s~TwN(cK7d6bkryLUXP{LWnd zkGgF$i{Dqi-}&6OdAHkd(dvvKZ}o$df=j%am=Jv6>(Hy1xGWnpWz>J4a{2oAt&Yrro}iI-QAwdD=D(CV9q+`%(AV@-{H{ z9^_SIpJ%`pw}oSRgLcf~Jii={##xuwOe&Uba5)z)xA_#;YwzC?J3?0SAI-I2{r#w@ zyWQ6fw%x3XtA5`zHZDGH{iwMA%9Xyv>H^Qd4~)-u_g^`+Yxb=L-r+tTd@9Fcp7Dw< zuD^M!=BY~E5X1{vrq`Z%)m+tMu zDZhcyCR*YeQQ};bnpl#VpQjL#nVZUBsBdhjZ)m6|VLAt>Koz7QC9x#cDjB3ImBGls z&`{UFQrE~J#L(2r#L~*dMAyLF%D`ZP=h;#e4Y~O#nQ4`{H9Qn4Jqgqx1F|7FKdq!Z qu_%?nF(p4KRlzN@D78GlD7#p}IoPyt>ti#ZItEWyKbLh*2~7ahln(g- diff --git a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png deleted file mode 100644 index 3f57d1c76ccb139dbf08eed75e945e5896163896..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1111 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;M!Q z+(IDCcKfnz$52J2-w_pT?Lfq~iD)5S3);_%iPXS-!wCEDk&lIxT@`oK}B zxO0vA<{yGQj*fd~=qPnwyxKftatEi1^$yQHE4wR9CZ&X3cyM2Vjm5Uh+*w6?Q?-EY zkF5d|zbZ$b-8A#vacA>QhL>$)PcM0H;(GP>p8cz~->X_}@MF{c=m?8j>kH0t9z1;Y zzM1nShFx#wJ$Rqvq_p^dk&#%FQes9r=b6p2oVr!(B}JoWw%dKMGIrt0;pGqz{=>QF ztmGyYmrHlbIb;@1Z}VUjIpOQ`Sx$1B>kOcZ2$_9o@G_0IFG;se*zcYnVdF!{}wuBv@OCJ3tpM=e^1vm^Q(96T+L=!{q1VycFCj7 zzEW&&n>`NdRoyw~d2f-z6&dg78ypAvec8`F(A4_7bA#KTtz4%xcZDAb|FhfEF?->e%n+qi zC9^Q2TGEHng(=+ftl+0_<-0{3Ka(;8X1Hbnp&AyTA7&W8kk!d7)_EoKfnz$52J2-w_pT?Lfr0s+r;B4q#NoX&_U4JYiyW`tygR~enFGs5 z78chB4K5QpR{Rhud6^@S8>DctNn=v_1I7sgrU43p(i*W0$2K{3arifQNKH=IQT}{- zQu_CIcdOUSYVNbU{O0(a_5WuUpF5NALHxdSuiv7K`}>|=*nVl>U)>6mU$;&-nO}On zBy;bzX!~BquiGA6@sH#BceG9=d5V`2(}t7U_dZ#_{^QB!vhQK_zQ+>w2HYMh@~7v2 zp2m6Yc*^r#=C-+t$!-rb^LNdwk=A!T_HRYY{x7qPdpF!!WBSSMWzY4Rn~z=z2p{?X zIR9MdjM>s!iKeO-58jD6VwwN*dxhv?F0ay~!JZa}7u~+%5PH_?sn?@>L5}{lZ|Wp( z+>MCdq28PHBwz8}_rggAfsXNe-}~GybAG+k`c_s^;j!Pb3!31d+ZY=CU37ZwnHNh9Bfg%FlQDeRB0cRZPt>>nw}j#q>u>MtyRxwH{MGxn zoiCm^I-6NOqiOf6IhC7KXLOd!qTF>-3!{E7pb59 zV&AImR(HBRSC#5kC113d*_r;==s;n#=+bzb+g+|3KJ=~>3tefrL1D(u>*Bk3E$;rQ ze0Yy-nN(w3=$l2-^KwLQ1KmFF*MI~)1!N;FA|k1e});nb<+ePL5~ z9E~>povHo1d9&W?zf29GW|}Y9c}^srb6C#m$uRGujK%Rvr(aH!j~28Y-lNVkwaGW^ zr{Jo(g2hGMr*^(hexrN0VByAsg>$`h=dbIwH1#mQ5+N|5yfI|5(}BkZmspJJo(VQG zoKR-sw+gkHRBV&*bUNdG#f7gn$~T^R%plXG_{`&hB>%}q50)aiPdyi=SPC6q=;d_i zo$^Mvs+iOH+6~WJmVNGEnYY}2vgYl$FMI*lZtVCvr+9`ELu137-~RI^GfJ{&3kd#T zn5(MsJaP$(k(AY?mbI-Rv+{H5BF5|S#IvRWe9bDua=Mp`osUrLK`dh@q*KiKUf^ ziLQaUm4U$o&$Fc{8glbfGSez?Yj`M9dJ?EX24q8Uep*R+Vo@rCV@iHfs)Ac)QEGX9 aQFgI{bFgXQ*2iW*bqt=aelF{r5}E*%!!$ks diff --git a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png deleted file mode 100644 index 36afe485b5bb65e88aa6b76926214da656dc804a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1182 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;M!Q z+(IDCcKfnz$52J2-w_pT?Lfq}Wn)5S3);_%iPz8SHuBFF2C-&cEjNf~%7 z65krq6%c1PiJw>vKmwJPk`xcl82S1BKtq}VAm~8m^V^vMR?a^? zt?Yt)@9ik%QlT?b7{g~IPKp1)WpV89nvbmfLc%_CvvflBnJaewkEyB-&Nivp6}~QT z1J?m9*6bVxamxdWd{_RuF1WLAL#*pWt`jF1PrSTz^UMC{b6EdRxcX2z$Yf(C^Mp0) z@0Z1&=Xk>LB=@6EcB1@_BiuIUS#PLLk!T6{ZgQ+ejZ>>=TE#Sunj`6RQn#?GY!2q0 zF3jl~bdBw1M-=VbS0aUEbjGfB8X^O?mR zll7N3Y*2bGFi~bgSNj6T{gy>N{x|#^_nZv=dYgrlNix4+M%SD!%QPu}50z^NxatEp zOkB69OL-6L35|5g`-;0nQl9)dcl*vlZ@!Y*y?5TQ3(Z_{+Dy6o+*ebTGeNyjo6 z%YRR>&yoLr`0nOt>jQgc%+60V(l7gcroU<-oBr+BtI`_0{@#=Po|{y5@09nW6Wiv# zpE$StJp270v%Y_IocG1j@bknebKmm{yx#lwwn=kl&spu>mXOl8nuTfay6?rJ9y?^0hFoWOY{AMoiF6~(eQC+(2-x8CYJ4B-fr1z@%B{g&s&=wZTZ9fdbVG7wVRUJ4ZXi@?ZDjy4FEcSOF)=O}HJktd18+%0 zK~#90?VR0p8Zi)szv1K^cMx}A(g7XB9c0r19T<0z4uUHP?tl)w?!f5)D!6%&;1G5f z7A&oXW8XLLkM`=bcF>FjMwFx}Fay5xakT-Kz}(E<01QCV9C#>Z%bzyDy_u~{(mn8} zv?GVw027l`19zp}IMtI$QfU{CwHcLm06dj8a75$cfXuAIF{I*vz&9ipfWR*#H-Nw= zBv*jIA0&5xz!xN!fWQwVw}4;=>9MGX(lco@vs2eds(|15-8AKL1WB6g|7~Uq;AaUh zE&%~6FOa+f8sh|#S3uwdl4n5R0Frk=P#DrR0D^*$QUiJdz7guVsWk>(;+$xw2r1Vy zLFu&EYjYxL-RGtYJ>9q5)BBm{DAfphjA;P?W>y1J;BTV#_w@?YEyrhsQI=MTK(BQW zA7a}#lImkOp6O{J>89lw%S8QX`b*f>T48neEJ$YnnqBe_c&}0d+N$d4$g0)AX+T}z zv&5W+zfA2mAjM8C8U~I7swLgFoUb$h94Q|lDXjOOgmeT@Bi_Vwvy7e_$_Yr?`{+OD zIwIas^u&0KuN%w{n;k@LxbX-Gc^LIe4mRa|qV!HCV>~l;pzk4p~ z!RsrwRSfVe3I*vQN6^`D0OIx2c>mxU3eYnxaMYcW{CB)4qygqyi;m}ZP88#PRm<@k zNpAyq-pOmKu~i$SL9+Orzl?3qJ38MtS>0+{5|a0FhtyrIyxMwD+iUTjf|*nhQtFax z+iF9ql4}>FDq$a*{t~q_RMCELNT~qLJBI(skK{7}NdrSlHBo%f!NMUSJ@}fa{e6vr z+m_=sIaXH_17GOOz#STv!Lb_5PTw9TG|Dx-1$DWG7f$IQN3=a4Q zR1^;Q22>ml_yyzw4mjOuFKOZd4mbsLs8Vpi?<=;GLK;&94!{sg&lgJ}efd$+gWD10 z0S-6?t&$@P7!>(ur~-rhz4X0`$*gn;>xL2U>OCXosM0Hbq)X6R%4 QL;wH)07*qoM6N<$f-lR^CIA2c diff --git a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png deleted file mode 100644 index 5c656012e61570308c4565423540d975b0daf689..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1219 zcmV;!1U&nRP)dbVG7wVRUJ4ZXi@?ZDjy4FEcSOF)=O}HJktd1RP03 zK~#90?VQndn=llHf93Q&=>*XR^#-96)Ek6s&<#p9$O!HP^#&yyBpZ+kY9{#mfJiYG zNEZoVH+<*tV7TU9{RknhBoquJGXUQKe8b132CxTED5WX@Pyj%(0Pth6u+EPfz*Z?$ zDw10Ozm|^Z45sSE}P=oWbH@EOCklY#{voV)IJCb@XVJt#v<1;$31uB_Pm;s^6m<5U@ z(1B#WlrVkR+_O8oNgGYn=j-*ax)>`(F^GkNMDFJEn<<_w2_013A7VB^17!JAZ{}`@(>vlNDG~$ zY;#itd;~QEVDn+)Xehp4gDRoZ5NZI~fH(jEb1XBqA!GvEs{5lakfa7_fvt_LQp)Mi z*o3BewZQf$roKRuK2S*e^E_5_`WV}gHc1lekp{NMdJx#=hpy!lYBe1IeA&>2vFHdso-Wcke^Zb`X&tvamg~SP(1}-!;8iN}0 z3}i8Q#Bn9G#0x*z^cQlSK($W6bDemd#OYw22JB#+NbX==iV#^Ka$U^gWL*yBNxpQU zePQASbzO?33-5vpF3D_})JdBgp2?!#mUUUUCneVp3wROK%bh69EUDkQ1ZuhXkDDkq zN&5LeGjpMz$5CuD|EXuoU(^SdG@2miz8aTC4PbL+GXLk2Pd*pg`PtNj1~002ovPDHLkV1g+19F_n8 diff --git a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png deleted file mode 100644 index 28b4b5438e55af7320e0a0e50a8a438de23db9f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1002 zcmVdbVG7wVRUJ4ZXi@?ZDjy4FEcSOF)=O}HJktd14Btf zK~#90?VQnd+AtJ`zsu=+VS=U;gicU0LD?X5gJgm-0-2yNLCFSWf|3bJCTKjt?So}X zIkx54vaekHos)AC%a(rsxw@7tf#6Wp8E^-D;c`iUEwB`k1AqXi>Js>7CM!M?U@js% zQPnx{!&*^R@*ixRFYmbERU8@2HmRaIPvcEgBTZC0U=NqWP{2B)^^lR=e?QPoArMq@W9Ax-e>ipDGn{bFAO zQc(Y)&b6w#bi)Im{zcU`YTZ-N${O6n+y1TX8*|hZw6X>_6`L_fU4iSH)xF8sj5+FR zq0WP*H&Zo#>{Xj}&o*hppo~{lU8(9-1*2xM8I#e~%IYJq${Y0-(UC4MT`xVqkd9Th zzP0&FH7LV70K9aAPRX5gMxYCb!ysVT-O*bHgFFldc^C}xFc{=vFvvp;20Z|ioZ}N$ zW{UMV=&OjV%TCbrRqFRn)Z}2j(|+wc$(>{NaRjRfD%?+0gsSd<&#kB_!775*9bMBH zs|YIH?2w06*Z|Q6CI(gA{B(|m))*f1QDb6|R$TpPdQAwdB52XkHI1nwF#C6T?0EUSvPZKI}E>_#(FN6u_29;Y-}_J=0vb{PgM04 z&&LEVm}G=u|Ly@>as7PH5Jl%Cz;xsuwR3l1ASL^fx5EgIpU9b#Cd*4UDk1e?nyvKy Y0bAvkc^|Z@`Tzg`07*qoM6N<$g4MLdtpET3 diff --git a/packages/Tethering/res/values-af/strings.xml b/packages/Tethering/res/values-af/strings.xml deleted file mode 100644 index f4c43b16a2a2..000000000000 --- a/packages/Tethering/res/values-af/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Verbinding of warmkol is aktief" - "Tik om op te stel." - - "Verbinding is gedeaktiveer" - "Kontak jou administrateur vir besonderhede" - "Warmkol- en verbindingstatus" - - - - - - - diff --git a/packages/Tethering/res/values-am/strings.xml b/packages/Tethering/res/values-am/strings.xml deleted file mode 100644 index 3a8de1200eb8..000000000000 --- a/packages/Tethering/res/values-am/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "እንደ ሞደም መሰካት ወይም መገናኛ ነጥብ ገባሪ" - "ለማዋቀር መታ ያድርጉ።" - - "እንደ ሞደም መሰካት ተሰናክሏል" - "ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ" - "መገናኛ ነጥብ እና እንደ ሞደም የመሰካት ሁኔታ" - - - - - - - diff --git a/packages/Tethering/res/values-ar/strings.xml b/packages/Tethering/res/values-ar/strings.xml deleted file mode 100644 index 355f59f09656..000000000000 --- a/packages/Tethering/res/values-ar/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "النطاق نشط أو نقطة الاتصال نشطة" - "انقر للإعداد." - - "التوصيل متوقف." - "تواصَل مع المشرف للحصول على التفاصيل." - "حالة نقطة الاتصال والتوصيل" - - - - - - - diff --git a/packages/Tethering/res/values-as/strings.xml b/packages/Tethering/res/values-as/strings.xml deleted file mode 100644 index f44cec0be8cb..000000000000 --- a/packages/Tethering/res/values-as/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "টে\'ডাৰিং অথবা হ\'টস্প\'ট সক্ৰিয় অৱস্থাত আছে" - "ছেট আপ কৰিবলৈ টিপক।" - - "টে\'ডাৰিঙৰ সুবিধাটো অক্ষম কৰি থোৱা হৈছে" - "সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক" - "হ’টস্প\'ট আৰু টে\'ডাৰিঙৰ স্থিতি" - - - - - - - diff --git a/packages/Tethering/res/values-az/strings.xml b/packages/Tethering/res/values-az/strings.xml deleted file mode 100644 index afd29dffbd39..000000000000 --- a/packages/Tethering/res/values-az/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Birləşmə və ya hotspot aktivdir" - "Ayarlamaq üçün toxunun." - - "Birləşmə deaktivdir" - "Detallar üçün adminlə əlaqə saxlayın" - "Hotspot & birləşmə statusu" - - - - - - - diff --git a/packages/Tethering/res/values-b+sr+Latn/strings.xml b/packages/Tethering/res/values-b+sr+Latn/strings.xml deleted file mode 100644 index 3ec6b75b34ab..000000000000 --- a/packages/Tethering/res/values-b+sr+Latn/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Privezivanje ili hotspot je aktivan" - "Dodirnite da biste podesili." - - "Privezivanje je onemogućeno" - "Potražite detalje od administratora" - "Status hotspota i privezivanja" - - - - - - - diff --git a/packages/Tethering/res/values-be/strings.xml b/packages/Tethering/res/values-be/strings.xml deleted file mode 100644 index 577c1d7bdd17..000000000000 --- a/packages/Tethering/res/values-be/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Мадэм або хот-спот актыўныя" - "Дакраніцеся, каб наладзіць." - - "Рэжым мадэма выключаны" - "Звярніцеся да адміністратара па падрабязную інфармацыю" - "Стан \"Хот-спот і мадэм\"" - - - - - - - diff --git a/packages/Tethering/res/values-bg/strings.xml b/packages/Tethering/res/values-bg/strings.xml deleted file mode 100644 index 9956a6191b64..000000000000 --- a/packages/Tethering/res/values-bg/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Има активна споделена връзка или точка за достъп" - "Докоснете, за да настроите." - - "Функцията за тетъринг е деактивирана" - "Свържете се с администратора си за подробности" - "Състояние на функцията за точка за достъп и тетъринг" - - - - - - - diff --git a/packages/Tethering/res/values-bn/strings.xml b/packages/Tethering/res/values-bn/strings.xml deleted file mode 100644 index 44d8dc619121..000000000000 --- a/packages/Tethering/res/values-bn/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "টিথারিং বা হটস্পট চালু আছে" - "সেট-আপ করতে ট্যাপ করুন।" - - "টিথারিং বন্ধ করা আছে" - "বিশদে জানতে অ্যাডমিনের সাথে যোগাযোগ করুন" - "হটস্পট ও টিথারিং স্ট্যাটাস" - - - - - - - diff --git a/packages/Tethering/res/values-bs/strings.xml b/packages/Tethering/res/values-bs/strings.xml deleted file mode 100644 index bf0395b7eac6..000000000000 --- a/packages/Tethering/res/values-bs/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Aktivno je povezivanje putem mobitela ili pristupna tačka" - "Dodirnite da postavite." - - "Povezivanje putem mobitela je onemogućeno" - "Kontaktirajte svog administratora za detalje" - "Status pristupne tačke i povezivanja putem mobitela" - - - - - - - diff --git a/packages/Tethering/res/values-ca/strings.xml b/packages/Tethering/res/values-ca/strings.xml deleted file mode 100644 index cbc161a4e984..000000000000 --- a/packages/Tethering/res/values-ca/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Compartició de xarxa o punt d\'accés Wi‑Fi actius" - "Toca per configurar." - - "La compartició de xarxa està desactivada" - "Contacta amb el teu administrador per obtenir més informació" - "Estat del punt d\'accés Wi‑Fi i de la compartició de xarxa" - - - - - - - diff --git a/packages/Tethering/res/values-cs/strings.xml b/packages/Tethering/res/values-cs/strings.xml deleted file mode 100644 index 5c21603987f7..000000000000 --- a/packages/Tethering/res/values-cs/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tethering nebo hotspot je aktivní" - "Klepnutím zahájíte nastavení." - - "Tethering je zakázán" - "O podrobnosti požádejte administrátora" - "Stav hotspotu a tetheringu" - - - - - - - diff --git a/packages/Tethering/res/values-da/strings.xml b/packages/Tethering/res/values-da/strings.xml deleted file mode 100644 index 741c7e2d79e2..000000000000 --- a/packages/Tethering/res/values-da/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Netdeling eller hotspot er aktivt" - "Tryk for at konfigurere." - - "Netdeling er deaktiveret" - "Kontakt din administrator for at få oplysninger" - "Status for hotspot og netdeling" - - - - - - - diff --git a/packages/Tethering/res/values-de/strings.xml b/packages/Tethering/res/values-de/strings.xml deleted file mode 100644 index 980a0626741a..000000000000 --- a/packages/Tethering/res/values-de/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tethering oder Hotspot aktiv" - "Zum Einrichten tippen." - - "Tethering ist deaktiviert" - "Bitte wende dich für weitere Informationen an den Administrator" - "Hotspot- und Tethering-Status" - - - - - - - diff --git a/packages/Tethering/res/values-el/strings.xml b/packages/Tethering/res/values-el/strings.xml deleted file mode 100644 index 3d8ad1efef5d..000000000000 --- a/packages/Tethering/res/values-el/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή" - "Πατήστε για ρύθμιση." - - "Η σύνδεση είναι απενεργοποιημένη" - "Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες" - "Κατάσταση σημείου πρόσβασης Wi-Fi και σύνδεσης" - - - - - - - diff --git a/packages/Tethering/res/values-en-rAU/strings.xml b/packages/Tethering/res/values-en-rAU/strings.xml deleted file mode 100644 index 18db440b3e86..000000000000 --- a/packages/Tethering/res/values-en-rAU/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tethering or hotspot active" - "Tap to set up." - - "Tethering is disabled" - "Contact your admin for details" - "Hotspot and tethering status" - - - - - - - diff --git a/packages/Tethering/res/values-en-rCA/strings.xml b/packages/Tethering/res/values-en-rCA/strings.xml deleted file mode 100644 index 18db440b3e86..000000000000 --- a/packages/Tethering/res/values-en-rCA/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tethering or hotspot active" - "Tap to set up." - - "Tethering is disabled" - "Contact your admin for details" - "Hotspot and tethering status" - - - - - - - diff --git a/packages/Tethering/res/values-en-rGB/strings.xml b/packages/Tethering/res/values-en-rGB/strings.xml deleted file mode 100644 index 18db440b3e86..000000000000 --- a/packages/Tethering/res/values-en-rGB/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tethering or hotspot active" - "Tap to set up." - - "Tethering is disabled" - "Contact your admin for details" - "Hotspot and tethering status" - - - - - - - diff --git a/packages/Tethering/res/values-en-rIN/strings.xml b/packages/Tethering/res/values-en-rIN/strings.xml deleted file mode 100644 index 18db440b3e86..000000000000 --- a/packages/Tethering/res/values-en-rIN/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tethering or hotspot active" - "Tap to set up." - - "Tethering is disabled" - "Contact your admin for details" - "Hotspot and tethering status" - - - - - - - diff --git a/packages/Tethering/res/values-en-rXC/strings.xml b/packages/Tethering/res/values-en-rXC/strings.xml deleted file mode 100644 index 23866e0db13e..000000000000 --- a/packages/Tethering/res/values-en-rXC/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‎Tethering or hotspot active‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎Tap to set up.‎‏‎‎‏‎" - - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‎Tethering is disabled‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎Contact your admin for details‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎Hotspot & tethering status‎‏‎‎‏‎" - - - - - - - diff --git a/packages/Tethering/res/values-es-rUS/strings.xml b/packages/Tethering/res/values-es-rUS/strings.xml deleted file mode 100644 index 0bf6c4ed0976..000000000000 --- a/packages/Tethering/res/values-es-rUS/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Conexión a red o hotspot conectados" - "Presiona para configurar esta opción." - - "Se inhabilitó la conexión mediante dispositivo portátil" - "Para obtener más información, comunícate con el administrador" - "Estado del hotspot y la conexión mediante dispositivo portátil" - - - - - - - diff --git a/packages/Tethering/res/values-es/strings.xml b/packages/Tethering/res/values-es/strings.xml deleted file mode 100644 index 195868b5d0e4..000000000000 --- a/packages/Tethering/res/values-es/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Conexión compartida o punto de acceso activos" - "Toca para configurar." - - "La conexión compartida está inhabilitada" - "Solicita más información a tu administrador" - "Estado del punto de acceso y de la conexión compartida" - - - - - - - diff --git a/packages/Tethering/res/values-et/strings.xml b/packages/Tethering/res/values-et/strings.xml deleted file mode 100644 index c4700a9638df..000000000000 --- a/packages/Tethering/res/values-et/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Jagamine või kuumkoht on aktiivne" - "Puudutage seadistamiseks." - - "Jagamine on keelatud" - "Lisateabe saamiseks võtke ühendust oma administraatoriga" - "Kuumkoha ja jagamise olek" - - - - - - - diff --git a/packages/Tethering/res/values-eu/strings.xml b/packages/Tethering/res/values-eu/strings.xml deleted file mode 100644 index bcb92d96be24..000000000000 --- a/packages/Tethering/res/values-eu/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Konexioa partekatzea edo sare publikoa aktibo" - "Sakatu konfiguratzeko." - - "Desgaituta dago konexioa partekatzeko aukera" - "Xehetasunak lortzeko, jarri administratzailearekin harremanetan" - "Sare publikoaren eta konexioa partekatzeko eginbidearen egoera" - - - - - - - diff --git a/packages/Tethering/res/values-fa/strings.xml b/packages/Tethering/res/values-fa/strings.xml deleted file mode 100644 index 51c3d731c050..000000000000 --- a/packages/Tethering/res/values-fa/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "اشتراک‌گذاری اینترنت یا نقطه اتصال فعال" - "برای راه‌اندازی ضربه بزنید." - - "اشتراک‌گذاری اینترنت غیرفعال است" - "برای جزئیات، با سرپرستتان تماس بگیرید" - "وضعیت نقطه اتصال و اشتراک‌گذاری اینترنت" - - - - - - - diff --git a/packages/Tethering/res/values-fi/strings.xml b/packages/Tethering/res/values-fi/strings.xml deleted file mode 100644 index 7a54e1615757..000000000000 --- a/packages/Tethering/res/values-fi/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Yhteyden jakaminen tai hotspot käytössä" - "Ota käyttöön napauttamalla." - - "Yhteyden jakaminen on poistettu käytöstä" - "Pyydä lisätietoja järjestelmänvalvojalta" - "Hotspotin ja yhteyden jakamisen tila" - - - - - - - diff --git a/packages/Tethering/res/values-fr-rCA/strings.xml b/packages/Tethering/res/values-fr-rCA/strings.xml deleted file mode 100644 index 556748f5f76f..000000000000 --- a/packages/Tethering/res/values-fr-rCA/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Partage de connexion ou point d\'accès sans fil activé" - "Touchez pour configurer." - - "Le partage de connexion est désactivé" - "Communiquez avec votre administrateur pour obtenir plus de détails" - "Point d\'accès et partage de connexion" - - - - - - - diff --git a/packages/Tethering/res/values-fr/strings.xml b/packages/Tethering/res/values-fr/strings.xml deleted file mode 100644 index 9fe55a23947d..000000000000 --- a/packages/Tethering/res/values-fr/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Partage de connexion ou point d\'accès activé" - "Appuyez pour effectuer la configuration." - - "Le partage de connexion est désactivé" - "Pour en savoir plus, contactez votre administrateur" - "État du point d\'accès et du partage de connexion" - - - - - - - diff --git a/packages/Tethering/res/values-gl/strings.xml b/packages/Tethering/res/values-gl/strings.xml deleted file mode 100644 index 474371a128dd..000000000000 --- a/packages/Tethering/res/values-gl/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Conexión compartida ou zona wifi activada" - "Toca para configurar." - - "A conexión compartida está desactivada" - "Contacta co administrador para obter información" - "Estado da zona wifi e da conexión compartida" - - - - - - - diff --git a/packages/Tethering/res/values-gu/strings.xml b/packages/Tethering/res/values-gu/strings.xml deleted file mode 100644 index cdb830a79a41..000000000000 --- a/packages/Tethering/res/values-gu/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "ઇન્ટરનેટ શેર કરવાની સુવિધા અથવા હૉટસ્પૉટ સક્રિય છે" - "સેટઅપ કરવા માટે ટૅપ કરો." - - "ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરી છે" - "વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો" - "હૉટસ્પૉટ અને ઇન્ટરનેટ શેર કરવાની સુવિધાનું સ્ટેટસ" - - - - - - - diff --git a/packages/Tethering/res/values-hi/strings.xml b/packages/Tethering/res/values-hi/strings.xml deleted file mode 100644 index f9e157c9f5a0..000000000000 --- a/packages/Tethering/res/values-hi/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "टेदरिंग या हॉटस्पॉट चालू है" - "सेट अप करने के लिए टैप करें." - - "टेदरिंग बंद है" - "जानकारी के लिए अपने एडमिन से संपर्क करें" - "हॉटस्पॉट और टेदरिंग की स्थिति" - - - - - - - diff --git a/packages/Tethering/res/values-hr/strings.xml b/packages/Tethering/res/values-hr/strings.xml deleted file mode 100644 index 9a99c6457c18..000000000000 --- a/packages/Tethering/res/values-hr/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Modemsko povezivanje ili žarišna točka aktivni" - "Dodirnite da biste postavili." - - "Modemsko je povezivanje onemogućeno" - "Obratite se administratoru da biste saznali pojedinosti" - "Status žarišne točke i modemskog povezivanja" - - - - - - - diff --git a/packages/Tethering/res/values-hu/strings.xml b/packages/Tethering/res/values-hu/strings.xml deleted file mode 100644 index f27c1c3e6334..000000000000 --- a/packages/Tethering/res/values-hu/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Megosztás vagy aktív hotspot" - "Koppintson a beállításhoz." - - "Az internetmegosztás le van tiltva" - "A részletekért forduljon rendszergazdájához" - "Hotspot és internetmegosztás állapota" - - - - - - - diff --git a/packages/Tethering/res/values-hy/strings.xml b/packages/Tethering/res/values-hy/strings.xml deleted file mode 100644 index b8b95ea5f97e..000000000000 --- a/packages/Tethering/res/values-hy/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Մոդեմի ռեժիմը միացված է" - "Հպեք՝ կարգավորելու համար։" - - "Մոդեմի ռեժիմն անջատված է" - "Մանրամասների համար դիմեք ձեր ադմինիստրատորին" - "Թեժ կետի և մոդեմի ռեժիմի կարգավիճակը" - - - - - - - diff --git a/packages/Tethering/res/values-in/strings.xml b/packages/Tethering/res/values-in/strings.xml deleted file mode 100644 index 24ead4eb3c29..000000000000 --- a/packages/Tethering/res/values-in/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tethering atau hotspot aktif" - "Ketuk untuk menyiapkan." - - "Tethering dinonaktifkan" - "Hubungi admin untuk mengetahui detailnya" - "Status hotspot & tethering" - - - - - - - diff --git a/packages/Tethering/res/values-is/strings.xml b/packages/Tethering/res/values-is/strings.xml deleted file mode 100644 index 839b0b96fcfe..000000000000 --- a/packages/Tethering/res/values-is/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Kveikt á tjóðrun eða aðgangsstað" - "Ýttu til að setja upp." - - "Slökkt er á tjóðrun" - "Hafðu samband við kerfisstjórann til að fá upplýsingar" - "Staða heits reits og tjóðrunar" - - - - - - - diff --git a/packages/Tethering/res/values-it/strings.xml b/packages/Tethering/res/values-it/strings.xml deleted file mode 100644 index 31e2b73cf6d0..000000000000 --- a/packages/Tethering/res/values-it/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Hotspot o tethering attivo" - "Tocca per impostare." - - "Tethering disattivato" - "Contatta il tuo amministratore per avere informazioni dettagliate" - "Stato hotspot e tethering" - - - - - - - diff --git a/packages/Tethering/res/values-iw/strings.xml b/packages/Tethering/res/values-iw/strings.xml deleted file mode 100644 index c97064b8d2c2..000000000000 --- a/packages/Tethering/res/values-iw/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "נקודה לשיתוף אינטרנט או שיתוף אינטרנט בין מכשירים: בסטטוס פעיל" - "יש להקיש כדי להגדיר." - - "שיתוף האינטרנט בין מכשירים מושבת" - "לפרטים, יש לפנות למנהל המערכת" - "סטטוס של נקודה לשיתוף אינטרנט ושיתוף אינטרנט בין מכשירים" - - - - - - - diff --git a/packages/Tethering/res/values-ja/strings.xml b/packages/Tethering/res/values-ja/strings.xml deleted file mode 100644 index c65f6e2f71b0..000000000000 --- a/packages/Tethering/res/values-ja/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "テザリングまたはアクセス ポイントが有効です" - "タップしてセットアップします。" - - "テザリングは無効に設定されています" - "詳しくは、管理者にお問い合わせください" - "アクセス ポイントとテザリングのステータス" - - - - - - - diff --git a/packages/Tethering/res/values-ka/strings.xml b/packages/Tethering/res/values-ka/strings.xml deleted file mode 100644 index 0dca3763f693..000000000000 --- a/packages/Tethering/res/values-ka/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "ტეტერინგი ან უსადენო ქსელი აქტიურია" - "შეეხეთ დასაყენებლად." - - "ტეტერინგი გათიშულია" - "დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს" - "უსადენო ქსელის და ტეტერინგის სტატუსი" - - - - - - - diff --git a/packages/Tethering/res/values-kk/strings.xml b/packages/Tethering/res/values-kk/strings.xml deleted file mode 100644 index 9b4423536bda..000000000000 --- a/packages/Tethering/res/values-kk/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Тетеринг немесе хотспот қосулы" - "Реттеу үшін түртіңіз." - - "Тетеринг өшірілді." - "Мәліметтерді әкімшіден алыңыз." - "Хотспот және тетеринг күйі" - - - - - - - diff --git a/packages/Tethering/res/values-km/strings.xml b/packages/Tethering/res/values-km/strings.xml deleted file mode 100644 index 7a6ab98d8852..000000000000 --- a/packages/Tethering/res/values-km/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "ការភ្ជាប់ ឬហតស្ប៉ត​កំពុងដំណើរការ" - "ចុច​ដើម្បី​រៀបចំ។" - - "ការភ្ជាប់​ត្រូវបានបិទ" - "ទាក់ទងអ្នកគ្រប់គ្រង​របស់អ្នក ដើម្បីទទួលបានព័ត៌មានលម្អិត" - "ស្ថានភាពនៃការភ្ជាប់ និងហតស្ប៉ត" - - - - - - - diff --git a/packages/Tethering/res/values-kn/strings.xml b/packages/Tethering/res/values-kn/strings.xml deleted file mode 100644 index 7c744b83e401..000000000000 --- a/packages/Tethering/res/values-kn/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್‌ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ" - "ಸೆಟಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ." - - "ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" - "ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ" - "ಹಾಟ್‌ಸ್ಪಾಟ್ ಮತ್ತು ಟೆಥರಿಂಗ್‌ ಸ್ಥಿತಿ" - - - - - - - diff --git a/packages/Tethering/res/values-ko/strings.xml b/packages/Tethering/res/values-ko/strings.xml deleted file mode 100644 index ecbddf5fdc08..000000000000 --- a/packages/Tethering/res/values-ko/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "테더링 또는 핫스팟 사용" - "설정하려면 탭하세요." - - "테더링이 사용 중지됨" - "자세한 정보는 관리자에게 문의하세요." - "핫스팟 및 테더링 상태" - - - - - - - diff --git a/packages/Tethering/res/values-ky/strings.xml b/packages/Tethering/res/values-ky/strings.xml deleted file mode 100644 index f763bf3ff0eb..000000000000 --- a/packages/Tethering/res/values-ky/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Жалгаштыруу же хотспот жандырылган" - "Жөндөө үчүн таптап коюңуз." - - "Жалгаштыруу функциясы өчүрүлгөн" - "Кеңири маалымат үчүн администраторуңузга кайрылыңыз" - "Хотспот жана байланыш түйүнүүн статусу" - - - - - - - diff --git a/packages/Tethering/res/values-lo/strings.xml b/packages/Tethering/res/values-lo/strings.xml deleted file mode 100644 index d85b1bd0965e..000000000000 --- a/packages/Tethering/res/values-lo/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "ເປີດການປ່ອຍສັນຍານ ຫຼື ຮັອດສະປອດແລ້ວ" - "ແຕະເພື່ອຕັ້ງຄ່າ." - - "ການປ່ອຍສັນຍານຖືກປິດໄວ້" - "ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ" - "ສະຖານະຮັອດສະປອດ ແລະ ການປ່ອຍສັນຍານ" - - - - - - - diff --git a/packages/Tethering/res/values-lt/strings.xml b/packages/Tethering/res/values-lt/strings.xml deleted file mode 100644 index 9a875932ff5f..000000000000 --- a/packages/Tethering/res/values-lt/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Įrenginys naudojamas kaip modemas arba įjungtas viešosios interneto prieigos taškas" - "Palieskite, kad nustatytumėte." - - "Įrenginio kaip modemo naudojimas išjungtas" - "Jei reikia išsamios informacijos, susisiekite su administratoriumi" - "Viešosios interneto prieigos taško ir įrenginio kaip modemo naudojimo būsena" - - - - - - - diff --git a/packages/Tethering/res/values-lv/strings.xml b/packages/Tethering/res/values-lv/strings.xml deleted file mode 100644 index bb32ab41b12d..000000000000 --- a/packages/Tethering/res/values-lv/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Piesaiste vai tīklājs ir aktīvs." - "Pieskarieties, lai to iestatītu." - - "Piesaiste ir atspējota" - "Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru." - "Tīklāja un piesaistes statuss" - - - - - - - diff --git a/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml deleted file mode 100644 index 052ca091ac14..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Warmkol het nie internet nie" - "Toestelle kan nie aan internet koppel nie" - "Skakel warmkol af" - "Warmkol is aan" - "Bykomende heffings kan geld terwyl jy swerf" - "Gaan voort" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml deleted file mode 100644 index 0518c5a14f22..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "መገናኛ ነጥቡ በይነመረብ የለውም" - "መሣሪያዎች ከበይነመረብ ጋር መገናኘት አይችሉም" - "መገናኛ ነጥብ ያጥፉ" - "የመገናኛ ነጥብ በርቷል" - "በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ" - "ቀጥል" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml deleted file mode 100644 index 40eb9a741c9c..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "متابعة" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml deleted file mode 100644 index 4c57f21eaeb3..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "হটস্পটৰ কোনো ইণ্টাৰনেট নাই" - "ডিভাইচসমূহ ইণ্টাৰনেটৰ সৈতে সংযোগ কৰিব নোৱাৰি" - "হটস্পট অফ কৰক" - "হটস্পট অন হৈ আছে" - "ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে" - "অব্যাহত ৰাখক" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml deleted file mode 100644 index 2610ab1bec8d..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspotun internetə girişi yoxdur" - "Cihazlar internetə qoşula bilmir" - "Hotspot\'u deaktiv edin" - "Hotspot aktivdir" - "Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər" - "Davam edin" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml deleted file mode 100644 index 7b032badf09a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nema pristup internetu" - "Uređaji ne mogu da se povežu na internet" - "Isključi hotspot" - "Hotspot je uključen" - "Možda važe dodatni troškovi u romingu" - "Nastavi" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml deleted file mode 100644 index 2362a1e6a539..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хот-спот не падключаны да інтэрнэту" - "Прылады не могуць падключацца да інтэрнэту" - "Выключыць хот-спот" - "Хот-спот уключаны" - "Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата" - "Працягнуць" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml deleted file mode 100644 index 6ef1b0bbaf62..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точката за достъп няма връзка с интернет" - "Устройствата не могат да се свържат с интернет" - "Изключване на точката за достъп" - "Точката за достъп е включена" - "Възможно е да ви бъдат начислени допълнителни такси при роуминг" - "Напред" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml deleted file mode 100644 index 7f9efba7f05f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "চালিয়ে যান" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml deleted file mode 100644 index 753973641568..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Pristupna tačka nema internet" - "Uređaji se ne mogu povezati na internet" - "Isključi pristupnu tačku" - "Pristupna tačka je uključena" - "Primjenjuju se dodatne tarife u romingu" - "Nastavi" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml deleted file mode 100644 index e3ad666c0bd4..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "El punt d\'accés Wi‑Fi no té accés a Internet" - "Els dispositius no es poden connectar a Internet" - "Desactiva el punt d\'accés Wi‑Fi" - "El punt d\'accés Wi‑Fi està activat" - "És possible que s\'apliquin costos addicionals en itinerància" - "Continua" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml deleted file mode 100644 index f0992814c128..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nemá připojení k internetu" - "Zařízení se nemohou připojit k internetu" - "Vypnout hotspot" - "Hotspot je aktivní" - "Při roamingu mohou být účtovány dodatečné poplatky" - "Pokračovat" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml deleted file mode 100644 index 1fb2374487e8..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspottet har intet internet" - "Enheder kan ikke oprette forbindelse til internettet" - "Deaktiver hotspot" - "Hotspottet er aktiveret" - "Der opkræves muligvis yderligere gebyrer ved roaming" - "Fortsæt" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml deleted file mode 100644 index 56d1d1df58a8..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot ist nicht mit dem Internet verbunden" - "Geräte können nicht mit dem Internet verbunden werden" - "Hotspot deaktivieren" - "Hotspot aktiviert" - "Für das Roaming können zusätzliche Gebühren anfallen" - "Weiter" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml deleted file mode 100644 index 674f1f6798ed..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Το σημείο πρόσβασης Wi-Fi δεν έχει πρόσβαση στο διαδίκτυο." - "Δεν είναι η δυνατή η σύνδεση των συσκευών στο διαδίκτυο." - "Απενεργοποίηση σημείου πρόσβασης Wi-Fi" - "Σημείο πρόσβασης Wi-Fi ενεργό" - "Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή." - "Συνέχεια" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml deleted file mode 100644 index 3046a3725d13..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml deleted file mode 100644 index 3046a3725d13..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml deleted file mode 100644 index 3046a3725d13..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml deleted file mode 100644 index 3046a3725d13..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml deleted file mode 100644 index 20c9b94cd5db..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‎‎‎Hotspot has no internet‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‏‎Devices can’t connect to internet‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‎Turn off hotspot‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎Hotspot is on‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎Additional charges may apply while roaming‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎Continue‎‏‎‎‏‎" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml deleted file mode 100644 index 196303fa8371..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "El hotspot no tiene Internet" - "Los dispositivos no pueden conectarse a Internet" - "Desactiva el hotspot" - "El hotspot está activado" - "Es posible que apliquen cargos adicionales por roaming" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml deleted file mode 100644 index cac5b23bd932..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "El punto de acceso no tiene conexión a Internet" - "Los dispositivos no se pueden conectar a Internet" - "Desactivar punto de acceso" - "Zona Wi-Fi activada" - "Puede que se apliquen cargos adicionales en itinerancia" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml deleted file mode 100644 index ff8dde542261..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Kuumkohal puudub Interneti-ühendus" - "Seadmed ei saa Internetiga ühendust luua" - "Lülita kuumkoht välja" - "Kuumkoht on sees" - "Rändluse kasutamisega võivad kaasneda lisatasud" - "Jätka" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml deleted file mode 100644 index 1758a4fada20..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Sare publikoak ez du Interneteko konexiorik" - "Gailuak ezin dira konektatu Internetera" - "Desaktibatu sare publikoa" - "Sare publikoa aktibatuta dago" - "Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritza moduan" - "Egin aurrera" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml deleted file mode 100644 index 79e3ef11d6e5..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "نقطه اتصال به اینترنت دسترسی ندارد" - "دستگاه‌ها به اینترنت متصل نشدند" - "نقطه اتصال را خاموش کنید" - "نقطه اتصال روشن است" - "ممکن است درحین فراگردی تغییرات دیگر اعمال شود" - "ادامه" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml deleted file mode 100644 index 64921bca9fe5..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspotilla ei ole internetyhteyttä" - "Laitteet eivät voi yhdistää internetiin" - "Laita hotspot pois päältä" - "Hotspot on päällä" - "Roaming voi aiheuttaa lisämaksuja" - "Jatka" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml deleted file mode 100644 index eda7b59761cd..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Le point d\'accès n\'est pas connecté à Internet" - "Appareils non connectés à Internet" - "Désactiver le point d\'accès" - "Le point d\'accès est activé" - "En itinérance, des frais supplémentaires peuvent s\'appliquer" - "Continuer" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml deleted file mode 100644 index eda7b59761cd..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Le point d\'accès n\'est pas connecté à Internet" - "Appareils non connectés à Internet" - "Désactiver le point d\'accès" - "Le point d\'accès est activé" - "En itinérance, des frais supplémentaires peuvent s\'appliquer" - "Continuer" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml deleted file mode 100644 index c163c61fbd8c..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "A zona wifi non ten acceso a Internet" - "Os dispositivos non se poden conectar a Internet" - "Desactivar zona wifi" - "A zona wifi está activada" - "Pódense aplicar cargos adicionais en itinerancia" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml deleted file mode 100644 index 0f4d26afdd2b..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "આગળ વધો" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml deleted file mode 100644 index a2442009b5ab..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "हॉटस्पॉट से इंटरनेट नहीं चल रहा" - "डिवाइस इंटरनेट से कनेक्ट नहीं हो पा रहे" - "हॉटस्पॉट बंद करें" - "हॉटस्पॉट चालू है" - "रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है" - "जारी रखें" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml deleted file mode 100644 index 41618afb2e89..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Žarišna točka nema pristup internetu" - "Uređaji se ne mogu povezati s internetom" - "Isključi žarišnu točku" - "Žarišna je točka uključena" - "U roamingu su mogući dodatni troškovi" - "Nastavi" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml deleted file mode 100644 index 39b7a6975b39..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "A hotspot nem csatlakozik az internethez" - "Az eszközök nem tudnak csatlakozni az internethez" - "Hotspot kikapcsolása" - "A hotspot be van kapcsolva" - "Roaming során további díjak léphetnek fel" - "Tovább" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml deleted file mode 100644 index c14ae10ad162..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Թեժ կետը միացված չէ ինտերնետին" - "Սարքերը չեն կարողանում միանալ ինտերնետին" - "Անջատել թեժ կետը" - "Թեժ կետը միացված է" - "Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել" - "Շարունակել" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml deleted file mode 100644 index 4998474a3619..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot tidak memiliki internet" - "Perangkat tidak dapat tersambung ke internet" - "Nonaktifkan hotspot" - "Hotspot aktif" - "Biaya tambahan mungkin berlaku saat roaming" - "Lanjutkan" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml deleted file mode 100644 index 82a7d0123455..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Heitur reitur er ekki nettengdur" - "Tæki geta ekki tengst við internetið" - "Slökkva á heitum reit" - "Kveikt er á heitum reit" - "Viðbótargjöld kunna að eiga við í reiki" - "Halda áfram" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml deleted file mode 100644 index a10d511c1770..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "L\'hotspot non ha accesso a Internet" - "I dispositivi non possono connettersi a Internet" - "Disattiva l\'hotspot" - "Hotspot attivo" - "Potrebbero essere addebitati costi aggiuntivi durante il roaming" - "Continua" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml deleted file mode 100644 index 80807bc23258..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "לנקודה לשיתוף אינטרנט אין חיבור לאינטרנט" - "המכשירים לא יכולים להתחבר לאינטרנט" - "כיבוי הנקודה לשיתוף אינטרנט" - "הנקודה לשיתוף אינטרנט פועלת" - "ייתכנו חיובים נוספים בעת נדידה" - "המשך" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml deleted file mode 100644 index 0e21a7f32292..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "アクセス ポイントがインターネットに接続されていません" - "デバイスをインターネットに接続できません" - "アクセス ポイントを OFF にする" - "アクセス ポイント: ON" - "ローミング時に追加料金が発生することがあります" - "続行" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml deleted file mode 100644 index 6d3b548744ba..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "უსადენო ქსელს არ აქვს ინტერნეტზე წვდომა" - "მოწყობილობები ვერ უკავშირდება ინტერნეტს" - "გამორთეთ უსადენო ქსელი" - "უსადენო ქსელი ჩართულია" - "როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური" - "გაგრძელება" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml deleted file mode 100644 index 985fc3ff9914..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хотспотта интернет жоқ" - "Құрылғылар интернетке қосылмайды" - "Хотспотты өшіру" - "Хотспот қосулы" - "Роуминг кезінде қосымша ақы алынуы мүмкін." - "Жалғастыру" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml deleted file mode 100644 index 03b5cb6e4b7d..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ហតស្ប៉ត​មិនមាន​អ៊ីនធឺណិត​ទេ" - "ឧបករណ៍​មិនអាច​ភ្ជាប់​អ៊ីនធឺណិត​បានទេ" - "បិទ​ហតស្ប៉ត" - "ហតស្ប៉ត​ត្រូវបានបើក" - "អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង" - "បន្ត" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml deleted file mode 100644 index 0427a776595b..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ಮುಂದುವರಿಸಿ" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml deleted file mode 100644 index 9218e9a09b58..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "핫스팟이 인터넷에 연결되지 않음" - "기기를 인터넷에 연결할 수 없음" - "핫스팟 사용 중지" - "핫스팟 사용 중" - "로밍 중에는 추가 요금이 발생할 수 있습니다." - "계속" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml deleted file mode 100644 index bc3d555597fd..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хотспоттун Интернети жок" - "Түзмөктөр Интернетке туташпай жатат" - "Туташуу түйүнүн өчүрүү" - "Кошулуу түйүнү күйүк" - "Роумингде кошумча акы алынышы мүмкүн" - "Улантуу" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml deleted file mode 100644 index 06dcbcbccc7b..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ສືບຕໍ່" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml deleted file mode 100644 index db5178bf2d57..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Nėra viešosios interneto prieigos taško interneto ryšio" - "Įrenginiams nepavyksta prisijungti prie interneto" - "Išjungti viešosios interneto prieigos tašką" - "Viešosios interneto prieigos taškas įjungtas" - "Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai" - "Tęsti" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml deleted file mode 100644 index c712173ca2b6..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Tīklājam nav interneta savienojuma" - "Ierīces nevar izveidot savienojumu ar internetu" - "Izslēgt tīklāju" - "Tīklājs ir ieslēgts" - "Viesabonēšanas laikā var tikt piemērota papildu samaksa" - "Tālāk" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml deleted file mode 100644 index aa4490912b87..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точката на пристап нема интернет" - "Уредите не може да се поврзат на интернет" - "Исклучи ја точката на пристап" - "Точката на пристап е вклучена" - "При роаминг може да се наплатат дополнителни трошоци" - "Продолжи" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml deleted file mode 100644 index 0ef956a5a424..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "തുടരുക" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml deleted file mode 100644 index 417213f543e9..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Сүлжээний цэг дээр интернэт алга байна" - "Төхөөрөмжүүд нь интернэтэд холбогдох боломжгүй байна" - "Сүлжээний цэгийг унтраах" - "Сүлжээний цэг асаалттай байна" - "Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй" - "Үргэлжлүүлэх" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml deleted file mode 100644 index 2ed153fb17a2..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "हॉटस्पॉटला इंटरनेट नाही" - "डिव्हाइस इंटरनेटला कनेक्ट करू शकत नाहीत" - "हॉटस्पॉट बंद करा" - "हॉटस्पॉट सुरू आहे" - "रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात" - "सुरू ठेवा" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml deleted file mode 100644 index 50817fd4a24b..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Tempat liputan tiada Internet" - "Peranti tidak dapat menyambung kepada Internet" - "Matikan tempat liputan" - "Tempat liputan dihidupkan" - "Caj tambahan mungkin digunakan semasa perayauan" - "Teruskan" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml deleted file mode 100644 index c0d70e3d5f11..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ဟော့စပေါ့တွင် အင်တာနက်မရှိပါ" - "စက်များက အင်တာနက်ချိတ်ဆက်၍ မရပါ" - "ဟော့စပေါ့ ပိတ်ရန်" - "ဟော့စပေါ့ ဖွင့်ထားသည်" - "ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်" - "ရှေ့ဆက်ရန်" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml deleted file mode 100644 index 1e7f1c6d0a9d..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Wi-Fi-sonen har ikke internettilgang" - "Enheter kan ikke koble til internett" - "Slå av Wi-Fi-sonen" - "Wi-Fi-sonen er på" - "Ytterligere kostnader kan påløpe under roaming" - "Fortsett" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml deleted file mode 100644 index fadd357ebfda..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "जारी राख्नुहोस्" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml deleted file mode 100644 index bf14a0fceda6..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot heeft geen internet" - "Apparaten kunnen geen verbinding maken met internet" - "Hotspot uitschakelen" - "Hotspot is ingeschakeld" - "Er kunnen extra kosten voor roaming in rekening worden gebracht." - "Doorgaan" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml deleted file mode 100644 index 1cdfce04d843..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ଜାରି ରଖନ୍ତୁ" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml deleted file mode 100644 index 93402c35d0d6..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ਜਾਰੀ ਰੱਖੋ" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml deleted file mode 100644 index 8becd0715f6f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nie ma internetu" - "Urządzenia nie mogą połączyć się z internetem" - "Wyłącz hotspot" - "Hotspot jest włączony" - "Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty" - "Dalej" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml deleted file mode 100644 index 8e01736f643f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "O ponto de acesso não tem conexão com a Internet" - "Não foi possível conectar os dispositivos à Internet" - "Desativar ponto de acesso" - "O ponto de acesso está ativado" - "Pode haver cobranças extras durante o roaming" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml deleted file mode 100644 index 2356379e2f8f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "A zona Wi-Fi não tem Internet" - "Não é possível ligar os dispositivos à Internet" - "Desativar zona Wi-Fi" - "A zona Wi-Fi está ativada" - "Podem aplicar-se custos adicionais em roaming." - "Continuar" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml deleted file mode 100644 index 8e01736f643f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "O ponto de acesso não tem conexão com a Internet" - "Não foi possível conectar os dispositivos à Internet" - "Desativar ponto de acesso" - "O ponto de acesso está ativado" - "Pode haver cobranças extras durante o roaming" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml deleted file mode 100644 index 2e62bd611cff..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspotul nu are internet" - "Dispozitivele nu se pot conecta la internet" - "Dezactivați hotspotul" - "Hotspotul este activ" - "Se pot aplica taxe suplimentare pentru roaming" - "Continuați" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml deleted file mode 100644 index 69f8c5961362..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точка доступа не подключена к Интернету" - "Не удается подключить устройства к Интернету" - "Отключить точку доступа" - "Точка доступа включена" - "За использование услуг связи в роуминге может взиматься дополнительная плата." - "Продолжить" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml deleted file mode 100644 index 632748a3e8f5..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "හොට්ස්පොට් හට අන්තර්ජාලය නැත" - "උපාංගවලට අන්තර්ජාලයට සම්බන්ධ විය නොහැකිය" - "හොට්ස්පොට් ක්‍රියාවිරහිත කරන්න" - "හොට්ස්පොට් ක්‍රියාත්මකයි" - "රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය" - "ඉදිරියට යන්න" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml deleted file mode 100644 index 247fc1b0e738..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nemá internetové pripojenie" - "Zariadenia sa nedajú pripojiť k internetu" - "Vypnúť hotspot" - "Hotspot je zapnutý" - "Počas roamingu vám môžu byť účtované ďalšie poplatky" - "Pokračovať" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml deleted file mode 100644 index ed223721978a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Dostopna točka nima internetne povezave" - "Naprave ne morejo vzpostaviti internetne povezave" - "Izklopi dostopno točko" - "Dostopna točka je vklopljena" - "Med gostovanjem lahko nastanejo dodatni stroški" - "Naprej" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml deleted file mode 100644 index 4bfab6e47449..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Zona e qasjes për internet nuk ka internet" - "Pajisjet nuk mund të lidhen me internetin" - "Çaktivizo zonën e qasjes për internet" - "Zona e qasjes për internet është aktive" - "Mund të zbatohen tarifime shtesë kur je në roaming" - "Vazhdo" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml deleted file mode 100644 index 478d53a25587..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хотспот нема приступ интернету" - "Уређаји не могу да се повежу на интернет" - "Искључи хотспот" - "Хотспот је укључен" - "Можда важе додатни трошкови у ромингу" - "Настави" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml deleted file mode 100644 index a793ed648347..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Surfzonen har ingen internetanslutning" - "Enheterna har ingen internetanslutning" - "Inaktivera surfzon" - "Surfzonen är aktiverad" - "Ytterligare avgifter kan tillkomma vid roaming" - "Fortsätt" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml deleted file mode 100644 index 3fe09fc22a4c..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Mtandao pepe hauna intaneti" - "Vifaa vimeshindwa kuunganisha kwenye intaneti" - "Zima mtandao pepe" - "Mtandaopepe umewashwa" - "Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo" - "Endelea" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml deleted file mode 100644 index 63c28c67024b..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "தொடர்க" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml deleted file mode 100644 index 2cf579ca0e9a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "కొనసాగించు" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml deleted file mode 100644 index 3837002b29a8..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ฮอตสปอตไม่ได้เชื่อมต่ออินเทอร์เน็ต" - "อุปกรณ์เชื่อมต่ออินเทอร์เน็ตไม่ได้" - "ปิดฮอตสปอต" - "ฮอตสปอตเปิดอยู่" - "อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง" - "ต่อไป" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml deleted file mode 100644 index 208f893447de..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Walang internet ang hotspot" - "Hindi makakonekta sa internet ang mga device" - "I-off ang hotspot" - "Naka-on ang hotspot" - "Posibleng magkaroon ng mga karagdagang singil habang nagro-roam" - "Ituloy" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml deleted file mode 100644 index 3482fafa2d9d..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot\'un internet bağlantısı yok" - "Cihazlar internete bağlanamıyor" - "Hotspot\'u kapat" - "Hotspot açık" - "Dolaşım sırasında ek ücretler uygulanabilir" - "Devam" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml deleted file mode 100644 index dea311443fda..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точка доступу не підключена до Інтернету" - "Не вдається підключити пристрої до Інтернету" - "Вимкнути точку доступу" - "Точку доступу ввімкнено" - "У роумінгу може стягуватися додаткова плата" - "Продовжити" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml deleted file mode 100644 index 09bc0c9eabb9..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ہاٹ اسپاٹ میں انٹرنیٹ نہیں ہے" - "آلات انٹرنیٹ سے منسلک نہیں ہو سکتے" - "ہاٹ اسپاٹ آف کریں" - "ہاٹ اسپاٹ آن ہے" - "رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں" - "جاری رکھیں" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml deleted file mode 100644 index 5231c5fff5ae..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "Davom etish" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml deleted file mode 100644 index bf4ee1011b41..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Điểm phát sóng không có kết nối Internet" - "Các thiết bị không thể kết nối Internet" - "Tắt điểm phát sóng" - "Điểm phát sóng đang bật" - "Bạn có thể mất thêm phí dữ liệu khi chuyển vùng" - "Tiếp tục" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml deleted file mode 100644 index 38c25636389f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "热点无法访问互联网" - "设备无法连接到互联网" - "关闭热点" - "热点已开启" - "漫游时可能会产生额外的费用" - "继续" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml deleted file mode 100644 index 3bb52e491f0a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "熱點沒有互聯網連線" - "裝置無法連線至互聯網" - "關閉熱點" - "已開啟熱點" - "漫遊時可能需要支付額外費用" - "繼續" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml deleted file mode 100644 index 298c3eac701a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "無線基地台沒有網際網路連線" - "裝置無法連上網際網路" - "關閉無線基地台" - "無線基地台已開啟" - "使用漫遊服務可能須支付額外費用" - "繼續" - diff --git a/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml deleted file mode 100644 index 3dc0078834c9..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "I-Hotspot ayina-inthanethi" - "Amadivayisi awakwazi ukuxhuma ku-inthanethi" - "Vala i-hotspot" - "I-Hotspot ivuliwe" - "Kungaba nezinkokhelo ezengeziwe uma uzula" - "Qhubeka" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml deleted file mode 100644 index 8f16cd19ac13..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Warmkol het nie internet nie" - "Toestelle kan nie aan internet koppel nie" - "Skakel warmkol af" - "Warmkol is aan" - "Bykomende heffings kan geld terwyl jy swerf" - "Gaan voort" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml deleted file mode 100644 index d064fd81d502..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "መገናኛ ነጥቡ በይነመረብ የለውም" - "መሣሪያዎች ከበይነመረብ ጋር መገናኘት አይችሉም" - "መገናኛ ነጥብ ያጥፉ" - "የመገናኛ ነጥብ በርቷል" - "በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ" - "ቀጥል" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml deleted file mode 100644 index e5e7e5e03d55..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "متابعة" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml deleted file mode 100644 index 5bcadfd7fcf6..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "হটস্পটৰ কোনো ইণ্টাৰনেট নাই" - "ডিভাইচসমূহ ইণ্টাৰনেটৰ সৈতে সংযোগ কৰিব নোৱাৰি" - "হটস্পট অফ কৰক" - "হটস্পট অন হৈ আছে" - "ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে" - "অব্যাহত ৰাখক" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml deleted file mode 100644 index 41c018eec10b..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspotun internetə girişi yoxdur" - "Cihazlar internetə qoşula bilmir" - "Hotspot\'u deaktiv edin" - "Hotspot aktivdir" - "Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər" - "Davam edin" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml deleted file mode 100644 index 8acc58797583..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nema pristup internetu" - "Uređaji ne mogu da se povežu na internet" - "Isključi hotspot" - "Hotspot je uključen" - "Možda važe dodatni troškovi u romingu" - "Nastavi" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml deleted file mode 100644 index b03379a899fd..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хот-спот не падключаны да інтэрнэту" - "Прылады не могуць падключацца да інтэрнэту" - "Выключыць хот-спот" - "Хот-спот уключаны" - "Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата" - "Працягнуць" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml deleted file mode 100644 index 122bdb69c696..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точката за достъп няма връзка с интернет" - "Устройствата не могат да се свържат с интернет" - "Изключване на точката за достъп" - "Точката за достъп е включена" - "Възможно е да ви бъдат начислени допълнителни такси при роуминг" - "Напред" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml deleted file mode 100644 index d5ee1a91ce5a..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "চালিয়ে যান" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml deleted file mode 100644 index ae86e0aa8e69..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Pristupna tačka nema internet" - "Uređaji se ne mogu povezati na internet" - "Isključi pristupnu tačku" - "Pristupna tačka je uključena" - "Primjenjuju se dodatne tarife u romingu" - "Nastavi" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml deleted file mode 100644 index 9c464266014c..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "El punt d\'accés Wi‑Fi no té accés a Internet" - "Els dispositius no es poden connectar a Internet" - "Desactiva el punt d\'accés Wi‑Fi" - "El punt d\'accés Wi‑Fi està activat" - "És possible que s\'apliquin costos addicionals en itinerància" - "Continua" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml deleted file mode 100644 index 66e4dfb3da5c..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nemá připojení k internetu" - "Zařízení se nemohou připojit k internetu" - "Vypnout hotspot" - "Hotspot je aktivní" - "Při roamingu mohou být účtovány dodatečné poplatky" - "Pokračovat" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml deleted file mode 100644 index 04a48a77c4a7..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspottet har intet internet" - "Enheder kan ikke oprette forbindelse til internettet" - "Deaktiver hotspot" - "Hotspottet er aktiveret" - "Der opkræves muligvis yderligere gebyrer ved roaming" - "Fortsæt" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml deleted file mode 100644 index a9136784e955..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot ist nicht mit dem Internet verbunden" - "Geräte können nicht mit dem Internet verbunden werden" - "Hotspot deaktivieren" - "Hotspot aktiviert" - "Für das Roaming können zusätzliche Gebühren anfallen" - "Weiter" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml deleted file mode 100644 index 19be3c707725..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Το σημείο πρόσβασης Wi-Fi δεν έχει πρόσβαση στο διαδίκτυο." - "Δεν είναι η δυνατή η σύνδεση των συσκευών στο διαδίκτυο." - "Απενεργοποίηση σημείου πρόσβασης Wi-Fi" - "Σημείο πρόσβασης Wi-Fi ενεργό" - "Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή." - "Συνέχεια" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml deleted file mode 100644 index b844c09c6211..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml deleted file mode 100644 index b844c09c6211..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml deleted file mode 100644 index b844c09c6211..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml deleted file mode 100644 index b844c09c6211..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml deleted file mode 100644 index 6384e89ce0bd..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‎‎Hotspot has no internet‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎Devices can’t connect to internet‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‎‎Turn off hotspot‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‎‏‎Hotspot is on‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎Additional charges may apply while roaming‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎‏‎‏‎‏‏‏‎‎Continue‎‏‎‎‏‎" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml deleted file mode 100644 index d4b6937881fc..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "El hotspot no tiene Internet" - "Los dispositivos no pueden conectarse a Internet" - "Desactiva el hotspot" - "El hotspot está activado" - "Es posible que apliquen cargos adicionales por roaming" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml deleted file mode 100644 index 158fd862960d..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "El punto de acceso no tiene conexión a Internet" - "Los dispositivos no se pueden conectar a Internet" - "Desactivar punto de acceso" - "Zona Wi-Fi activada" - "Puede que se apliquen cargos adicionales en itinerancia" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml deleted file mode 100644 index 271f82ad6a38..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Kuumkohal puudub Interneti-ühendus" - "Seadmed ei saa Internetiga ühendust luua" - "Lülita kuumkoht välja" - "Kuumkoht on sees" - "Rändluse kasutamisega võivad kaasneda lisatasud" - "Jätka" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml deleted file mode 100644 index 7a2b99e0284a..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Sare publikoak ez du Interneteko konexiorik" - "Gailuak ezin dira konektatu Internetera" - "Desaktibatu sare publikoa" - "Sare publikoa aktibatuta dago" - "Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritza moduan" - "Egin aurrera" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml deleted file mode 100644 index b370e0fd81c8..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "نقطه اتصال به اینترنت دسترسی ندارد" - "دستگاه‌ها به اینترنت متصل نشدند" - "نقطه اتصال را خاموش کنید" - "نقطه اتصال روشن است" - "ممکن است درحین فراگردی تغییرات دیگر اعمال شود" - "ادامه" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml deleted file mode 100644 index da86391ee97f..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspotilla ei ole internetyhteyttä" - "Laitteet eivät voi yhdistää internetiin" - "Laita hotspot pois päältä" - "Hotspot on päällä" - "Roaming voi aiheuttaa lisämaksuja" - "Jatka" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml deleted file mode 100644 index 6ffd8116e80d..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Le point d\'accès n\'est pas connecté à Internet" - "Appareils non connectés à Internet" - "Désactiver le point d\'accès" - "Le point d\'accès est activé" - "En itinérance, des frais supplémentaires peuvent s\'appliquer" - "Continuer" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml deleted file mode 100644 index 6ffd8116e80d..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Le point d\'accès n\'est pas connecté à Internet" - "Appareils non connectés à Internet" - "Désactiver le point d\'accès" - "Le point d\'accès est activé" - "En itinérance, des frais supplémentaires peuvent s\'appliquer" - "Continuer" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml deleted file mode 100644 index 9e7f00cbe0ab..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "A zona wifi non ten acceso a Internet" - "Os dispositivos non se poden conectar a Internet" - "Desactivar zona wifi" - "A zona wifi está activada" - "Pódense aplicar cargos adicionais en itinerancia" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml deleted file mode 100644 index e85c00c6481e..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "આગળ વધો" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml deleted file mode 100644 index b6faa3a0f7b8..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "हॉटस्पॉट से इंटरनेट नहीं चल रहा" - "डिवाइस इंटरनेट से कनेक्ट नहीं हो पा रहे" - "हॉटस्पॉट बंद करें" - "हॉटस्पॉट चालू है" - "रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है" - "जारी रखें" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml deleted file mode 100644 index 86b58ded2292..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Žarišna točka nema pristup internetu" - "Uređaji se ne mogu povezati s internetom" - "Isključi žarišnu točku" - "Žarišna je točka uključena" - "U roamingu su mogući dodatni troškovi" - "Nastavi" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml deleted file mode 100644 index 27ddabf29dfc..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "A hotspot nem csatlakozik az internethez" - "Az eszközök nem tudnak csatlakozni az internethez" - "Hotspot kikapcsolása" - "A hotspot be van kapcsolva" - "Roaming során további díjak léphetnek fel" - "Tovább" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml deleted file mode 100644 index abdb20762692..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Թեժ կետը միացված չէ ինտերնետին" - "Սարքերը չեն կարողանում միանալ ինտերնետին" - "Անջատել թեժ կետը" - "Թեժ կետը միացված է" - "Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել" - "Շարունակել" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml deleted file mode 100644 index 513d2fb040c6..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot tidak memiliki internet" - "Perangkat tidak dapat tersambung ke internet" - "Nonaktifkan hotspot" - "Hotspot aktif" - "Biaya tambahan mungkin berlaku saat roaming" - "Lanjutkan" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml deleted file mode 100644 index f4e5dd4ad337..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Heitur reitur er ekki nettengdur" - "Tæki geta ekki tengst við internetið" - "Slökkva á heitum reit" - "Kveikt er á heitum reit" - "Viðbótargjöld kunna að eiga við í reiki" - "Halda áfram" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml deleted file mode 100644 index b82363270bec..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "L\'hotspot non ha accesso a Internet" - "I dispositivi non possono connettersi a Internet" - "Disattiva l\'hotspot" - "Hotspot attivo" - "Potrebbero essere addebitati costi aggiuntivi durante il roaming" - "Continua" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml deleted file mode 100644 index 0922ee9e4de8..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "לנקודה לשיתוף אינטרנט אין חיבור לאינטרנט" - "המכשירים לא יכולים להתחבר לאינטרנט" - "כיבוי הנקודה לשיתוף אינטרנט" - "הנקודה לשיתוף אינטרנט פועלת" - "ייתכנו חיובים נוספים בעת נדידה" - "המשך" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml deleted file mode 100644 index 63ddc476e66e..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "アクセス ポイントがインターネットに接続されていません" - "デバイスをインターネットに接続できません" - "アクセス ポイントを OFF にする" - "アクセス ポイント: ON" - "ローミング時に追加料金が発生することがあります" - "続行" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml deleted file mode 100644 index 4f20c76a12af..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "უსადენო ქსელს არ აქვს ინტერნეტზე წვდომა" - "მოწყობილობები ვერ უკავშირდება ინტერნეტს" - "გამორთეთ უსადენო ქსელი" - "უსადენო ქსელი ჩართულია" - "როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური" - "გაგრძელება" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml deleted file mode 100644 index 11e293416b83..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хотспотта интернет жоқ" - "Құрылғылар интернетке қосылмайды" - "Хотспотты өшіру" - "Хотспот қосулы" - "Роуминг кезінде қосымша ақы алынуы мүмкін." - "Жалғастыру" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml deleted file mode 100644 index b8d94d40a33d..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ហតស្ប៉ត​មិនមាន​អ៊ីនធឺណិត​ទេ" - "ឧបករណ៍​មិនអាច​ភ្ជាប់​អ៊ីនធឺណិត​បានទេ" - "បិទ​ហតស្ប៉ត" - "ហតស្ប៉ត​ត្រូវបានបើក" - "អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង" - "បន្ត" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml deleted file mode 100644 index 3e8aaebbe952..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ಮುಂದುವರಿಸಿ" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml deleted file mode 100644 index 59de04c55d48..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "핫스팟이 인터넷에 연결되지 않음" - "기기를 인터넷에 연결할 수 없음" - "핫스팟 사용 중지" - "핫스팟 사용 중" - "로밍 중에는 추가 요금이 발생할 수 있습니다." - "계속" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml deleted file mode 100644 index 7ecb6970e799..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хотспоттун Интернети жок" - "Түзмөктөр Интернетке туташпай жатат" - "Туташуу түйүнүн өчүрүү" - "Кошулуу түйүнү күйүк" - "Роумингде кошумча акы алынышы мүмкүн" - "Улантуу" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml deleted file mode 100644 index 5d1766707c4a..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ສືບຕໍ່" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml deleted file mode 100644 index aa15bfe1f588..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Nėra viešosios interneto prieigos taško interneto ryšio" - "Įrenginiams nepavyksta prisijungti prie interneto" - "Išjungti viešosios interneto prieigos tašką" - "Viešosios interneto prieigos taškas įjungtas" - "Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai" - "Tęsti" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml deleted file mode 100644 index 1e0d2f1c6fa3..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Tīklājam nav interneta savienojuma" - "Ierīces nevar izveidot savienojumu ar internetu" - "Izslēgt tīklāju" - "Tīklājs ir ieslēgts" - "Viesabonēšanas laikā var tikt piemērota papildu samaksa" - "Tālāk" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml deleted file mode 100644 index 5fe2a49af645..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точката на пристап нема интернет" - "Уредите не може да се поврзат на интернет" - "Исклучи ја точката на пристап" - "Точката на пристап е вклучена" - "При роаминг може да се наплатат дополнителни трошоци" - "Продолжи" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml deleted file mode 100644 index 930ffa184ea6..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "തുടരുക" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml deleted file mode 100644 index 462e73f7a49b..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Сүлжээний цэг дээр интернэт алга байна" - "Төхөөрөмжүүд нь интернэтэд холбогдох боломжгүй байна" - "Сүлжээний цэгийг унтраах" - "Сүлжээний цэг асаалттай байна" - "Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй" - "Үргэлжлүүлэх" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml deleted file mode 100644 index b1d9b8505b94..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "हॉटस्पॉटला इंटरनेट नाही" - "डिव्हाइस इंटरनेटला कनेक्ट करू शकत नाहीत" - "हॉटस्पॉट बंद करा" - "हॉटस्पॉट सुरू आहे" - "रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात" - "सुरू ठेवा" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml deleted file mode 100644 index 936629ca1475..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Tempat liputan tiada Internet" - "Peranti tidak dapat menyambung kepada Internet" - "Matikan tempat liputan" - "Tempat liputan dihidupkan" - "Caj tambahan mungkin digunakan semasa perayauan" - "Teruskan" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml deleted file mode 100644 index 052df883eb98..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ဟော့စပေါ့တွင် အင်တာနက်မရှိပါ" - "စက်များက အင်တာနက်ချိတ်ဆက်၍ မရပါ" - "ဟော့စပေါ့ ပိတ်ရန်" - "ဟော့စပေါ့ ဖွင့်ထားသည်" - "ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်" - "ရှေ့ဆက်ရန်" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml deleted file mode 100644 index 09012cbfec6d..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Wi-Fi-sonen har ikke internettilgang" - "Enheter kan ikke koble til internett" - "Slå av Wi-Fi-sonen" - "Wi-Fi-sonen er på" - "Ytterligere kostnader kan påløpe under roaming" - "Fortsett" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml deleted file mode 100644 index e1770b3f777b..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "जारी राख्नुहोस्" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml deleted file mode 100644 index 912290cb6713..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot heeft geen internet" - "Apparaten kunnen geen verbinding maken met internet" - "Hotspot uitschakelen" - "Hotspot is ingeschakeld" - "Er kunnen extra kosten voor roaming in rekening worden gebracht." - "Doorgaan" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml deleted file mode 100644 index 6a842428e0de..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ଜାରି ରଖନ୍ତୁ" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml deleted file mode 100644 index bb1479d3fb0f..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ਜਾਰੀ ਰੱਖੋ" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml deleted file mode 100644 index 51d5c3fd7eb3..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nie ma internetu" - "Urządzenia nie mogą połączyć się z internetem" - "Wyłącz hotspot" - "Hotspot jest włączony" - "Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty" - "Dalej" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml deleted file mode 100644 index 6e605797d05e..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "O ponto de acesso não tem conexão com a Internet" - "Não foi possível conectar os dispositivos à Internet" - "Desativar ponto de acesso" - "O ponto de acesso está ativado" - "Pode haver cobranças extras durante o roaming" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml deleted file mode 100644 index 79957977dca9..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "A zona Wi-Fi não tem Internet" - "Não é possível ligar os dispositivos à Internet" - "Desativar zona Wi-Fi" - "A zona Wi-Fi está ativada" - "Podem aplicar-se custos adicionais em roaming." - "Continuar" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml deleted file mode 100644 index 6e605797d05e..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "O ponto de acesso não tem conexão com a Internet" - "Não foi possível conectar os dispositivos à Internet" - "Desativar ponto de acesso" - "O ponto de acesso está ativado" - "Pode haver cobranças extras durante o roaming" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml deleted file mode 100644 index 7be2f72195d9..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspotul nu are internet" - "Dispozitivele nu se pot conecta la internet" - "Dezactivați hotspotul" - "Hotspotul este activ" - "Se pot aplica taxe suplimentare pentru roaming" - "Continuați" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml deleted file mode 100644 index 6ab396d50de6..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точка доступа не подключена к Интернету" - "Не удается подключить устройства к Интернету" - "Отключить точку доступа" - "Точка доступа включена" - "За использование услуг связи в роуминге может взиматься дополнительная плата." - "Продолжить" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml deleted file mode 100644 index 357dd904ac99..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "හොට්ස්පොට් හට අන්තර්ජාලය නැත" - "උපාංගවලට අන්තර්ජාලයට සම්බන්ධ විය නොහැකිය" - "හොට්ස්පොට් ක්‍රියාවිරහිත කරන්න" - "හොට්ස්පොට් ක්‍රියාත්මකයි" - "රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය" - "ඉදිරියට යන්න" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml deleted file mode 100644 index 276e5797eeb3..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nemá internetové pripojenie" - "Zariadenia sa nedajú pripojiť k internetu" - "Vypnúť hotspot" - "Hotspot je zapnutý" - "Počas roamingu vám môžu byť účtované ďalšie poplatky" - "Pokračovať" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml deleted file mode 100644 index 884bddd292e8..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Dostopna točka nima internetne povezave" - "Naprave ne morejo vzpostaviti internetne povezave" - "Izklopi dostopno točko" - "Dostopna točka je vklopljena" - "Med gostovanjem lahko nastanejo dodatni stroški" - "Naprej" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml deleted file mode 100644 index a2caddf66713..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Zona e qasjes për internet nuk ka internet" - "Pajisjet nuk mund të lidhen me internetin" - "Çaktivizo zonën e qasjes për internet" - "Zona e qasjes për internet është aktive" - "Mund të zbatohen tarifime shtesë kur je në roaming" - "Vazhdo" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml deleted file mode 100644 index 774592333120..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хотспот нема приступ интернету" - "Уређаји не могу да се повежу на интернет" - "Искључи хотспот" - "Хотспот је укључен" - "Можда важе додатни трошкови у ромингу" - "Настави" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml deleted file mode 100644 index 906862aa1736..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Surfzonen har ingen internetanslutning" - "Enheterna har ingen internetanslutning" - "Inaktivera surfzon" - "Surfzonen är aktiverad" - "Ytterligare avgifter kan tillkomma vid roaming" - "Fortsätt" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml deleted file mode 100644 index 0eb922fff6e1..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Mtandao pepe hauna intaneti" - "Vifaa vimeshindwa kuunganisha kwenye intaneti" - "Zima mtandao pepe" - "Mtandaopepe umewashwa" - "Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo" - "Endelea" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml deleted file mode 100644 index 1d66c6d68948..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "தொடர்க" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml deleted file mode 100644 index 2ee0fa8ddafe..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "కొనసాగించు" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml deleted file mode 100644 index 44114e589128..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ฮอตสปอตไม่ได้เชื่อมต่ออินเทอร์เน็ต" - "อุปกรณ์เชื่อมต่ออินเทอร์เน็ตไม่ได้" - "ปิดฮอตสปอต" - "ฮอตสปอตเปิดอยู่" - "อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง" - "ต่อไป" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml deleted file mode 100644 index 440999014c65..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Walang internet ang hotspot" - "Hindi makakonekta sa internet ang mga device" - "I-off ang hotspot" - "Naka-on ang hotspot" - "Posibleng magkaroon ng mga karagdagang singil habang nagro-roam" - "Ituloy" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml deleted file mode 100644 index d21ad95181f8..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot\'un internet bağlantısı yok" - "Cihazlar internete bağlanamıyor" - "Hotspot\'u kapat" - "Hotspot açık" - "Dolaşım sırasında ek ücretler uygulanabilir" - "Devam" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml deleted file mode 100644 index e7b8c68eb1e0..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точка доступу не підключена до Інтернету" - "Не вдається підключити пристрої до Інтернету" - "Вимкнути точку доступу" - "Точку доступу ввімкнено" - "У роумінгу може стягуватися додаткова плата" - "Продовжити" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml deleted file mode 100644 index 08edfcffeb80..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ہاٹ اسپاٹ میں انٹرنیٹ نہیں ہے" - "آلات انٹرنیٹ سے منسلک نہیں ہو سکتے" - "ہاٹ اسپاٹ آف کریں" - "ہاٹ اسپاٹ آن ہے" - "رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں" - "جاری رکھیں" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml deleted file mode 100644 index 9def0a1b065b..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "Davom etish" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml deleted file mode 100644 index e4f818bf42b4..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Điểm phát sóng không có kết nối Internet" - "Các thiết bị không thể kết nối Internet" - "Tắt điểm phát sóng" - "Điểm phát sóng đang bật" - "Bạn có thể mất thêm phí dữ liệu khi chuyển vùng" - "Tiếp tục" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml deleted file mode 100644 index cee4682e7d8f..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "热点无法访问互联网" - "设备无法连接到互联网" - "关闭热点" - "热点已开启" - "漫游时可能会产生额外的费用" - "继续" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml deleted file mode 100644 index 05321db9f2bd..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "熱點沒有互聯網連線" - "裝置無法連線至互聯網" - "關閉熱點" - "已開啟熱點" - "漫遊時可能需要支付額外費用" - "繼續" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml deleted file mode 100644 index 57b9e0de3bf8..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "無線基地台沒有網際網路連線" - "裝置無法連上網際網路" - "關閉無線基地台" - "無線基地台已開啟" - "使用漫遊服務可能須支付額外費用" - "繼續" - diff --git a/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml deleted file mode 100644 index 7e899705af93..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "I-Hotspot ayina-inthanethi" - "Amadivayisi awakwazi ukuxhuma ku-inthanethi" - "Vala i-hotspot" - "I-Hotspot ivuliwe" - "Kungaba nezinkokhelo ezengeziwe uma uzula" - "Qhubeka" - diff --git a/packages/Tethering/res/values-mcc310-mnc004/config.xml b/packages/Tethering/res/values-mcc310-mnc004/config.xml deleted file mode 100644 index 5c5be0466a36..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004/config.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - 5000 - - - true - \ No newline at end of file diff --git a/packages/Tethering/res/values-mcc310-mnc004/strings.xml b/packages/Tethering/res/values-mcc310-mnc004/strings.xml deleted file mode 100644 index ce9ff6080717..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004/strings.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - Tethering has no internet - - Devices can\u2019t connect - - Turn off tethering - - - Hotspot or tethering is on - - Additional charges may apply while roaming - diff --git a/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml deleted file mode 100644 index 6fc432256a11..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Warmkol het nie internet nie" - "Toestelle kan nie aan internet koppel nie" - "Skakel warmkol af" - "Warmkol is aan" - "Bykomende heffings kan geld terwyl jy swerf" - "Gaan voort" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml deleted file mode 100644 index 749cb540227b..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "መገናኛ ነጥቡ በይነመረብ የለውም" - "መሣሪያዎች ከበይነመረብ ጋር መገናኘት አይችሉም" - "መገናኛ ነጥብ ያጥፉ" - "የመገናኛ ነጥብ በርቷል" - "በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ" - "ቀጥል" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml deleted file mode 100644 index 946002366361..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "متابعة" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml deleted file mode 100644 index e18e4ec67dd8..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "হটস্পটৰ কোনো ইণ্টাৰনেট নাই" - "ডিভাইচসমূহ ইণ্টাৰনেটৰ সৈতে সংযোগ কৰিব নোৱাৰি" - "হটস্পট অফ কৰক" - "হটস্পট অন হৈ আছে" - "ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে" - "অব্যাহত ৰাখক" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml deleted file mode 100644 index 77740cb6c6f9..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspotun internetə girişi yoxdur" - "Cihazlar internetə qoşula bilmir" - "Hotspot\'u deaktiv edin" - "Hotspot aktivdir" - "Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər" - "Davam edin" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml deleted file mode 100644 index 7170c06662d0..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nema pristup internetu" - "Uređaji ne mogu da se povežu na internet" - "Isključi hotspot" - "Hotspot je uključen" - "Možda važe dodatni troškovi u romingu" - "Nastavi" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml deleted file mode 100644 index 7388d218fd6d..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хот-спот не падключаны да інтэрнэту" - "Прылады не могуць падключацца да інтэрнэту" - "Выключыць хот-спот" - "Хот-спот уключаны" - "Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата" - "Працягнуць" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml deleted file mode 100644 index aa7a40bfa1d9..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точката за достъп няма връзка с интернет" - "Устройствата не могат да се свържат с интернет" - "Изключване на точката за достъп" - "Точката за достъп е включена" - "Възможно е да ви бъдат начислени допълнителни такси при роуминг" - "Напред" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml deleted file mode 100644 index 00bac782ed6b..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "চালিয়ে যান" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml deleted file mode 100644 index 907821260b8c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Pristupna tačka nema internet" - "Uređaji se ne mogu povezati na internet" - "Isključi pristupnu tačku" - "Pristupna tačka je uključena" - "Primjenjuju se dodatne tarife u romingu" - "Nastavi" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml deleted file mode 100644 index 43c9e137bbd2..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "El punt d\'accés Wi‑Fi no té accés a Internet" - "Els dispositius no es poden connectar a Internet" - "Desactiva el punt d\'accés Wi‑Fi" - "El punt d\'accés Wi‑Fi està activat" - "És possible que s\'apliquin costos addicionals en itinerància" - "Continua" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml deleted file mode 100644 index c9210f7f4028..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nemá připojení k internetu" - "Zařízení se nemohou připojit k internetu" - "Vypnout hotspot" - "Hotspot je aktivní" - "Při roamingu mohou být účtovány dodatečné poplatky" - "Pokračovat" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml deleted file mode 100644 index 3615ff49ffa6..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspottet har intet internet" - "Enheder kan ikke oprette forbindelse til internettet" - "Deaktiver hotspot" - "Hotspottet er aktiveret" - "Der opkræves muligvis yderligere gebyrer ved roaming" - "Fortsæt" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml deleted file mode 100644 index ee8809d80b1f..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot ist nicht mit dem Internet verbunden" - "Geräte können nicht mit dem Internet verbunden werden" - "Hotspot deaktivieren" - "Hotspot aktiviert" - "Für das Roaming können zusätzliche Gebühren anfallen" - "Weiter" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml deleted file mode 100644 index 3a79be87357d..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Το σημείο πρόσβασης Wi-Fi δεν έχει πρόσβαση στο διαδίκτυο." - "Δεν είναι η δυνατή η σύνδεση των συσκευών στο διαδίκτυο." - "Απενεργοποίηση σημείου πρόσβασης Wi-Fi" - "Σημείο πρόσβασης Wi-Fi ενεργό" - "Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή." - "Συνέχεια" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml deleted file mode 100644 index 4c7de1799808..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml deleted file mode 100644 index 4c7de1799808..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml deleted file mode 100644 index 4c7de1799808..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml deleted file mode 100644 index 4c7de1799808..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot has no Internet" - "Devices can’t connect to Internet" - "Turn off hotspot" - "Hotspot is on" - "Additional charges may apply while roaming" - "Continue" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml deleted file mode 100644 index 2daad6ad1c33..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‏‏‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎Hotspot has no internet‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎Devices can’t connect to internet‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‎‎‏‎‏‏‎‎Turn off hotspot‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‏‎Hotspot is on‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‏‏‎Additional charges may apply while roaming‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎Continue‎‏‎‎‏‎" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml deleted file mode 100644 index 68d5fc26de8e..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "El hotspot no tiene Internet" - "Los dispositivos no pueden conectarse a Internet" - "Desactiva el hotspot" - "El hotspot está activado" - "Es posible que apliquen cargos adicionales por roaming" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml deleted file mode 100644 index 930e0886424c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "El punto de acceso no tiene conexión a Internet" - "Los dispositivos no se pueden conectar a Internet" - "Desactivar punto de acceso" - "Zona Wi-Fi activada" - "Puede que se apliquen cargos adicionales en itinerancia" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml deleted file mode 100644 index e59e12e71dc8..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Kuumkohal puudub Interneti-ühendus" - "Seadmed ei saa Internetiga ühendust luua" - "Lülita kuumkoht välja" - "Kuumkoht on sees" - "Rändluse kasutamisega võivad kaasneda lisatasud" - "Jätka" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml deleted file mode 100644 index 4358266323a4..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Sare publikoak ez du Interneteko konexiorik" - "Gailuak ezin dira konektatu Internetera" - "Desaktibatu sare publikoa" - "Sare publikoa aktibatuta dago" - "Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritza moduan" - "Egin aurrera" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml deleted file mode 100644 index a2324d84f0d9..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "نقطه اتصال به اینترنت دسترسی ندارد" - "دستگاه‌ها به اینترنت متصل نشدند" - "نقطه اتصال را خاموش کنید" - "نقطه اتصال روشن است" - "ممکن است درحین فراگردی تغییرات دیگر اعمال شود" - "ادامه" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml deleted file mode 100644 index ec6ac929fbe7..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspotilla ei ole internetyhteyttä" - "Laitteet eivät voi yhdistää internetiin" - "Laita hotspot pois päältä" - "Hotspot on päällä" - "Roaming voi aiheuttaa lisämaksuja" - "Jatka" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml deleted file mode 100644 index eeaf8f3ad4f3..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Le point d\'accès n\'est pas connecté à Internet" - "Appareils non connectés à Internet" - "Désactiver le point d\'accès" - "Le point d\'accès est activé" - "En itinérance, des frais supplémentaires peuvent s\'appliquer" - "Continuer" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml deleted file mode 100644 index eeaf8f3ad4f3..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Le point d\'accès n\'est pas connecté à Internet" - "Appareils non connectés à Internet" - "Désactiver le point d\'accès" - "Le point d\'accès est activé" - "En itinérance, des frais supplémentaires peuvent s\'appliquer" - "Continuer" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml deleted file mode 100644 index 56b3a9b79de2..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "A zona wifi non ten acceso a Internet" - "Os dispositivos non se poden conectar a Internet" - "Desactivar zona wifi" - "A zona wifi está activada" - "Pódense aplicar cargos adicionais en itinerancia" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml deleted file mode 100644 index c2af9a668833..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "આગળ વધો" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml deleted file mode 100644 index 8bb5fb29ae11..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "हॉटस्पॉट से इंटरनेट नहीं चल रहा" - "डिवाइस इंटरनेट से कनेक्ट नहीं हो पा रहे" - "हॉटस्पॉट बंद करें" - "हॉटस्पॉट चालू है" - "रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है" - "जारी रखें" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml deleted file mode 100644 index 551b1b36fe41..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Žarišna točka nema pristup internetu" - "Uređaji se ne mogu povezati s internetom" - "Isključi žarišnu točku" - "Žarišna je točka uključena" - "U roamingu su mogući dodatni troškovi" - "Nastavi" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml deleted file mode 100644 index 4113195c1927..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "A hotspot nem csatlakozik az internethez" - "Az eszközök nem tudnak csatlakozni az internethez" - "Hotspot kikapcsolása" - "A hotspot be van kapcsolva" - "Roaming során további díjak léphetnek fel" - "Tovább" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml deleted file mode 100644 index 393eac056ce9..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Թեժ կետը միացված չէ ինտերնետին" - "Սարքերը չեն կարողանում միանալ ինտերնետին" - "Անջատել թեժ կետը" - "Թեժ կետը միացված է" - "Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել" - "Շարունակել" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml deleted file mode 100644 index e2538328cf1c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot tidak memiliki internet" - "Perangkat tidak dapat tersambung ke internet" - "Nonaktifkan hotspot" - "Hotspot aktif" - "Biaya tambahan mungkin berlaku saat roaming" - "Lanjutkan" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml deleted file mode 100644 index d28383f51beb..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Heitur reitur er ekki nettengdur" - "Tæki geta ekki tengst við internetið" - "Slökkva á heitum reit" - "Kveikt er á heitum reit" - "Viðbótargjöld kunna að eiga við í reiki" - "Halda áfram" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml deleted file mode 100644 index 388aa7a2c5df..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "L\'hotspot non ha accesso a Internet" - "I dispositivi non possono connettersi a Internet" - "Disattiva l\'hotspot" - "Hotspot attivo" - "Potrebbero essere addebitati costi aggiuntivi durante il roaming" - "Continua" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml deleted file mode 100644 index bbc379501c5b..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "לנקודה לשיתוף אינטרנט אין חיבור לאינטרנט" - "המכשירים לא יכולים להתחבר לאינטרנט" - "כיבוי הנקודה לשיתוף אינטרנט" - "הנקודה לשיתוף אינטרנט פועלת" - "ייתכנו חיובים נוספים בעת נדידה" - "המשך" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml deleted file mode 100644 index d7cb66b1dd80..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "アクセス ポイントがインターネットに接続されていません" - "デバイスをインターネットに接続できません" - "アクセス ポイントを OFF にする" - "アクセス ポイント: ON" - "ローミング時に追加料金が発生することがあります" - "続行" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml deleted file mode 100644 index 9651a563bd63..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "უსადენო ქსელს არ აქვს ინტერნეტზე წვდომა" - "მოწყობილობები ვერ უკავშირდება ინტერნეტს" - "გამორთეთ უსადენო ქსელი" - "უსადენო ქსელი ჩართულია" - "როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური" - "გაგრძელება" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml deleted file mode 100644 index f2db66b11e3d..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хотспотта интернет жоқ" - "Құрылғылар интернетке қосылмайды" - "Хотспотты өшіру" - "Хотспот қосулы" - "Роуминг кезінде қосымша ақы алынуы мүмкін." - "Жалғастыру" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml deleted file mode 100644 index 16699c5a0ac0..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ហតស្ប៉ត​មិនមាន​អ៊ីនធឺណិត​ទេ" - "ឧបករណ៍​មិនអាច​ភ្ជាប់​អ៊ីនធឺណិត​បានទេ" - "បិទ​ហតស្ប៉ត" - "ហតស្ប៉ត​ត្រូវបានបើក" - "អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង" - "បន្ត" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml deleted file mode 100644 index 3c75b1205b53..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ಮುಂದುವರಿಸಿ" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml deleted file mode 100644 index 3892bc36a96f..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "핫스팟이 인터넷에 연결되지 않음" - "기기를 인터넷에 연결할 수 없음" - "핫스팟 사용 중지" - "핫스팟 사용 중" - "로밍 중에는 추가 요금이 발생할 수 있습니다." - "계속" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml deleted file mode 100644 index 85e92e0a3c0a..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хотспоттун Интернети жок" - "Түзмөктөр Интернетке туташпай жатат" - "Туташуу түйүнүн өчүрүү" - "Кошулуу түйүнү күйүк" - "Роумингде кошумча акы алынышы мүмкүн" - "Улантуу" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml deleted file mode 100644 index 881e05c5e0d0..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ສືບຕໍ່" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml deleted file mode 100644 index 83aabd176fbe..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Nėra viešosios interneto prieigos taško interneto ryšio" - "Įrenginiams nepavyksta prisijungti prie interneto" - "Išjungti viešosios interneto prieigos tašką" - "Viešosios interneto prieigos taškas įjungtas" - "Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai" - "Tęsti" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml deleted file mode 100644 index 83feb11f8a47..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Tīklājam nav interneta savienojuma" - "Ierīces nevar izveidot savienojumu ar internetu" - "Izslēgt tīklāju" - "Tīklājs ir ieslēgts" - "Viesabonēšanas laikā var tikt piemērota papildu samaksa" - "Tālāk" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml deleted file mode 100644 index 040e2a5d8ee3..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точката на пристап нема интернет" - "Уредите не може да се поврзат на интернет" - "Исклучи ја точката на пристап" - "Точката на пристап е вклучена" - "При роаминг може да се наплатат дополнителни трошоци" - "Продолжи" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml deleted file mode 100644 index c9b12cd70645..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "തുടരുക" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml deleted file mode 100644 index e5a845051d1f..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Сүлжээний цэг дээр интернэт алга байна" - "Төхөөрөмжүүд нь интернэтэд холбогдох боломжгүй байна" - "Сүлжээний цэгийг унтраах" - "Сүлжээний цэг асаалттай байна" - "Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй" - "Үргэлжлүүлэх" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml deleted file mode 100644 index c7f1cc6c661c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "हॉटस्पॉटला इंटरनेट नाही" - "डिव्हाइस इंटरनेटला कनेक्ट करू शकत नाहीत" - "हॉटस्पॉट बंद करा" - "हॉटस्पॉट सुरू आहे" - "रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात" - "सुरू ठेवा" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml deleted file mode 100644 index 35d36f69f1fe..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Tempat liputan tiada Internet" - "Peranti tidak dapat menyambung kepada Internet" - "Matikan tempat liputan" - "Tempat liputan dihidupkan" - "Caj tambahan mungkin digunakan semasa perayauan" - "Teruskan" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml deleted file mode 100644 index bc374725ae54..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ဟော့စပေါ့တွင် အင်တာနက်မရှိပါ" - "စက်များက အင်တာနက်ချိတ်ဆက်၍ မရပါ" - "ဟော့စပေါ့ ပိတ်ရန်" - "ဟော့စပေါ့ ဖွင့်ထားသည်" - "ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်" - "ရှေ့ဆက်ရန်" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml deleted file mode 100644 index 413e165c0f92..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Wi-Fi-sonen har ikke internettilgang" - "Enheter kan ikke koble til internett" - "Slå av Wi-Fi-sonen" - "Wi-Fi-sonen er på" - "Ytterligere kostnader kan påløpe under roaming" - "Fortsett" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml deleted file mode 100644 index 9b2256abfc0c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "जारी राख्नुहोस्" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml deleted file mode 100644 index 7f7f39187f27..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot heeft geen internet" - "Apparaten kunnen geen verbinding maken met internet" - "Hotspot uitschakelen" - "Hotspot is ingeschakeld" - "Er kunnen extra kosten voor roaming in rekening worden gebracht." - "Doorgaan" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml deleted file mode 100644 index b478d1393bdd..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ଜାରି ରଖନ୍ତୁ" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml deleted file mode 100644 index e0940a518d91..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "ਜਾਰੀ ਰੱਖੋ" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml deleted file mode 100644 index c578b278d976..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nie ma internetu" - "Urządzenia nie mogą połączyć się z internetem" - "Wyłącz hotspot" - "Hotspot jest włączony" - "Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty" - "Dalej" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml deleted file mode 100644 index 502b5ddb7d6c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "O ponto de acesso não tem conexão com a Internet" - "Não foi possível conectar os dispositivos à Internet" - "Desativar ponto de acesso" - "O ponto de acesso está ativado" - "Pode haver cobranças extras durante o roaming" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml deleted file mode 100644 index a477516145e1..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "A zona Wi-Fi não tem Internet" - "Não é possível ligar os dispositivos à Internet" - "Desativar zona Wi-Fi" - "A zona Wi-Fi está ativada" - "Podem aplicar-se custos adicionais em roaming." - "Continuar" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml deleted file mode 100644 index 502b5ddb7d6c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "O ponto de acesso não tem conexão com a Internet" - "Não foi possível conectar os dispositivos à Internet" - "Desativar ponto de acesso" - "O ponto de acesso está ativado" - "Pode haver cobranças extras durante o roaming" - "Continuar" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml deleted file mode 100644 index d6808b04e611..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspotul nu are internet" - "Dispozitivele nu se pot conecta la internet" - "Dezactivați hotspotul" - "Hotspotul este activ" - "Se pot aplica taxe suplimentare pentru roaming" - "Continuați" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml deleted file mode 100644 index 22dcfcf42de5..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точка доступа не подключена к Интернету" - "Не удается подключить устройства к Интернету" - "Отключить точку доступа" - "Точка доступа включена" - "За использование услуг связи в роуминге может взиматься дополнительная плата." - "Продолжить" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml deleted file mode 100644 index 5008b7326c8a..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "හොට්ස්පොට් හට අන්තර්ජාලය නැත" - "උපාංගවලට අන්තර්ජාලයට සම්බන්ධ විය නොහැකිය" - "හොට්ස්පොට් ක්‍රියාවිරහිත කරන්න" - "හොට්ස්පොට් ක්‍රියාත්මකයි" - "රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය" - "ඉදිරියට යන්න" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml deleted file mode 100644 index 010677d1d6cf..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot nemá internetové pripojenie" - "Zariadenia sa nedajú pripojiť k internetu" - "Vypnúť hotspot" - "Hotspot je zapnutý" - "Počas roamingu vám môžu byť účtované ďalšie poplatky" - "Pokračovať" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml deleted file mode 100644 index 3662ca9e2a92..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Dostopna točka nima internetne povezave" - "Naprave ne morejo vzpostaviti internetne povezave" - "Izklopi dostopno točko" - "Dostopna točka je vklopljena" - "Med gostovanjem lahko nastanejo dodatni stroški" - "Naprej" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml deleted file mode 100644 index 5453d54fd977..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Zona e qasjes për internet nuk ka internet" - "Pajisjet nuk mund të lidhen me internetin" - "Çaktivizo zonën e qasjes për internet" - "Zona e qasjes për internet është aktive" - "Mund të zbatohen tarifime shtesë kur je në roaming" - "Vazhdo" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml deleted file mode 100644 index f52cbf387ee2..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Хотспот нема приступ интернету" - "Уређаји не могу да се повежу на интернет" - "Искључи хотспот" - "Хотспот је укључен" - "Можда важе додатни трошкови у ромингу" - "Настави" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml deleted file mode 100644 index 8474342f267c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Surfzonen har ingen internetanslutning" - "Enheterna har ingen internetanslutning" - "Inaktivera surfzon" - "Surfzonen är aktiverad" - "Ytterligare avgifter kan tillkomma vid roaming" - "Fortsätt" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml deleted file mode 100644 index 5a812e3f0cb3..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Mtandao pepe hauna intaneti" - "Vifaa vimeshindwa kuunganisha kwenye intaneti" - "Zima mtandao pepe" - "Mtandaopepe umewashwa" - "Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo" - "Endelea" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml deleted file mode 100644 index 315403135d15..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "தொடர்க" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml deleted file mode 100644 index 414def596341..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "కొనసాగించు" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml deleted file mode 100644 index a26ac0403b32..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ฮอตสปอตไม่ได้เชื่อมต่ออินเทอร์เน็ต" - "อุปกรณ์เชื่อมต่ออินเทอร์เน็ตไม่ได้" - "ปิดฮอตสปอต" - "ฮอตสปอตเปิดอยู่" - "อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง" - "ต่อไป" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml deleted file mode 100644 index 6e98146cd599..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Walang internet ang hotspot" - "Hindi makakonekta sa internet ang mga device" - "I-off ang hotspot" - "Naka-on ang hotspot" - "Posibleng magkaroon ng mga karagdagang singil habang nagro-roam" - "Ituloy" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml deleted file mode 100644 index 423bd7668923..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Hotspot\'un internet bağlantısı yok" - "Cihazlar internete bağlanamıyor" - "Hotspot\'u kapat" - "Hotspot açık" - "Dolaşım sırasında ek ücretler uygulanabilir" - "Devam" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml deleted file mode 100644 index 193b3c348b46..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Точка доступу не підключена до Інтернету" - "Не вдається підключити пристрої до Інтернету" - "Вимкнути точку доступу" - "Точку доступу ввімкнено" - "У роумінгу може стягуватися додаткова плата" - "Продовжити" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml deleted file mode 100644 index 3564ead36154..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "ہاٹ اسپاٹ میں انٹرنیٹ نہیں ہے" - "آلات انٹرنیٹ سے منسلک نہیں ہو سکتے" - "ہاٹ اسپاٹ آف کریں" - "ہاٹ اسپاٹ آن ہے" - "رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں" - "جاری رکھیں" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml deleted file mode 100644 index 769cc2c385ef..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - "Davom etish" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml deleted file mode 100644 index 998ebe4d7ba8..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "Điểm phát sóng không có kết nối Internet" - "Các thiết bị không thể kết nối Internet" - "Tắt điểm phát sóng" - "Điểm phát sóng đang bật" - "Bạn có thể mất thêm phí dữ liệu khi chuyển vùng" - "Tiếp tục" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml deleted file mode 100644 index 75786086b653..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "热点无法访问互联网" - "设备无法连接到互联网" - "关闭热点" - "热点已开启" - "漫游时可能会产生额外的费用" - "继续" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml deleted file mode 100644 index 7f7453c306d7..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "熱點沒有互聯網連線" - "裝置無法連線至互聯網" - "關閉熱點" - "已開啟熱點" - "漫遊時可能需要支付額外費用" - "繼續" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml deleted file mode 100644 index 4b4afc017e10..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "無線基地台沒有網際網路連線" - "裝置無法連上網際網路" - "關閉無線基地台" - "無線基地台已開啟" - "使用漫遊服務可能須支付額外費用" - "繼續" - diff --git a/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml deleted file mode 100644 index 48ac295ebcaa..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - "I-Hotspot ayina-inthanethi" - "Amadivayisi awakwazi ukuxhuma ku-inthanethi" - "Vala i-hotspot" - "I-Hotspot ivuliwe" - "Kungaba nezinkokhelo ezengeziwe uma uzula" - "Qhubeka" - diff --git a/packages/Tethering/res/values-mcc311-mnc480/config.xml b/packages/Tethering/res/values-mcc311-mnc480/config.xml deleted file mode 100644 index 5c5be0466a36..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480/config.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - 5000 - - - true - \ No newline at end of file diff --git a/packages/Tethering/res/values-mcc311-mnc480/strings.xml b/packages/Tethering/res/values-mcc311-mnc480/strings.xml deleted file mode 100644 index ce9ff6080717..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480/strings.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - Tethering has no internet - - Devices can\u2019t connect - - Turn off tethering - - - Hotspot or tethering is on - - Additional charges may apply while roaming - diff --git a/packages/Tethering/res/values-mk/strings.xml b/packages/Tethering/res/values-mk/strings.xml deleted file mode 100644 index 04f0fa8c718c..000000000000 --- a/packages/Tethering/res/values-mk/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Активно е врзување или точка на пристап" - "Допрете за поставување." - - "Врзувањето е оневозможено" - "Контактирајте со администраторот за детали" - "Статус на точката на пристап и врзувањето" - - - - - - - diff --git a/packages/Tethering/res/values-ml/strings.xml b/packages/Tethering/res/values-ml/strings.xml deleted file mode 100644 index 2d14f8e0f51b..000000000000 --- a/packages/Tethering/res/values-ml/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്‌പോട്ട് സജീവമാണ്" - "സജ്ജീകരിക്കാൻ ടാപ്പ് ചെയ്യുക." - - "ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു" - "വിശദാംശങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക" - "ഹോട്ട്‌സ്പോട്ടിന്റെയും ടെതറിംഗിന്റെയും നില" - - - - - - - diff --git a/packages/Tethering/res/values-mn/strings.xml b/packages/Tethering/res/values-mn/strings.xml deleted file mode 100644 index 4f3334c8e389..000000000000 --- a/packages/Tethering/res/values-mn/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Модем болгох эсвэл сүлжээний цэг идэвхтэй байна" - "Тохируулахын тулд товшино уу." - - "Модем болгохыг идэвхгүй болгосон" - "Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу" - "Сүлжээний цэг болон модем болгох төлөв" - - - - - - - diff --git a/packages/Tethering/res/values-mr/strings.xml b/packages/Tethering/res/values-mr/strings.xml deleted file mode 100644 index ba9f32498238..000000000000 --- a/packages/Tethering/res/values-mr/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "टेदरिंग किंवा हॉटस्पॉट अ‍ॅक्टिव्ह आहे" - "सेट करण्यासाठी टॅप करा." - - "टेदरिंग बंद केले आहे" - "तपशीलांसाठी तुमच्या ॲडमिनशी संपर्क साधा" - "हॉटस्पॉट आणि टेदरिंगची स्थिती" - - - - - - - diff --git a/packages/Tethering/res/values-ms/strings.xml b/packages/Tethering/res/values-ms/strings.xml deleted file mode 100644 index c343e311f4d2..000000000000 --- a/packages/Tethering/res/values-ms/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Penambatan atau tempat liputan aktif" - "Ketik untuk membuat persediaan." - - "Penambatan dilumpuhkan" - "Hubungi pentadbir anda untuk mendapatkan maklumat lanjut" - "Status tempat liputan & penambatan" - - - - - - - diff --git a/packages/Tethering/res/values-my/strings.xml b/packages/Tethering/res/values-my/strings.xml deleted file mode 100644 index 84bcdb4c07a6..000000000000 --- a/packages/Tethering/res/values-my/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်" - "စနစ်ထည့်သွင်းရန် တို့ပါ။" - - "မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းကို ပိတ်ထားသည်" - "အသေးစိတ်အတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ" - "ဟော့စပေါ့နှင့် မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း အခြေအနေ" - - - - - - - diff --git a/packages/Tethering/res/values-nb/strings.xml b/packages/Tethering/res/values-nb/strings.xml deleted file mode 100644 index 877c128c2d1d..000000000000 --- a/packages/Tethering/res/values-nb/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Internettdeling eller Wi-Fi-sone er aktiv" - "Trykk for å konfigurere." - - "Internettdeling er slått av" - "Ta kontakt med administratoren din for å få mer informasjon" - "Status for Wi-Fi-sone og internettdeling" - - - - - - - diff --git a/packages/Tethering/res/values-ne/strings.xml b/packages/Tethering/res/values-ne/strings.xml deleted file mode 100644 index d50fe240c44f..000000000000 --- a/packages/Tethering/res/values-ne/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "टेदरिङ वा हटस्पट सक्रिय छ" - "सेटअप गर्न ट्याप गर्नुहोस्।" - - "टेदरिङ सुविधा असक्षम पारिएको छ" - "विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्" - "हटस्पट तथा टेदरिङको स्थिति" - - - - - - - diff --git a/packages/Tethering/res/values-nl/strings.xml b/packages/Tethering/res/values-nl/strings.xml deleted file mode 100644 index 6950c239abbe..000000000000 --- a/packages/Tethering/res/values-nl/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tethering of hotspot actief" - "Tik om in te stellen." - - "Tethering is uitgeschakeld" - "Neem contact op met je beheerder voor meer informatie" - "Status van hotspot en tethering" - - - - - - - diff --git a/packages/Tethering/res/values-or/strings.xml b/packages/Tethering/res/values-or/strings.xml deleted file mode 100644 index 2500a6f66b31..000000000000 --- a/packages/Tethering/res/values-or/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "ଟିଥେରିଂ କିମ୍ୱା ହଟସ୍ପଟ୍ ସକ୍ରିୟ ଅଛି" - "ସେଟ୍ ଅପ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।" - - "ଟିଥେରିଂ ଅକ୍ଷମ କରାଯାଇଛି" - "ବିବରଣୀଗୁଡ଼ିକ ପାଇଁ ଆପଣଙ୍କ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ" - "ହଟସ୍ପଟ୍ ଓ ଟିଥେରିଂ ସ୍ଥିତି" - - - - - - - diff --git a/packages/Tethering/res/values-pa/strings.xml b/packages/Tethering/res/values-pa/strings.xml deleted file mode 100644 index 1fd496f968f6..000000000000 --- a/packages/Tethering/res/values-pa/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ" - "ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।" - - "ਟੈਦਰਿੰਗ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ" - "ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ" - "ਹੌਟਸਪੌਟ ਅਤੇ ਟੈਦਰਿੰਗ ਦੀ ਸਥਿਤੀ" - - - - - - - diff --git a/packages/Tethering/res/values-pl/strings.xml b/packages/Tethering/res/values-pl/strings.xml deleted file mode 100644 index df1d5aeffa71..000000000000 --- a/packages/Tethering/res/values-pl/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Aktywny tethering lub punkt dostępu" - "Kliknij, by skonfigurować" - - "Tethering został wyłączony" - "Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem" - "Hotspot i tethering – stan" - - - - - - - diff --git a/packages/Tethering/res/values-pt-rBR/strings.xml b/packages/Tethering/res/values-pt-rBR/strings.xml deleted file mode 100644 index 2c3757d6decb..000000000000 --- a/packages/Tethering/res/values-pt-rBR/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Ponto de acesso ou tethering ativo" - "Toque para configurar." - - "Tethering desativado" - "Fale com seu administrador para saber detalhes" - "Status de ponto de acesso e tethering" - - - - - - - diff --git a/packages/Tethering/res/values-pt-rPT/strings.xml b/packages/Tethering/res/values-pt-rPT/strings.xml deleted file mode 100644 index 5af2d22a575d..000000000000 --- a/packages/Tethering/res/values-pt-rPT/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Ligação (à Internet) via telemóvel ou zona Wi-Fi ativas" - "Toque para configurar." - - "A ligação (à Internet) via telemóvel está desativada." - "Contacte o administrador para obter detalhes." - "Estado da zona Wi-Fi e da ligação (à Internet) via telemóvel" - - - - - - - diff --git a/packages/Tethering/res/values-pt/strings.xml b/packages/Tethering/res/values-pt/strings.xml deleted file mode 100644 index 2c3757d6decb..000000000000 --- a/packages/Tethering/res/values-pt/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Ponto de acesso ou tethering ativo" - "Toque para configurar." - - "Tethering desativado" - "Fale com seu administrador para saber detalhes" - "Status de ponto de acesso e tethering" - - - - - - - diff --git a/packages/Tethering/res/values-ro/strings.xml b/packages/Tethering/res/values-ro/strings.xml deleted file mode 100644 index 1dad542d4f89..000000000000 --- a/packages/Tethering/res/values-ro/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tethering sau hotspot activ" - "Atingeți ca să configurați." - - "Tetheringul este dezactivat" - "Contactați administratorul pentru detalii" - "Starea hotspotului și a tetheringului" - - - - - - - diff --git a/packages/Tethering/res/values-ru/strings.xml b/packages/Tethering/res/values-ru/strings.xml deleted file mode 100644 index 4d31484229d0..000000000000 --- a/packages/Tethering/res/values-ru/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Включен режим модема или точка доступа" - "Нажмите, чтобы настроить." - - "Использование телефона в качестве модема запрещено" - "Чтобы узнать подробности, обратитесь к администратору." - "Статус хот-спота и режима модема" - - - - - - - diff --git a/packages/Tethering/res/values-si/strings.xml b/packages/Tethering/res/values-si/strings.xml deleted file mode 100644 index d21f2b538805..000000000000 --- a/packages/Tethering/res/values-si/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "ටෙදරින් හෝ හොට්ස්පොට් සක්‍රීයයි" - "පිහිටුවීමට තට්ටු කරන්න." - - "ටෙදරින් අබල කර ඇත" - "විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න" - "හොට්ස්පොට් & ටෙදරින් තත්ත්වය" - - - - - - - diff --git a/packages/Tethering/res/values-sk/strings.xml b/packages/Tethering/res/values-sk/strings.xml deleted file mode 100644 index f2242b93b34a..000000000000 --- a/packages/Tethering/res/values-sk/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tethering alebo prístupový bod je aktívny" - "Klepnutím prejdete na nastavenie." - - "Tethering je deaktivovaný" - "O podrobnosti požiadajte svojho správcu" - "Stav hotspotu a tetheringu" - - - - - - - diff --git a/packages/Tethering/res/values-sl/strings.xml b/packages/Tethering/res/values-sl/strings.xml deleted file mode 100644 index c01cace36028..000000000000 --- a/packages/Tethering/res/values-sl/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Povezava z internetom prek mobilnega telefona ali dostopna točka je aktivna" - "Dotaknite se, če želite nastaviti." - - "Povezava z internetom prek mobilnega telefona je onemogočena" - "Za podrobnosti se obrnite na skrbnika" - "Stanje dostopne točke in povezave z internetom prek mobilnega telefona" - - - - - - - diff --git a/packages/Tethering/res/values-sq/strings.xml b/packages/Tethering/res/values-sq/strings.xml deleted file mode 100644 index f7e2a7be74f4..000000000000 --- a/packages/Tethering/res/values-sq/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Ndarja e internetit ose zona e qasjes së internetit është aktive" - "Trokit për ta konfiguruar." - - "Ndarja e internetit është çaktivizuar" - "Kontakto me administratorin për detaje" - "Statusi i zonës së qasjes dhe ndarjes së internetit" - - - - - - - diff --git a/packages/Tethering/res/values-sr/strings.xml b/packages/Tethering/res/values-sr/strings.xml deleted file mode 100644 index c5f84eb45d1c..000000000000 --- a/packages/Tethering/res/values-sr/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Привезивање или хотспот је активан" - "Додирните да бисте подесили." - - "Привезивање је онемогућено" - "Потражите детаље од администратора" - "Статус хотспота и привезивања" - - - - - - - diff --git a/packages/Tethering/res/values-sv/strings.xml b/packages/Tethering/res/values-sv/strings.xml deleted file mode 100644 index d745dad2ff43..000000000000 --- a/packages/Tethering/res/values-sv/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Internetdelning eller surfzon har aktiverats" - "Tryck om du vill konfigurera." - - "Internetdelning har inaktiverats" - "Kontakta administratören om du vill veta mer" - "Trådlös surfzon och internetdelning har inaktiverats" - - - - - - - diff --git a/packages/Tethering/res/values-sw/strings.xml b/packages/Tethering/res/values-sw/strings.xml deleted file mode 100644 index beaa3063740c..000000000000 --- a/packages/Tethering/res/values-sw/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Kusambaza mtandao au mtandaopepe umewashwa" - "Gusa ili uweke mipangilio." - - "Umezima kipengele cha kusambaza mtandao" - "Wasiliana na msimamizi wako ili upate maelezo zaidi" - "Mtandaopepe na hali ya kusambaza mtandao" - - - - - - - diff --git a/packages/Tethering/res/values-ta/strings.xml b/packages/Tethering/res/values-ta/strings.xml deleted file mode 100644 index bc8957e0963a..000000000000 --- a/packages/Tethering/res/values-ta/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "டெதெரிங் அல்லது ஹாட்ஸ்பாட் இயங்குகிறது" - "அமைக்க, தட்டவும்." - - "டெதெரிங் முடக்கப்பட்டுள்ளது" - "விவரங்களுக்கு உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்" - "ஹாட்ஸ்பாட் & டெதெரிங் நிலை" - - - - - - - diff --git a/packages/Tethering/res/values-te/strings.xml b/packages/Tethering/res/values-te/strings.xml deleted file mode 100644 index b7afdb4cad04..000000000000 --- a/packages/Tethering/res/values-te/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "టెథరింగ్ లేదా హాట్‌స్పాట్ యాక్టివ్‌గా ఉంది" - "సెటప్ చేయడానికి ట్యాప్ చేయండి." - - "టెథరింగ్ డిజేబుల్ చేయబడింది" - "వివరాల కోసం మీ అడ్మిన్‌ని సంప్రదించండి" - "హాట్‌స్పాట్ & టెథరింగ్ స్థితి" - - - - - - - diff --git a/packages/Tethering/res/values-th/strings.xml b/packages/Tethering/res/values-th/strings.xml deleted file mode 100644 index e60d43496ec5..000000000000 --- a/packages/Tethering/res/values-th/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือหรือฮอตสปอตทำงานอยู่" - "แตะเพื่อตั้งค่า" - - "ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว" - "ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด" - "สถานะฮอตสปอตและการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ" - - - - - - - diff --git a/packages/Tethering/res/values-tl/strings.xml b/packages/Tethering/res/values-tl/strings.xml deleted file mode 100644 index 79523bb0f409..000000000000 --- a/packages/Tethering/res/values-tl/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Aktibo ang pag-tether o hotspot" - "I-tap para i-set up." - - "Naka-disable ang pag-tether" - "Makipag-ugnayan sa iyong admin para sa mga detalye" - "Status ng hotspot at pag-tether" - - - - - - - diff --git a/packages/Tethering/res/values-tr/strings.xml b/packages/Tethering/res/values-tr/strings.xml deleted file mode 100644 index cf100a42e27f..000000000000 --- a/packages/Tethering/res/values-tr/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tethering veya hotspot etkin" - "Ayarlamak için dokunun." - - "Tethering devre dışı bırakıldı" - "Ayrıntılı bilgi için yöneticinize başvurun" - "Hotspot ve tethering durumu" - - - - - - - diff --git a/packages/Tethering/res/values-uk/strings.xml b/packages/Tethering/res/values-uk/strings.xml deleted file mode 100644 index 0a8ceddad7c6..000000000000 --- a/packages/Tethering/res/values-uk/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Модем чи точка доступу активні" - "Натисніть, щоб налаштувати." - - "Використання телефона як модема вимкнено" - "Щоб дізнатися більше, зв\'яжіться з адміністратором" - "Статус точки доступу та модема" - - - - - - - diff --git a/packages/Tethering/res/values-ur/strings.xml b/packages/Tethering/res/values-ur/strings.xml deleted file mode 100644 index 043dba316146..000000000000 --- a/packages/Tethering/res/values-ur/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "ٹیدرنگ یا ہاٹ اسپاٹ فعال" - "سیٹ اپ کرنے کیلئے تھپتھپائیں۔" - - "ٹیدرنگ غیر فعال ہے" - "تفصیلات کے لئے اپنے منتظم سے رابطہ کریں" - "ہاٹ اسپاٹ اور ٹیتھرنگ کا اسٹیٹس" - - - - - - - diff --git a/packages/Tethering/res/values-uz/strings.xml b/packages/Tethering/res/values-uz/strings.xml deleted file mode 100644 index 5b9d62afd9d8..000000000000 --- a/packages/Tethering/res/values-uz/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Modem rejimi yoki hotspot yoniq" - "Sozlash uchun bosing." - - "Modem rejimi faolsizlantirildi" - "Tafsilotlari uchun administratoringizga murojaat qiling" - "Hotspot va modem rejimi holati" - - - - - - - diff --git a/packages/Tethering/res/values-vi/strings.xml b/packages/Tethering/res/values-vi/strings.xml deleted file mode 100644 index 19240700eebd..000000000000 --- a/packages/Tethering/res/values-vi/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Tính năng chia sẻ kết nối hoặc điểm phát sóng đang hoạt động" - "Hãy nhấn để thiết lập." - - "Đã tắt tính năng chia sẻ kết nối" - "Hãy liên hệ với quản trị viên của bạn để biết chi tiết" - "Trạng thái điểm phát sóng và trạng thái chia sẻ kết nối" - - - - - - - diff --git a/packages/Tethering/res/values-zh-rCN/strings.xml b/packages/Tethering/res/values-zh-rCN/strings.xml deleted file mode 100644 index d137df5f3331..000000000000 --- a/packages/Tethering/res/values-zh-rCN/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "网络共享或热点已启用" - "点按即可设置。" - - "网络共享已停用" - "如需了解详情,请与您的管理员联系" - "热点和网络共享状态" - - - - - - - diff --git a/packages/Tethering/res/values-zh-rHK/strings.xml b/packages/Tethering/res/values-zh-rHK/strings.xml deleted file mode 100644 index 12c071091b8c..000000000000 --- a/packages/Tethering/res/values-zh-rHK/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "網絡共享或熱點已啟用" - "輕按即可設定。" - - "網絡共享已停用" - "請聯絡您的管理員以瞭解詳情" - "熱點和網絡共享狀態" - - - - - - - diff --git a/packages/Tethering/res/values-zh-rTW/strings.xml b/packages/Tethering/res/values-zh-rTW/strings.xml deleted file mode 100644 index 24fb76e824af..000000000000 --- a/packages/Tethering/res/values-zh-rTW/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "數據連線或無線基地台已啟用" - "輕觸即可進行設定。" - - "數據連線已停用" - "詳情請洽你的管理員" - "無線基地台與數據連線狀態" - - - - - - - diff --git a/packages/Tethering/res/values-zu/strings.xml b/packages/Tethering/res/values-zu/strings.xml deleted file mode 100644 index f4859aa195a4..000000000000 --- a/packages/Tethering/res/values-zu/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - "Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe" - "Thepha ukuze usethe." - - "Ukusebenzisa ifoni njengemodemu kukhutshaziwe" - "Xhumana nomphathi wakho ukuze uthole imininingwane" - "I-Hotspot nesimo sokusebenzisa ifoni njengemodemu" - - - - - - - diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml deleted file mode 100644 index 5f8d2997197f..000000000000 --- a/packages/Tethering/res/values/config.xml +++ /dev/null @@ -1,194 +0,0 @@ - - - - - - - "usb\\d" - "rndis\\d" - - - - - - - - - "wlan\\d" - "softap\\d" - - - - - "wigig\\d" - - - - - "p2p-p2p\\d-.*" - "p2p\\d" - - - - - "bt-pan" - - - - true - - - false - - - false - - - - - - - 5000 - - - - - - - true - - - - - - - - - - - - - - - - 24 - - - com.android.settings/.wifi.tether.TetherService - - - - -1 - - - - false - diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml deleted file mode 100644 index 0ee7a992ee20..000000000000 --- a/packages/Tethering/res/values/overlayable.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml deleted file mode 100644 index d63c7c5063cc..000000000000 --- a/packages/Tethering/res/values/strings.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - Tethering or hotspot active - - Tap to set up. - - - - Tethering is disabled - - Contact your admin for details - - - - Hotspot & tethering status - - - - - - - - - - - - - diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java deleted file mode 100644 index 9fda1257b4c9..000000000000 --- a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 android.net.dhcp; - -/** - * Convenience wrapper around IDhcpServerCallbacks.Stub that implements getInterfaceVersion(). - * @hide - */ -public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub { - /** - * Get the version of the aidl interface implemented by the callbacks. - */ - @Override - public int getInterfaceVersion() { - return IDhcpServerCallbacks.VERSION; - } - - @Override - public String getInterfaceHash() { - return IDhcpServerCallbacks.HASH; - } -} diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java deleted file mode 100644 index aaaec17bf922..000000000000 --- a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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 android.net.dhcp; - -import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH; - -import android.net.LinkAddress; -import android.util.ArraySet; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.net.Inet4Address; -import java.util.Collection; -import java.util.Collections; -import java.util.Set; - -/** - * Subclass of {@link DhcpServingParamsParcel} with additional utility methods for building. - * - *

This utility class does not check for validity of the parameters: invalid parameters are - * reported by the receiving module when unparceling the parcel. - * - * @see DhcpServingParams - * @hide - */ -public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { - public static final int MTU_UNSET = 0; - - /** - * Set the server address and served prefix for the DHCP server. - * - *

This parameter is required. - */ - public DhcpServingParamsParcelExt setServerAddr(@NonNull LinkAddress serverAddr) { - this.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress()); - this.serverAddrPrefixLength = serverAddr.getPrefixLength(); - return this; - } - - /** - * Set the default routers to be advertised to DHCP clients. - * - *

Each router must be inside the served prefix. This may be an empty set, but it must - * always be set explicitly. - */ - public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Set defaultRouters) { - this.defaultRouters = toIntArray(defaultRouters); - return this; - } - - /** - * Set the default routers to be advertised to DHCP clients. - * - *

Each router must be inside the served prefix. This may be an empty list of routers, - * but it must always be set explicitly. - */ - public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) { - return setDefaultRouters(newArraySet(defaultRouters)); - } - - /** - * Convenience method to build the parameters with no default router. - * - *

Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address. - */ - public DhcpServingParamsParcelExt setNoDefaultRouter() { - return setDefaultRouters(); - } - - /** - * Set the DNS servers to be advertised to DHCP clients. - * - *

This may be an empty set, but it must always be set explicitly. - */ - public DhcpServingParamsParcelExt setDnsServers(@NonNull Set dnsServers) { - this.dnsServers = toIntArray(dnsServers); - return this; - } - - /** - * Set the DNS servers to be advertised to DHCP clients. - * - *

This may be an empty list of servers, but it must always be set explicitly. - */ - public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) { - return setDnsServers(newArraySet(dnsServers)); - } - - /** - * Convenience method to build the parameters with no DNS server. - * - *

Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address. - */ - public DhcpServingParamsParcelExt setNoDnsServer() { - return setDnsServers(); - } - - /** - * Set excluded addresses that the DHCP server is not allowed to assign to clients. - * - *

This parameter is optional. DNS servers and default routers are always excluded - * and do not need to be set here. - */ - public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Set excludedAddrs) { - this.excludedAddrs = toIntArray(excludedAddrs); - return this; - } - - /** - * Set excluded addresses that the DHCP server is not allowed to assign to clients. - * - *

This parameter is optional. DNS servers and default routers are always excluded - * and do not need to be set here. - */ - public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) { - return setExcludedAddrs(newArraySet(excludedAddrs)); - } - - /** - * Set the lease time for leases assigned by the DHCP server. - * - *

This parameter is required. - */ - public DhcpServingParamsParcelExt setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) { - this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs; - return this; - } - - /** - * Set the link MTU to be advertised to DHCP clients. - * - *

If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter - * is optional and defaults to {@link #MTU_UNSET}. - */ - public DhcpServingParamsParcelExt setLinkMtu(int linkMtu) { - this.linkMtu = linkMtu; - return this; - } - - /** - * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option. - * - *

If not set, the default value is false. - */ - public DhcpServingParamsParcelExt setMetered(boolean metered) { - this.metered = metered; - return this; - } - - /** - * Set the client address to tell DHCP server only offer this address. - * The client's prefix length is the same as server's. - * - *

If not set, the default value is null. - */ - public DhcpServingParamsParcelExt setSingleClientAddr(@Nullable Inet4Address clientAddr) { - this.singleClientAddr = clientAddr == null ? 0 : inet4AddressToIntHTH(clientAddr); - return this; - } - - /** - * Set whether the DHCP server should request a new prefix from IpServer when receiving - * DHCPDECLINE message in certain particular link (e.g. there is only one downstream USB - * tethering client). If it's false, process DHCPDECLINE message as RFC2131#4.3.3 suggests. - * - *

If not set, the default value is false. - */ - public DhcpServingParamsParcelExt setChangePrefixOnDecline(boolean changePrefixOnDecline) { - this.changePrefixOnDecline = changePrefixOnDecline; - return this; - } - - private static int[] toIntArray(@NonNull Collection addrs) { - int[] res = new int[addrs.size()]; - int i = 0; - for (Inet4Address addr : addrs) { - res[i] = inet4AddressToIntHTH(addr); - i++; - } - return res; - } - - private static ArraySet newArraySet(Inet4Address... addrs) { - ArraySet addrSet = new ArraySet<>(addrs.length); - Collections.addAll(addrSet, addrs); - return addrSet; - } -} diff --git a/packages/Tethering/src/android/net/ip/DadProxy.java b/packages/Tethering/src/android/net/ip/DadProxy.java deleted file mode 100644 index e2976b78908c..000000000000 --- a/packages/Tethering/src/android/net/ip/DadProxy.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import android.net.util.InterfaceParams; -import android.os.Handler; - -import androidx.annotation.VisibleForTesting; - -/** - * Basic Duplicate address detection proxy. - * - * @hide - */ -public class DadProxy { - private static final String TAG = DadProxy.class.getSimpleName(); - - @VisibleForTesting - public static NeighborPacketForwarder naForwarder; - public static NeighborPacketForwarder nsForwarder; - - public DadProxy(Handler h, InterfaceParams tetheredIface) { - naForwarder = new NeighborPacketForwarder(h, tetheredIface, - NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); - nsForwarder = new NeighborPacketForwarder(h, tetheredIface, - NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); - } - - /** Stop NS/NA Forwarders. */ - public void stop() { - naForwarder.stop(); - nsForwarder.stop(); - } - - /** Set upstream iface on both forwarders. */ - public void setUpstreamIface(InterfaceParams upstreamIface) { - naForwarder.setUpstreamIface(upstreamIface); - nsForwarder.setUpstreamIface(upstreamIface); - } -} diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java deleted file mode 100644 index 52d59fcdc19b..000000000000 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ /dev/null @@ -1,1422 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration; -import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; -import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; -import static android.net.util.NetworkConstants.asByte; -import static android.net.util.PrefixUtils.asIpPrefix; -import static android.net.util.TetheringMessageBase.BASE_IPSERVER; -import static android.system.OsConstants.RT_SCOPE_UNIVERSE; - -import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; - -import android.net.INetd; -import android.net.INetworkStackStatusCallback; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.MacAddress; -import android.net.RouteInfo; -import android.net.TetheredClient; -import android.net.TetheringManager; -import android.net.TetheringRequestParcel; -import android.net.dhcp.DhcpLeaseParcelable; -import android.net.dhcp.DhcpServerCallbacks; -import android.net.dhcp.DhcpServingParamsParcel; -import android.net.dhcp.DhcpServingParamsParcelExt; -import android.net.dhcp.IDhcpEventCallbacks; -import android.net.dhcp.IDhcpServer; -import android.net.ip.IpNeighborMonitor.NeighborEvent; -import android.net.ip.RouterAdvertisementDaemon.RaParams; -import android.net.shared.NetdUtils; -import android.net.shared.RouteUtils; -import android.net.util.InterfaceParams; -import android.net.util.InterfaceSet; -import android.net.util.PrefixUtils; -import android.net.util.SharedLog; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceSpecificException; -import android.util.Log; -import android.util.SparseArray; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.util.MessageUtils; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; -import com.android.networkstack.tethering.BpfCoordinator; -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; -import com.android.networkstack.tethering.PrivateAddressCoordinator; - -import java.io.IOException; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.NetworkInterface; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Random; -import java.util.Set; - -/** - * Provides the interface to IP-layer serving functionality for a given network - * interface, e.g. for tethering or "local-only hotspot" mode. - * - * @hide - */ -public class IpServer extends StateMachine { - public static final int STATE_UNAVAILABLE = 0; - public static final int STATE_AVAILABLE = 1; - public static final int STATE_TETHERED = 2; - public static final int STATE_LOCAL_ONLY = 3; - - /** Get string name of |state|.*/ - public static String getStateString(int state) { - switch (state) { - case STATE_UNAVAILABLE: return "UNAVAILABLE"; - case STATE_AVAILABLE: return "AVAILABLE"; - case STATE_TETHERED: return "TETHERED"; - case STATE_LOCAL_ONLY: return "LOCAL_ONLY"; - } - return "UNKNOWN: " + state; - } - - private static final byte DOUG_ADAMS = (byte) 42; - - // TODO: have PanService use some visible version of this constant - private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1/24"; - - // TODO: have this configurable - private static final int DHCP_LEASE_TIME_SECS = 3600; - - private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString("00:00:00:00:00:00"); - - private static final String TAG = "IpServer"; - private static final boolean DBG = false; - private static final boolean VDBG = false; - private static final Class[] sMessageClasses = { - IpServer.class - }; - private static final SparseArray sMagicDecoderRing = - MessageUtils.findMessageNames(sMessageClasses); - - /** IpServer callback. */ - public static class Callback { - /** - * Notify that |who| has changed its tethering state. - * - * @param who the calling instance of IpServer - * @param state one of STATE_* - * @param lastError one of TetheringManager.TETHER_ERROR_* - */ - public void updateInterfaceState(IpServer who, int state, int lastError) { } - - /** - * Notify that |who| has new LinkProperties. - * - * @param who the calling instance of IpServer - * @param newLp the new LinkProperties to report - */ - public void updateLinkProperties(IpServer who, LinkProperties newLp) { } - - /** - * Notify that the DHCP leases changed in one of the IpServers. - */ - public void dhcpLeasesChanged() { } - - /** - * Request Tethering change. - * - * @param tetheringType the downstream type of this IpServer. - * @param enabled enable or disable tethering. - */ - public void requestEnableTethering(int tetheringType, boolean enabled) { } - } - - /** Capture IpServer dependencies, for injection. */ - public abstract static class Dependencies { - /** - * Create a DadProxy instance to be used by IpServer. - * To support multiple tethered interfaces concurrently DAD Proxy - * needs to be supported per IpServer instead of per upstream. - */ - public DadProxy getDadProxy(Handler handler, InterfaceParams ifParams) { - return new DadProxy(handler, ifParams); - } - - /** Create an IpNeighborMonitor to be used by this IpServer */ - public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log, - IpNeighborMonitor.NeighborEventConsumer consumer) { - return new IpNeighborMonitor(handler, log, consumer); - } - - /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/ - public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { - return new RouterAdvertisementDaemon(ifParams); - } - - /** Get |ifName|'s interface information.*/ - public InterfaceParams getInterfaceParams(String ifName) { - return InterfaceParams.getByName(ifName); - } - - /** Get |ifName|'s interface index. */ - public int getIfindex(String ifName) { - try { - return NetworkInterface.getByName(ifName).getIndex(); - } catch (IOException | NullPointerException e) { - Log.e(TAG, "Can't determine interface index for interface " + ifName); - return 0; - } - } - - /** Create a DhcpServer instance to be used by IpServer. */ - public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params, - DhcpServerCallbacks cb); - } - - // request from the user that it wants to tether - public static final int CMD_TETHER_REQUESTED = BASE_IPSERVER + 1; - // request from the user that it wants to untether - public static final int CMD_TETHER_UNREQUESTED = BASE_IPSERVER + 2; - // notification that this interface is down - public static final int CMD_INTERFACE_DOWN = BASE_IPSERVER + 3; - // notification from the {@link Tethering.TetherMainSM} that it had trouble enabling IP - // Forwarding - public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IPSERVER + 4; - // notification from the {@link Tethering.TetherMainSM} SM that it had trouble disabling IP - // Forwarding - public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IPSERVER + 5; - // notification from the {@link Tethering.TetherMainSM} SM that it had trouble starting - // tethering - public static final int CMD_START_TETHERING_ERROR = BASE_IPSERVER + 6; - // notification from the {@link Tethering.TetherMainSM} that it had trouble stopping tethering - public static final int CMD_STOP_TETHERING_ERROR = BASE_IPSERVER + 7; - // notification from the {@link Tethering.TetherMainSM} that it had trouble setting the DNS - // forwarders - public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IPSERVER + 8; - // the upstream connection has changed - public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9; - // new IPv6 tethering parameters need to be processed - public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10; - // new neighbor cache entry on our interface - public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11; - // request from DHCP server that it wants to have a new prefix - public static final int CMD_NEW_PREFIX_REQUEST = BASE_IPSERVER + 12; - // request from PrivateAddressCoordinator to restart tethering. - public static final int CMD_NOTIFY_PREFIX_CONFLICT = BASE_IPSERVER + 13; - - private final State mInitialState; - private final State mLocalHotspotState; - private final State mTetheredState; - private final State mUnavailableState; - private final State mWaitingForRestartState; - - private final SharedLog mLog; - private final INetd mNetd; - @NonNull - private final BpfCoordinator mBpfCoordinator; - private final Callback mCallback; - private final InterfaceController mInterfaceCtrl; - private final PrivateAddressCoordinator mPrivateAddressCoordinator; - - private final String mIfaceName; - private final int mInterfaceType; - private final LinkProperties mLinkProperties; - private final boolean mUsingLegacyDhcp; - private final boolean mUsingBpfOffload; - - private final Dependencies mDeps; - - private int mLastError; - private int mServingMode; - private InterfaceSet mUpstreamIfaceSet; // may change over time - private InterfaceParams mInterfaceParams; - // TODO: De-duplicate this with mLinkProperties above. Currently, these link - // properties are those selected by the IPv6TetheringCoordinator and relayed - // to us. By comparison, mLinkProperties contains the addresses and directly - // connected routes that have been formed from these properties iff. we have - // succeeded in configuring them and are able to announce them within Router - // Advertisements (otherwise, we do not add them to mLinkProperties at all). - private LinkProperties mLastIPv6LinkProperties; - private RouterAdvertisementDaemon mRaDaemon; - private DadProxy mDadProxy; - - // To be accessed only on the handler thread - private int mDhcpServerStartIndex = 0; - private IDhcpServer mDhcpServer; - private RaParams mLastRaParams; - - private LinkAddress mStaticIpv4ServerAddr; - private LinkAddress mStaticIpv4ClientAddr; - - @NonNull - private List mDhcpLeases = Collections.emptyList(); - - private int mLastIPv6UpstreamIfindex = 0; - - private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer { - public void accept(NeighborEvent e) { - sendMessage(CMD_NEIGHBOR_EVENT, e); - } - } - - private final IpNeighborMonitor mIpNeighborMonitor; - - private LinkAddress mIpv4Address; - - // TODO: Add a dependency object to pass the data members or variables from the tethering - // object. It helps to reduce the arguments of the constructor. - public IpServer( - String ifaceName, Looper looper, int interfaceType, SharedLog log, - INetd netd, @NonNull BpfCoordinator coordinator, Callback callback, - boolean usingLegacyDhcp, boolean usingBpfOffload, - PrivateAddressCoordinator addressCoordinator, Dependencies deps) { - super(ifaceName, looper); - mLog = log.forSubComponent(ifaceName); - mNetd = netd; - mBpfCoordinator = coordinator; - mCallback = callback; - mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog); - mIfaceName = ifaceName; - mInterfaceType = interfaceType; - mLinkProperties = new LinkProperties(); - mUsingLegacyDhcp = usingLegacyDhcp; - mUsingBpfOffload = usingBpfOffload; - mPrivateAddressCoordinator = addressCoordinator; - mDeps = deps; - resetLinkProperties(); - mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; - mServingMode = STATE_AVAILABLE; - - mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog, - new MyNeighborEventConsumer()); - - // IP neighbor monitor monitors the neighbor events for adding/removing offload - // forwarding rules per client. If BPF offload is not supported, don't start listening - // for neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule, - // removeIpv6ForwardingRule. - if (mUsingBpfOffload && !mIpNeighborMonitor.start()) { - mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName); - } - - mInitialState = new InitialState(); - mLocalHotspotState = new LocalHotspotState(); - mTetheredState = new TetheredState(); - mUnavailableState = new UnavailableState(); - mWaitingForRestartState = new WaitingForRestartState(); - addState(mInitialState); - addState(mLocalHotspotState); - addState(mTetheredState); - addState(mWaitingForRestartState, mTetheredState); - addState(mUnavailableState); - - setInitialState(mInitialState); - } - - /** Interface name which IpServer served.*/ - public String interfaceName() { - return mIfaceName; - } - - /** - * Tethering downstream type. It would be one of TetheringManager#TETHERING_*. - */ - public int interfaceType() { - return mInterfaceType; - } - - /** Last error from this IpServer. */ - public int lastError() { - return mLastError; - } - - /** Serving mode is the current state of IpServer state machine. */ - public int servingMode() { - return mServingMode; - } - - /** The properties of the network link which IpServer is serving. */ - public LinkProperties linkProperties() { - return new LinkProperties(mLinkProperties); - } - - /** The address which IpServer is using. */ - public LinkAddress getAddress() { - return mIpv4Address; - } - - /** - * Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper - * thread. - */ - public List getAllLeases() { - return Collections.unmodifiableList(mDhcpLeases); - } - - /** Stop this IpServer. After this is called this IpServer should not be used any more. */ - public void stop() { - sendMessage(CMD_INTERFACE_DOWN); - } - - /** - * Tethering is canceled. IpServer state machine will be available and wait for - * next tethering request. - */ - public void unwanted() { - sendMessage(CMD_TETHER_UNREQUESTED); - } - - /** Internals. */ - - private boolean startIPv4() { - return configureIPv4(true); - } - - /** - * Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer - * handler. - * - *

Different instances of this class can be created for each call to IDhcpServer methods, - * with different implementations of the callback, to differentiate handling of success/error in - * each call. - */ - private abstract class OnHandlerStatusCallback extends INetworkStackStatusCallback.Stub { - @Override - public void onStatusAvailable(int statusCode) { - getHandler().post(() -> callback(statusCode)); - } - - public abstract void callback(int statusCode); - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - - @Override - public String getInterfaceHash() { - return this.HASH; - } - } - - private class DhcpServerCallbacksImpl extends DhcpServerCallbacks { - private final int mStartIndex; - - private DhcpServerCallbacksImpl(int startIndex) { - mStartIndex = startIndex; - } - - @Override - public void onDhcpServerCreated(int statusCode, IDhcpServer server) throws RemoteException { - getHandler().post(() -> { - // We are on the handler thread: mDhcpServerStartIndex can be read safely. - if (mStartIndex != mDhcpServerStartIndex) { - // This start request is obsolete. Explicitly stop the DHCP server to shut - // down its thread. When the |server| binder token goes out of scope, the - // garbage collector will finalize it, which causes the network stack process - // garbage collector to collect the server itself. - try { - server.stop(null); - } catch (RemoteException e) { } - return; - } - - if (statusCode != STATUS_SUCCESS) { - mLog.e("Error obtaining DHCP server: " + statusCode); - handleError(); - return; - } - - mDhcpServer = server; - try { - mDhcpServer.startWithCallbacks(new OnHandlerStatusCallback() { - @Override - public void callback(int startStatusCode) { - if (startStatusCode != STATUS_SUCCESS) { - mLog.e("Error starting DHCP server: " + startStatusCode); - handleError(); - } - } - }, new DhcpEventCallback()); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - }); - } - - private void handleError() { - mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR; - transitionTo(mInitialState); - } - } - - private class DhcpEventCallback extends IDhcpEventCallbacks.Stub { - @Override - public void onLeasesChanged(List leaseParcelables) { - final ArrayList leases = new ArrayList<>(); - for (DhcpLeaseParcelable lease : leaseParcelables) { - final LinkAddress address = new LinkAddress( - intToInet4AddressHTH(lease.netAddr), lease.prefixLength, - 0 /* flags */, RT_SCOPE_UNIVERSE /* as per RFC6724#3.2 */, - lease.expTime /* deprecationTime */, lease.expTime /* expirationTime */); - - final MacAddress macAddress; - try { - macAddress = MacAddress.fromBytes(lease.hwAddr); - } catch (IllegalArgumentException e) { - Log.wtf(TAG, "Invalid address received from DhcpServer: " - + Arrays.toString(lease.hwAddr)); - return; - } - - final TetheredClient.AddressInfo addressInfo = new TetheredClient.AddressInfo( - address, lease.hostname); - leases.add(new TetheredClient( - macAddress, - Collections.singletonList(addressInfo), - mInterfaceType)); - } - - getHandler().post(() -> { - mDhcpLeases = leases; - mCallback.dhcpLeasesChanged(); - }); - } - - @Override - public void onNewPrefixRequest(@NonNull final IpPrefix currentPrefix) { - Objects.requireNonNull(currentPrefix); - sendMessage(CMD_NEW_PREFIX_REQUEST, currentPrefix); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - - @Override - public String getInterfaceHash() throws RemoteException { - return this.HASH; - } - } - - private RouteInfo getDirectConnectedRoute(@NonNull final LinkAddress ipv4Address) { - Objects.requireNonNull(ipv4Address); - return new RouteInfo(PrefixUtils.asIpPrefix(ipv4Address), null, mIfaceName, RTN_UNICAST); - } - - private DhcpServingParamsParcel makeServingParams(@NonNull final Inet4Address defaultRouter, - @NonNull final Inet4Address dnsServer, @NonNull LinkAddress serverAddr, - @Nullable Inet4Address clientAddr) { - final boolean changePrefixOnDecline = - (mInterfaceType == TetheringManager.TETHERING_NCM && clientAddr == null); - return new DhcpServingParamsParcelExt() - .setDefaultRouters(defaultRouter) - .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS) - .setDnsServers(dnsServer) - .setServerAddr(serverAddr) - .setMetered(true) - .setSingleClientAddr(clientAddr) - .setChangePrefixOnDecline(changePrefixOnDecline); - // TODO: also advertise link MTU - } - - private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) { - if (mUsingLegacyDhcp) { - return true; - } - - final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress(); - final Inet4Address clientAddr = clientLinkAddr == null ? null : - (Inet4Address) clientLinkAddr.getAddress(); - - final DhcpServingParamsParcel params = makeServingParams(addr /* defaultRouter */, - addr /* dnsServer */, serverLinkAddr, clientAddr); - mDhcpServerStartIndex++; - mDeps.makeDhcpServer( - mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex)); - return true; - } - - private void stopDhcp() { - // Make all previous start requests obsolete so servers are not started later - mDhcpServerStartIndex++; - - if (mDhcpServer != null) { - try { - mDhcpServer.stop(new OnHandlerStatusCallback() { - @Override - public void callback(int statusCode) { - if (statusCode != STATUS_SUCCESS) { - mLog.e("Error stopping DHCP server: " + statusCode); - mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR; - // Not much more we can do here - } - mDhcpLeases.clear(); - getHandler().post(mCallback::dhcpLeasesChanged); - } - }); - mDhcpServer = null; - } catch (RemoteException e) { - mLog.e("Error stopping DHCP server", e); - // Not much more we can do here - } - } - } - - private boolean configureDhcp(boolean enable, final LinkAddress serverAddr, - final LinkAddress clientAddr) { - if (enable) { - return startDhcp(serverAddr, clientAddr); - } else { - stopDhcp(); - return true; - } - } - - private void stopIPv4() { - configureIPv4(false); - // NOTE: All of configureIPv4() will be refactored out of existence - // into calls to InterfaceController, shared with startIPv4(). - mInterfaceCtrl.clearIPv4Address(); - mPrivateAddressCoordinator.releaseDownstream(this); - mIpv4Address = null; - mStaticIpv4ServerAddr = null; - mStaticIpv4ClientAddr = null; - } - - private boolean configureIPv4(boolean enabled) { - if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")"); - - if (enabled) { - mIpv4Address = requestIpv4Address(true /* useLastAddress */); - } - - if (mIpv4Address == null) { - mLog.e("No available ipv4 address"); - return false; - } - - if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) { - // BT configures the interface elsewhere: only start DHCP. - // TODO: make all tethering types behave the same way, and delete the bluetooth - // code that calls into NetworkManagementService directly. - return configureDhcp(enabled, mIpv4Address, null /* clientAddress */); - } - - final IpPrefix ipv4Prefix = asIpPrefix(mIpv4Address); - - final Boolean setIfaceUp; - if (mInterfaceType == TetheringManager.TETHERING_WIFI - || mInterfaceType == TetheringManager.TETHERING_WIFI_P2P - || mInterfaceType == TetheringManager.TETHERING_ETHERNET - || mInterfaceType == TetheringManager.TETHERING_WIGIG) { - // The WiFi and Ethernet stack has ownership of the interface up/down state. - // It is unclear whether the Bluetooth or USB stacks will manage their own - // state. - setIfaceUp = null; - } else { - setIfaceUp = enabled; - } - if (!mInterfaceCtrl.setInterfaceConfiguration(mIpv4Address, setIfaceUp)) { - mLog.e("Error configuring interface"); - if (!enabled) stopDhcp(); - return false; - } - - if (enabled) { - mLinkProperties.addLinkAddress(mIpv4Address); - mLinkProperties.addRoute(getDirectConnectedRoute(mIpv4Address)); - } else { - mLinkProperties.removeLinkAddress(mIpv4Address); - mLinkProperties.removeRoute(getDirectConnectedRoute(mIpv4Address)); - } - return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr); - } - - private LinkAddress requestIpv4Address(final boolean useLastAddress) { - if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr; - - if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) { - return new LinkAddress(BLUETOOTH_IFACE_ADDR); - } - - return mPrivateAddressCoordinator.requestDownstreamAddress(this, useLastAddress); - } - - private boolean startIPv6() { - mInterfaceParams = mDeps.getInterfaceParams(mIfaceName); - if (mInterfaceParams == null) { - mLog.e("Failed to find InterfaceParams"); - stopIPv6(); - return false; - } - - mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams); - if (!mRaDaemon.start()) { - stopIPv6(); - return false; - } - - // TODO: use ShimUtils instead of explicitly checking the version here. - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R || "S".equals(Build.VERSION.CODENAME) - || "T".equals(Build.VERSION.CODENAME)) { - // DAD Proxy starts forwarding packets after IPv6 upstream is present. - mDadProxy = mDeps.getDadProxy(getHandler(), mInterfaceParams); - } - - return true; - } - - private void stopIPv6() { - mInterfaceParams = null; - setRaParams(null); - - if (mRaDaemon != null) { - mRaDaemon.stop(); - mRaDaemon = null; - } - - if (mDadProxy != null) { - mDadProxy.stop(); - mDadProxy = null; - } - } - - // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only - // LinkProperties. These have extraneous data filtered out and only the - // necessary prefixes included (per its prefix distribution policy). - // - // TODO: Evaluate using a data structure than is more directly suited to - // communicating only the relevant information. - private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) { - if (mRaDaemon == null) return; - - // Avoid unnecessary work on spurious updates. - if (Objects.equals(mLastIPv6LinkProperties, v6only)) { - return; - } - - RaParams params = null; - String upstreamIface = null; - InterfaceParams upstreamIfaceParams = null; - int upstreamIfIndex = 0; - - if (v6only != null) { - upstreamIface = v6only.getInterfaceName(); - upstreamIfaceParams = mDeps.getInterfaceParams(upstreamIface); - if (upstreamIfaceParams != null) { - upstreamIfIndex = upstreamIfaceParams.index; - } - params = new RaParams(); - params.mtu = v6only.getMtu(); - params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); - - if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment); - - for (LinkAddress linkAddr : v6only.getLinkAddresses()) { - if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue; - - final IpPrefix prefix = new IpPrefix( - linkAddr.getAddress(), linkAddr.getPrefixLength()); - params.prefixes.add(prefix); - - final Inet6Address dnsServer = getLocalDnsIpFor(prefix); - if (dnsServer != null) { - params.dnses.add(dnsServer); - } - } - - // Add upstream index to name mapping for the tether stats usage in the coordinator. - // Although this mapping could be added by both class Tethering and IpServer, adding - // mapping from IpServer guarantees that the mapping is added before the adding - // forwarding rules. That is because there are different state machines in both - // classes. It is hard to guarantee the link property update order between multiple - // state machines. - mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfIndex, upstreamIface); - } - - // If v6only is null, we pass in null to setRaParams(), which handles - // deprecation of any existing RA data. - - setRaParams(params); - mLastIPv6LinkProperties = v6only; - - updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, null); - mLastIPv6UpstreamIfindex = upstreamIfIndex; - if (mDadProxy != null) { - mDadProxy.setUpstreamIface(upstreamIfaceParams); - } - } - - private void removeRoutesFromLocalNetwork(@NonNull final List toBeRemoved) { - final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork( - mNetd, toBeRemoved); - if (removalFailures > 0) { - mLog.e(String.format("Failed to remove %d IPv6 routes from local table.", - removalFailures)); - } - - for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route); - } - - private void addRoutesToLocalNetwork(@NonNull final List toBeAdded) { - try { - // It's safe to call networkAddInterface() even if - // the interface is already in the local_network. - mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName); - try { - // Add routes from local network. Note that adding routes that - // already exist does not cause an error (EEXIST is silently ignored). - RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded); - } catch (IllegalStateException e) { - mLog.e("Failed to add IPv4/v6 routes to local table: " + e); - return; - } - } catch (ServiceSpecificException | RemoteException e) { - mLog.e("Failed to add " + mIfaceName + " to local table: ", e); - return; - } - - for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route); - } - - private void configureLocalIPv6Routes( - HashSet deprecatedPrefixes, HashSet newPrefixes) { - // [1] Remove the routes that are deprecated. - if (!deprecatedPrefixes.isEmpty()) { - removeRoutesFromLocalNetwork(getLocalRoutesFor(mIfaceName, deprecatedPrefixes)); - } - - // [2] Add only the routes that have not previously been added. - if (newPrefixes != null && !newPrefixes.isEmpty()) { - HashSet addedPrefixes = (HashSet) newPrefixes.clone(); - if (mLastRaParams != null) { - addedPrefixes.removeAll(mLastRaParams.prefixes); - } - - if (!addedPrefixes.isEmpty()) { - addRoutesToLocalNetwork(getLocalRoutesFor(mIfaceName, addedPrefixes)); - } - } - } - - private void configureLocalIPv6Dns( - HashSet deprecatedDnses, HashSet newDnses) { - // TODO: Is this really necessary? Can we not fail earlier if INetd cannot be located? - if (mNetd == null) { - if (newDnses != null) newDnses.clear(); - mLog.e("No netd service instance available; not setting local IPv6 addresses"); - return; - } - - // [1] Remove deprecated local DNS IP addresses. - if (!deprecatedDnses.isEmpty()) { - for (Inet6Address dns : deprecatedDnses) { - if (!mInterfaceCtrl.removeAddress(dns, RFC7421_PREFIX_LENGTH)) { - mLog.e("Failed to remove local dns IP " + dns); - } - - mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH)); - } - } - - // [2] Add only the local DNS IP addresses that have not previously been added. - if (newDnses != null && !newDnses.isEmpty()) { - final HashSet addedDnses = (HashSet) newDnses.clone(); - if (mLastRaParams != null) { - addedDnses.removeAll(mLastRaParams.dnses); - } - - for (Inet6Address dns : addedDnses) { - if (!mInterfaceCtrl.addAddress(dns, RFC7421_PREFIX_LENGTH)) { - mLog.e("Failed to add local dns IP " + dns); - newDnses.remove(dns); - } - - mLinkProperties.addLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH)); - } - } - - try { - mNetd.tetherApplyDnsInterfaces(); - } catch (ServiceSpecificException | RemoteException e) { - mLog.e("Failed to update local DNS caching server"); - if (newDnses != null) newDnses.clear(); - } - } - - private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) { - // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF - // offload is disabled. Add this check just in case. - // TODO: Perhaps remove this protection check. - if (!mUsingBpfOffload) return; - - mBpfCoordinator.tetherOffloadRuleAdd(this, rule); - } - - private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule) { - // TODO: Perhaps remove this protection check. - // See the related comment in #addIpv6ForwardingRule. - if (!mUsingBpfOffload) return; - - mBpfCoordinator.tetherOffloadRuleRemove(this, rule); - } - - private void clearIpv6ForwardingRules() { - if (!mUsingBpfOffload) return; - - mBpfCoordinator.tetherOffloadRuleClear(this); - } - - private void updateIpv6ForwardingRule(int newIfindex) { - // TODO: Perhaps remove this protection check. - // See the related comment in #addIpv6ForwardingRule. - if (!mUsingBpfOffload) return; - - mBpfCoordinator.tetherOffloadRuleUpdate(this, newIfindex); - } - - // Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream - // changes or if a neighbor event is received. - private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex, - NeighborEvent e) { - // If we no longer have an upstream, clear forwarding rules and do nothing else. - if (upstreamIfindex == 0) { - clearIpv6ForwardingRules(); - return; - } - - // If the upstream interface has changed, remove all rules and re-add them with the new - // upstream interface. - if (prevUpstreamIfindex != upstreamIfindex) { - updateIpv6ForwardingRule(upstreamIfindex); - } - - // If we're here to process a NeighborEvent, do so now. - // mInterfaceParams must be non-null or the event would not have arrived. - if (e == null) return; - if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress() - || e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) { - return; - } - - // When deleting rules, we still need to pass a non-null MAC, even though it's ignored. - // Do this here instead of in the Ipv6ForwardingRule constructor to ensure that we never - // add rules with a null MAC, only delete them. - MacAddress dstMac = e.isValid() ? e.macAddr : NULL_MAC_ADDRESS; - Ipv6ForwardingRule rule = new Ipv6ForwardingRule(upstreamIfindex, - mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr, dstMac); - if (e.isValid()) { - addIpv6ForwardingRule(rule); - } else { - removeIpv6ForwardingRule(rule); - } - } - - private void handleNeighborEvent(NeighborEvent e) { - if (mInterfaceParams != null - && mInterfaceParams.index == e.ifindex - && mInterfaceParams.hasMacAddress) { - updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e); - } - } - - private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) { - if (!currentPrefix.contains(mIpv4Address.getAddress()) - || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) { - Log.e(TAG, "Invalid prefix: " + currentPrefix); - return; - } - - final LinkAddress deprecatedLinkAddress = mIpv4Address; - mIpv4Address = requestIpv4Address(false); - if (mIpv4Address == null) { - mLog.e("Fail to request a new downstream prefix"); - return; - } - final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress(); - - // Add new IPv4 address on the interface. - if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) { - mLog.e("Failed to add new IP " + srvAddr); - return; - } - - // Remove deprecated routes from local network. - removeRoutesFromLocalNetwork( - Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress))); - mLinkProperties.removeLinkAddress(deprecatedLinkAddress); - - // Add new routes to local network. - addRoutesToLocalNetwork( - Collections.singletonList(getDirectConnectedRoute(mIpv4Address))); - mLinkProperties.addLinkAddress(mIpv4Address); - - // Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't - // listen on the interface configured with new IPv4 address, that results DNS validation - // failure of downstream client even if appropriate routes have been configured. - try { - mNetd.tetherApplyDnsInterfaces(); - } catch (ServiceSpecificException | RemoteException e) { - mLog.e("Failed to update local DNS caching server"); - return; - } - sendLinkProperties(); - - // Notify DHCP server that new prefix/route has been applied on IpServer. - final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null : - (Inet4Address) mStaticIpv4ClientAddr.getAddress(); - final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */, - srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr); - try { - mDhcpServer.updateParams(params, new OnHandlerStatusCallback() { - @Override - public void callback(int statusCode) { - if (statusCode != STATUS_SUCCESS) { - mLog.e("Error updating DHCP serving params: " + statusCode); - } - } - }); - } catch (RemoteException e) { - mLog.e("Error updating DHCP serving params", e); - } - } - - private byte getHopLimit(String upstreamIface, int adjustTTL) { - try { - int upstreamHopLimit = Integer.parseUnsignedInt( - mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit")); - upstreamHopLimit = upstreamHopLimit + adjustTTL; - // Cap the hop limit to 255. - return (byte) Integer.min(upstreamHopLimit, 255); - } catch (Exception e) { - mLog.e("Failed to find upstream interface hop limit", e); - } - return RaParams.DEFAULT_HOPLIMIT; - } - - private void setRaParams(RaParams newParams) { - if (mRaDaemon != null) { - final RaParams deprecatedParams = - RaParams.getDeprecatedRaParams(mLastRaParams, newParams); - - configureLocalIPv6Routes(deprecatedParams.prefixes, - (newParams != null) ? newParams.prefixes : null); - - configureLocalIPv6Dns(deprecatedParams.dnses, - (newParams != null) ? newParams.dnses : null); - - mRaDaemon.buildNewRa(deprecatedParams, newParams); - } - - mLastRaParams = newParams; - } - - private void logMessage(State state, int what) { - mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what))); - } - - private void sendInterfaceState(int newInterfaceState) { - mServingMode = newInterfaceState; - mCallback.updateInterfaceState(this, newInterfaceState, mLastError); - sendLinkProperties(); - } - - private void sendLinkProperties() { - mCallback.updateLinkProperties(this, new LinkProperties(mLinkProperties)); - } - - private void resetLinkProperties() { - mLinkProperties.clear(); - mLinkProperties.setInterfaceName(mIfaceName); - } - - private void maybeConfigureStaticIp(final TetheringRequestParcel request) { - // Ignore static address configuration if they are invalid or null. In theory, static - // addresses should not be invalid here because TetheringManager do not allow caller to - // specify invalid static address configuration. - if (request == null || request.localIPv4Address == null - || request.staticClientAddress == null || !checkStaticAddressConfiguration( - request.localIPv4Address, request.staticClientAddress)) { - return; - } - - mStaticIpv4ServerAddr = request.localIPv4Address; - mStaticIpv4ClientAddr = request.staticClientAddress; - } - - class InitialState extends State { - @Override - public void enter() { - sendInterfaceState(STATE_AVAILABLE); - } - - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_REQUESTED: - mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; - switch (message.arg1) { - case STATE_LOCAL_ONLY: - maybeConfigureStaticIp((TetheringRequestParcel) message.obj); - transitionTo(mLocalHotspotState); - break; - case STATE_TETHERED: - maybeConfigureStaticIp((TetheringRequestParcel) message.obj); - transitionTo(mTetheredState); - break; - default: - mLog.e("Invalid tethering interface serving state specified."); - } - break; - case CMD_INTERFACE_DOWN: - transitionTo(mUnavailableState); - break; - case CMD_IPV6_TETHER_UPDATE: - updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - class BaseServingState extends State { - @Override - public void enter() { - if (!startIPv4()) { - mLastError = TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR; - return; - } - - try { - NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address)); - } catch (RemoteException | ServiceSpecificException | IllegalStateException e) { - mLog.e("Error Tethering", e); - mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; - return; - } - - if (!startIPv6()) { - mLog.e("Failed to startIPv6"); - // TODO: Make this a fatal error once Bluetooth IPv6 is sorted. - return; - } - } - - @Override - public void exit() { - // Note that at this point, we're leaving the tethered state. We can fail any - // of these operations, but it doesn't really change that we have to try them - // all in sequence. - stopIPv6(); - - try { - NetdUtils.untetherInterface(mNetd, mIfaceName); - } catch (RemoteException | ServiceSpecificException e) { - mLastError = TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; - mLog.e("Failed to untether interface: " + e); - } - - stopIPv4(); - - resetLinkProperties(); - } - - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_UNREQUESTED: - transitionTo(mInitialState); - if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName); - break; - case CMD_INTERFACE_DOWN: - transitionTo(mUnavailableState); - if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName); - break; - case CMD_IPV6_TETHER_UPDATE: - updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1); - sendLinkProperties(); - break; - case CMD_IP_FORWARDING_ENABLE_ERROR: - case CMD_IP_FORWARDING_DISABLE_ERROR: - case CMD_START_TETHERING_ERROR: - case CMD_STOP_TETHERING_ERROR: - case CMD_SET_DNS_FORWARDERS_ERROR: - mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR; - transitionTo(mInitialState); - break; - case CMD_NEW_PREFIX_REQUEST: - handleNewPrefixRequest((IpPrefix) message.obj); - break; - case CMD_NOTIFY_PREFIX_CONFLICT: - mLog.i("restart tethering: " + mInterfaceType); - mCallback.requestEnableTethering(mInterfaceType, false /* enabled */); - transitionTo(mWaitingForRestartState); - break; - default: - return false; - } - return true; - } - } - - // Handling errors in BaseServingState.enter() by transitioning is - // problematic because transitioning during a multi-state jump yields - // a Log.wtf(). Ultimately, there should be only one ServingState, - // and forwarding and NAT rules should be handled by a coordinating - // functional element outside of IpServer. - class LocalHotspotState extends BaseServingState { - @Override - public void enter() { - super.enter(); - if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) { - transitionTo(mInitialState); - } - - if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName); - sendInterfaceState(STATE_LOCAL_ONLY); - } - - @Override - public boolean processMessage(Message message) { - if (super.processMessage(message)) return true; - - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_REQUESTED: - mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode."); - break; - case CMD_TETHER_CONNECTION_CHANGED: - // Ignored in local hotspot state. - break; - default: - return false; - } - return true; - } - } - - // Handling errors in BaseServingState.enter() by transitioning is - // problematic because transitioning during a multi-state jump yields - // a Log.wtf(). Ultimately, there should be only one ServingState, - // and forwarding and NAT rules should be handled by a coordinating - // functional element outside of IpServer. - class TetheredState extends BaseServingState { - @Override - public void enter() { - super.enter(); - if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) { - transitionTo(mInitialState); - } - - if (DBG) Log.d(TAG, "Tethered " + mIfaceName); - sendInterfaceState(STATE_TETHERED); - } - - @Override - public void exit() { - cleanupUpstream(); - super.exit(); - } - - private void cleanupUpstream() { - if (mUpstreamIfaceSet == null) return; - - for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname); - mUpstreamIfaceSet = null; - clearIpv6ForwardingRules(); - } - - private void cleanupUpstreamInterface(String upstreamIface) { - // Note that we don't care about errors here. - // Sometimes interfaces are gone before we get - // to remove their rules, which generates errors. - // Just do the best we can. - try { - mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString()); - } - try { - mNetd.tetherRemoveForward(mIfaceName, upstreamIface); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Exception in disableNat: " + e.toString()); - } - } - - @Override - public boolean processMessage(Message message) { - if (super.processMessage(message)) return true; - - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_REQUESTED: - mLog.e("CMD_TETHER_REQUESTED while already tethering."); - break; - case CMD_TETHER_CONNECTION_CHANGED: - final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj; - if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) { - if (VDBG) Log.d(TAG, "Connection changed noop - dropping"); - break; - } - - if (newUpstreamIfaceSet == null) { - cleanupUpstream(); - break; - } - - for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) { - cleanupUpstreamInterface(removed); - } - - final Set added = upstreamInterfacesAdd(newUpstreamIfaceSet); - // This makes the call to cleanupUpstream() in the error - // path for any interface neatly cleanup all the interfaces. - mUpstreamIfaceSet = newUpstreamIfaceSet; - - for (String ifname : added) { - try { - mNetd.tetherAddForward(mIfaceName, ifname); - mNetd.ipfwdAddInterfaceForward(mIfaceName, ifname); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Exception enabling NAT: " + e.toString()); - cleanupUpstream(); - mLastError = TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR; - transitionTo(mInitialState); - return true; - } - } - break; - case CMD_NEIGHBOR_EVENT: - handleNeighborEvent((NeighborEvent) message.obj); - break; - default: - return false; - } - return true; - } - - private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) { - if (mUpstreamIfaceSet == null && newIfaces == null) return true; - if (mUpstreamIfaceSet != null && newIfaces != null) { - return mUpstreamIfaceSet.equals(newIfaces); - } - return false; - } - - private Set upstreamInterfacesRemoved(InterfaceSet newIfaces) { - if (mUpstreamIfaceSet == null) return new HashSet<>(); - - final HashSet removed = new HashSet<>(mUpstreamIfaceSet.ifnames); - removed.removeAll(newIfaces.ifnames); - return removed; - } - - private Set upstreamInterfacesAdd(InterfaceSet newIfaces) { - final HashSet added = new HashSet<>(newIfaces.ifnames); - if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames); - return added; - } - } - - /** - * This state is terminal for the per interface state machine. At this - * point, the tethering main state machine should have removed this interface - * specific state machine from its list of possible recipients of - * tethering requests. The state machine itself will hang around until - * the garbage collector finds it. - */ - class UnavailableState extends State { - @Override - public void enter() { - mIpNeighborMonitor.stop(); - mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; - sendInterfaceState(STATE_UNAVAILABLE); - } - } - - class WaitingForRestartState extends State { - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_UNREQUESTED: - transitionTo(mInitialState); - mLog.i("Untethered (unrequested) and restarting " + mIfaceName); - mCallback.requestEnableTethering(mInterfaceType, true /* enabled */); - break; - case CMD_INTERFACE_DOWN: - transitionTo(mUnavailableState); - mLog.i("Untethered (interface down) and restarting" + mIfaceName); - mCallback.requestEnableTethering(mInterfaceType, true /* enabled */); - break; - default: - return false; - } - return true; - } - } - - // Accumulate routes representing "prefixes to be assigned to the local - // interface", for subsequent modification of local_network routing. - private static ArrayList getLocalRoutesFor( - String ifname, HashSet prefixes) { - final ArrayList localRoutes = new ArrayList(); - for (IpPrefix ipp : prefixes) { - localRoutes.add(new RouteInfo(ipp, null, ifname, RTN_UNICAST)); - } - return localRoutes; - } - - // Given a prefix like 2001:db8::/64 return an address like 2001:db8::1. - private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) { - final byte[] dnsBytes = localPrefix.getRawAddress(); - dnsBytes[dnsBytes.length - 1] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1)); - try { - return Inet6Address.getByAddress(null, dnsBytes, 0); - } catch (UnknownHostException e) { - Log.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix); - return null; - } - } - - private static byte getRandomSanitizedByte(byte dflt, byte... excluded) { - final byte random = (byte) (new Random()).nextInt(); - for (int value : excluded) { - if (random == value) return dflt; - } - return random; - } -} diff --git a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java deleted file mode 100644 index 73fc833fabf5..000000000000 --- a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import static android.system.OsConstants.AF_INET6; -import static android.system.OsConstants.AF_PACKET; -import static android.system.OsConstants.ETH_P_IPV6; -import static android.system.OsConstants.IPPROTO_RAW; -import static android.system.OsConstants.SOCK_DGRAM; -import static android.system.OsConstants.SOCK_NONBLOCK; -import static android.system.OsConstants.SOCK_RAW; - -import android.net.util.InterfaceParams; -import android.net.util.PacketReader; -import android.net.util.SocketUtils; -import android.net.util.TetheringUtils; -import android.os.Handler; -import android.system.ErrnoException; -import android.system.Os; -import android.util.Log; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.Inet6Address; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.Arrays; - -/** - * Basic IPv6 Neighbor Advertisement Forwarder. - * - * Forward NA packets from upstream iface to tethered iface - * and NS packets from tethered iface to upstream iface. - * - * @hide - */ -public class NeighborPacketForwarder extends PacketReader { - private final String mTag; - - private FileDescriptor mFd; - - // TODO: get these from NetworkStackConstants. - private static final int IPV6_ADDR_LEN = 16; - private static final int IPV6_DST_ADDR_OFFSET = 24; - private static final int IPV6_HEADER_LEN = 40; - private static final int ETH_HEADER_LEN = 14; - - private InterfaceParams mListenIfaceParams, mSendIfaceParams; - - private final int mType; - public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136; - public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135; - - public NeighborPacketForwarder(Handler h, InterfaceParams tetheredInterface, int type) { - super(h); - mTag = NeighborPacketForwarder.class.getSimpleName() + "-" - + tetheredInterface.name + "-" + type; - mType = type; - - if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { - mSendIfaceParams = tetheredInterface; - } else { - mListenIfaceParams = tetheredInterface; - } - } - - /** Set new upstream iface and start/stop based on new params. */ - public void setUpstreamIface(InterfaceParams upstreamParams) { - final InterfaceParams oldUpstreamParams; - - if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { - oldUpstreamParams = mListenIfaceParams; - mListenIfaceParams = upstreamParams; - } else { - oldUpstreamParams = mSendIfaceParams; - mSendIfaceParams = upstreamParams; - } - - if (oldUpstreamParams == null && upstreamParams != null) { - start(); - } else if (oldUpstreamParams != null && upstreamParams == null) { - stop(); - } else if (oldUpstreamParams != null && upstreamParams != null - && oldUpstreamParams.index != upstreamParams.index) { - stop(); - start(); - } - } - - // TODO: move NetworkStackUtils.closeSocketQuietly to - // frameworks/libs/net/common/device/com/android/net/module/util/[someclass]. - private void closeSocketQuietly(FileDescriptor fd) { - try { - SocketUtils.closeSocket(fd); - } catch (IOException ignored) { - } - } - - @Override - protected FileDescriptor createFd() { - try { - // ICMPv6 packets from modem do not have eth header, so RAW socket cannot be used. - // To keep uniformity in both directions PACKET socket can be used. - mFd = Os.socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0); - - // TODO: convert setup*Socket to setupICMPv6BpfFilter with filter type? - if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { - TetheringUtils.setupNaSocket(mFd); - } else if (mType == ICMPV6_NEIGHBOR_SOLICITATION) { - TetheringUtils.setupNsSocket(mFd); - } - - SocketAddress bindAddress = SocketUtils.makePacketSocketAddress( - ETH_P_IPV6, mListenIfaceParams.index); - Os.bind(mFd, bindAddress); - } catch (ErrnoException | SocketException e) { - Log.wtf(mTag, "Failed to create socket", e); - closeSocketQuietly(mFd); - return null; - } - - return mFd; - } - - private Inet6Address getIpv6DestinationAddress(byte[] recvbuf) { - Inet6Address dstAddr; - try { - dstAddr = (Inet6Address) Inet6Address.getByAddress(Arrays.copyOfRange(recvbuf, - IPV6_DST_ADDR_OFFSET, IPV6_DST_ADDR_OFFSET + IPV6_ADDR_LEN)); - } catch (UnknownHostException | ClassCastException impossible) { - throw new AssertionError("16-byte array not valid IPv6 address?"); - } - return dstAddr; - } - - @Override - protected void handlePacket(byte[] recvbuf, int length) { - if (mSendIfaceParams == null) { - return; - } - - // The BPF filter should already have checked the length of the packet, but... - if (length < IPV6_HEADER_LEN) { - return; - } - Inet6Address destv6 = getIpv6DestinationAddress(recvbuf); - if (!destv6.isMulticastAddress()) { - return; - } - InetSocketAddress dest = new InetSocketAddress(destv6, 0); - - FileDescriptor fd = null; - try { - fd = Os.socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW); - SocketUtils.bindSocketToInterface(fd, mSendIfaceParams.name); - - int ret = Os.sendto(fd, recvbuf, 0, length, 0, dest); - } catch (ErrnoException | SocketException e) { - Log.e(mTag, "handlePacket error: " + e); - } finally { - closeSocketQuietly(fd); - } - } -} diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java deleted file mode 100644 index 7c0b7cc7515c..000000000000 --- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java +++ /dev/null @@ -1,744 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import static android.net.util.NetworkConstants.IPV6_MIN_MTU; -import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; -import static android.net.util.TetheringUtils.getAllNodesForScopeId; -import static android.system.OsConstants.AF_INET6; -import static android.system.OsConstants.IPPROTO_ICMPV6; -import static android.system.OsConstants.SOCK_RAW; -import static android.system.OsConstants.SOL_SOCKET; -import static android.system.OsConstants.SO_SNDTIMEO; - -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.TrafficStats; -import android.net.util.InterfaceParams; -import android.net.util.SocketUtils; -import android.net.util.TetheringUtils; -import android.system.ErrnoException; -import android.system.Os; -import android.system.StructTimeval; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.TrafficStatsConstants; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketException; -import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - - -/** - * Basic IPv6 Router Advertisement Daemon. - * - * TODO: - * - * - Rewrite using Handler (and friends) so that AlarmManager can deliver - * "kick" messages when it's time to send a multicast RA. - * - * @hide - */ -public class RouterAdvertisementDaemon { - private static final String TAG = RouterAdvertisementDaemon.class.getSimpleName(); - private static final byte ICMPV6_ND_ROUTER_SOLICIT = asByte(133); - private static final byte ICMPV6_ND_ROUTER_ADVERT = asByte(134); - private static final int MIN_RA_HEADER_SIZE = 16; - - // Summary of various timers and lifetimes. - private static final int MIN_RTR_ADV_INTERVAL_SEC = 300; - private static final int MAX_RTR_ADV_INTERVAL_SEC = 600; - // In general, router, prefix, and DNS lifetimes are all advised to be - // greater than or equal to 3 * MAX_RTR_ADV_INTERVAL. Here, we double - // that to allow for multicast packet loss. - // - // This MAX_RTR_ADV_INTERVAL_SEC and DEFAULT_LIFETIME are also consistent - // with the https://tools.ietf.org/html/rfc7772#section-4 discussion of - // "approximately 7 RAs per hour". - private static final int DEFAULT_LIFETIME = 6 * MAX_RTR_ADV_INTERVAL_SEC; - // From https://tools.ietf.org/html/rfc4861#section-10 . - private static final int MIN_DELAY_BETWEEN_RAS_SEC = 3; - // Both initial and final RAs, but also for changes in RA contents. - // From https://tools.ietf.org/html/rfc4861#section-10 . - private static final int MAX_URGENT_RTR_ADVERTISEMENTS = 5; - - private static final int DAY_IN_SECONDS = 86_400; - - private final InterfaceParams mInterface; - private final InetSocketAddress mAllNodes; - - // This lock is to protect the RA from being updated while being - // transmitted on another thread (multicast or unicast). - // - // TODO: This should be handled with a more RCU-like approach. - private final Object mLock = new Object(); - @GuardedBy("mLock") - private final byte[] mRA = new byte[IPV6_MIN_MTU]; - @GuardedBy("mLock") - private int mRaLength; - @GuardedBy("mLock") - private final DeprecatedInfoTracker mDeprecatedInfoTracker; - @GuardedBy("mLock") - private RaParams mRaParams; - - private volatile FileDescriptor mSocket; - private volatile MulticastTransmitter mMulticastTransmitter; - private volatile UnicastResponder mUnicastResponder; - - /** Encapsulate the RA parameters for RouterAdvertisementDaemon.*/ - public static class RaParams { - // Tethered traffic will have the hop limit properly decremented. - // Consequently, set the hoplimit greater by one than the upstream - // unicast hop limit. - // - // TODO: Dynamically pass down the IPV6_UNICAST_HOPS value from the - // upstream interface for more correct behaviour. - static final byte DEFAULT_HOPLIMIT = 65; - - public boolean hasDefaultRoute; - public byte hopLimit; - public int mtu; - public HashSet prefixes; - public HashSet dnses; - - public RaParams() { - hasDefaultRoute = false; - hopLimit = DEFAULT_HOPLIMIT; - mtu = IPV6_MIN_MTU; - prefixes = new HashSet(); - dnses = new HashSet(); - } - - public RaParams(RaParams other) { - hasDefaultRoute = other.hasDefaultRoute; - hopLimit = other.hopLimit; - mtu = other.mtu; - prefixes = (HashSet) other.prefixes.clone(); - dnses = (HashSet) other.dnses.clone(); - } - - /** - * Returns the subset of RA parameters that become deprecated when - * moving from announcing oldRa to announcing newRa. - * - * Currently only tracks differences in |prefixes| and |dnses|. - */ - public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) { - RaParams newlyDeprecated = new RaParams(); - - if (oldRa != null) { - for (IpPrefix ipp : oldRa.prefixes) { - if (newRa == null || !newRa.prefixes.contains(ipp)) { - newlyDeprecated.prefixes.add(ipp); - } - } - - for (Inet6Address dns : oldRa.dnses) { - if (newRa == null || !newRa.dnses.contains(dns)) { - newlyDeprecated.dnses.add(dns); - } - } - } - - return newlyDeprecated; - } - } - - private static class DeprecatedInfoTracker { - private final HashMap mPrefixes = new HashMap<>(); - private final HashMap mDnses = new HashMap<>(); - - Set getPrefixes() { - return mPrefixes.keySet(); - } - - void putPrefixes(Set prefixes) { - for (IpPrefix ipp : prefixes) { - mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS); - } - } - - void removePrefixes(Set prefixes) { - for (IpPrefix ipp : prefixes) { - mPrefixes.remove(ipp); - } - } - - Set getDnses() { - return mDnses.keySet(); - } - - void putDnses(Set dnses) { - for (Inet6Address dns : dnses) { - mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS); - } - } - - void removeDnses(Set dnses) { - for (Inet6Address dns : dnses) { - mDnses.remove(dns); - } - } - - boolean isEmpty() { - return mPrefixes.isEmpty() && mDnses.isEmpty(); - } - - private boolean decrementCounters() { - boolean removed = decrementCounter(mPrefixes); - removed |= decrementCounter(mDnses); - return removed; - } - - private boolean decrementCounter(HashMap map) { - boolean removed = false; - - for (Iterator> it = map.entrySet().iterator(); - it.hasNext();) { - Map.Entry kv = it.next(); - if (kv.getValue() == 0) { - it.remove(); - removed = true; - } else { - kv.setValue(kv.getValue() - 1); - } - } - - return removed; - } - } - - public RouterAdvertisementDaemon(InterfaceParams ifParams) { - mInterface = ifParams; - mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0); - mDeprecatedInfoTracker = new DeprecatedInfoTracker(); - } - - /** Build new RA.*/ - public void buildNewRa(RaParams deprecatedParams, RaParams newParams) { - synchronized (mLock) { - if (deprecatedParams != null) { - mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes); - mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses); - } - - if (newParams != null) { - // Process information that is no longer deprecated. - mDeprecatedInfoTracker.removePrefixes(newParams.prefixes); - mDeprecatedInfoTracker.removeDnses(newParams.dnses); - } - - mRaParams = newParams; - assembleRaLocked(); - } - - maybeNotifyMulticastTransmitter(); - } - - /** Start router advertisement daemon. */ - public boolean start() { - if (!createSocket()) { - return false; - } - - mMulticastTransmitter = new MulticastTransmitter(); - mMulticastTransmitter.start(); - - mUnicastResponder = new UnicastResponder(); - mUnicastResponder.start(); - - return true; - } - - /** Stop router advertisement daemon. */ - public void stop() { - closeSocket(); - // Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before - // the thread's termination. - maybeNotifyMulticastTransmitter(); - mMulticastTransmitter = null; - mUnicastResponder = null; - } - - @GuardedBy("mLock") - private void assembleRaLocked() { - final ByteBuffer ra = ByteBuffer.wrap(mRA); - ra.order(ByteOrder.BIG_ENDIAN); - - final boolean haveRaParams = (mRaParams != null); - boolean shouldSendRA = false; - - try { - putHeader(ra, haveRaParams && mRaParams.hasDefaultRoute, - haveRaParams ? mRaParams.hopLimit : RaParams.DEFAULT_HOPLIMIT); - putSlla(ra, mInterface.macAddr.toByteArray()); - mRaLength = ra.position(); - - // https://tools.ietf.org/html/rfc5175#section-4 says: - // - // "MUST NOT be added to a Router Advertisement message - // if no flags in the option are set." - // - // putExpandedFlagsOption(ra); - - if (haveRaParams) { - putMtu(ra, mRaParams.mtu); - mRaLength = ra.position(); - - for (IpPrefix ipp : mRaParams.prefixes) { - putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME); - mRaLength = ra.position(); - shouldSendRA = true; - } - - if (mRaParams.dnses.size() > 0) { - putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME); - mRaLength = ra.position(); - shouldSendRA = true; - } - } - - for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) { - putPio(ra, ipp, 0, 0); - mRaLength = ra.position(); - shouldSendRA = true; - } - - final Set deprecatedDnses = mDeprecatedInfoTracker.getDnses(); - if (!deprecatedDnses.isEmpty()) { - putRdnss(ra, deprecatedDnses, 0); - mRaLength = ra.position(); - shouldSendRA = true; - } - } catch (BufferOverflowException e) { - // The packet up to mRaLength is valid, since it has been updated - // progressively as the RA was built. Log an error, and continue - // on as best as possible. - Log.e(TAG, "Could not construct new RA: " + e); - } - - // We have nothing worth announcing; indicate as much to maybeSendRA(). - if (!shouldSendRA) { - mRaLength = 0; - } - } - - private void maybeNotifyMulticastTransmitter() { - final MulticastTransmitter m = mMulticastTransmitter; - if (m != null) { - m.hup(); - } - } - - private static byte asByte(int value) { - return (byte) value; - } - private static short asShort(int value) { - return (short) value; - } - - private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) { - /** - Router Advertisement Message Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Cur Hop Limit |M|O|H|Prf|P|R|R| Router Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Reachable Time | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Retrans Timer | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Options ... - +-+-+-+-+-+-+-+-+-+-+-+- - */ - ra.put(ICMPV6_ND_ROUTER_ADVERT) - .put(asByte(0)) - .putShort(asShort(0)) - .put(hopLimit) - // RFC 4191 "high" preference, iff. advertising a default route. - .put(hasDefaultRoute ? asByte(0x08) : asByte(0)) - .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0)) - .putInt(0) - .putInt(0); - } - - private static void putSlla(ByteBuffer ra, byte[] slla) { - /** - Source/Target Link-layer Address - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Link-Layer Address ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - if (slla == null || slla.length != 6) { - // Only IEEE 802.3 6-byte addresses are supported. - return; - } - - final byte nd_option_slla = 1; - final byte slla_num_8octets = 1; - ra.put(nd_option_slla) - .put(slla_num_8octets) - .put(slla); - } - - private static void putExpandedFlagsOption(ByteBuffer ra) { - /** - Router Advertisement Expanded Flags Option - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Bit fields available .. - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - ... for assignment | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - final byte nd_option__efo = 26; - final byte efo_num_8octets = 1; - - ra.put(nd_option__efo) - .put(efo_num_8octets) - .putShort(asShort(0)) - .putInt(0); - } - - private static void putMtu(ByteBuffer ra, int mtu) { - /** - MTU - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | MTU | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - final byte nd_option_mtu = 5; - final byte mtu_num_8octs = 1; - ra.put(nd_option_mtu) - .put(mtu_num_8octs) - .putShort(asShort(0)) - .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu); - } - - private static void putPio(ByteBuffer ra, IpPrefix ipp, - int validTime, int preferredTime) { - /** - Prefix Information - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Prefix Length |L|A| Reserved1 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Valid Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Preferred Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Reserved2 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - + + - | | - + Prefix + - | | - + + - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - final int prefixLength = ipp.getPrefixLength(); - if (prefixLength != 64) { - return; - } - final byte nd_option_pio = 3; - final byte pio_num_8octets = 4; - - if (validTime < 0) validTime = 0; - if (preferredTime < 0) preferredTime = 0; - if (preferredTime > validTime) preferredTime = validTime; - - final byte[] addr = ipp.getAddress().getAddress(); - ra.put(nd_option_pio) - .put(pio_num_8octets) - .put(asByte(prefixLength)) - .put(asByte(0xc0)) /* L & A set */ - .putInt(validTime) - .putInt(preferredTime) - .putInt(0) - .put(addr); - } - - private static void putRio(ByteBuffer ra, IpPrefix ipp) { - /** - Route Information Option - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Prefix Length |Resvd|Prf|Resvd| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Route Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Prefix (Variable Length) | - . . - . . - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - final int prefixLength = ipp.getPrefixLength(); - if (prefixLength > 64) { - return; - } - final byte nd_option_rio = 24; - final byte rio_num_8octets = asByte( - (prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3); - - final byte[] addr = ipp.getAddress().getAddress(); - ra.put(nd_option_rio) - .put(rio_num_8octets) - .put(asByte(prefixLength)) - .put(asByte(0x18)) - .putInt(DEFAULT_LIFETIME); - - // Rely upon an IpPrefix's address being properly zeroed. - if (prefixLength > 0) { - ra.put(addr, 0, (prefixLength <= 64) ? 8 : 16); - } - } - - private static void putRdnss(ByteBuffer ra, Set dnses, int lifetime) { - /** - Recursive DNS Server (RDNSS) Option - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - : Addresses of IPv6 Recursive DNS Servers : - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - final HashSet filteredDnses = new HashSet<>(); - for (Inet6Address dns : dnses) { - if ((new LinkAddress(dns, RFC7421_PREFIX_LENGTH)).isGlobalPreferred()) { - filteredDnses.add(dns); - } - } - if (filteredDnses.isEmpty()) return; - - final byte nd_option_rdnss = 25; - final byte rdnss_num_8octets = asByte(dnses.size() * 2 + 1); - ra.put(nd_option_rdnss) - .put(rdnss_num_8octets) - .putShort(asShort(0)) - .putInt(lifetime); - - for (Inet6Address dns : filteredDnses) { - // NOTE: If the full of list DNS servers doesn't fit in the packet, - // this code will cause a buffer overflow and the RA won't include - // this instance of the option at all. - // - // TODO: Consider looking at ra.remaining() to determine how many - // DNS servers will fit, and adding only those. - ra.put(dns.getAddress()); - } - } - - private boolean createSocket() { - final int send_timout_ms = 300; - - final int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR); - try { - mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); - // Setting SNDTIMEO is purely for defensive purposes. - Os.setsockoptTimeval( - mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms)); - SocketUtils.bindSocketToInterface(mSocket, mInterface.name); - TetheringUtils.setupRaSocket(mSocket, mInterface.index); - } catch (ErrnoException | IOException e) { - Log.e(TAG, "Failed to create RA daemon socket: " + e); - return false; - } finally { - TrafficStats.setThreadStatsTag(oldTag); - } - - return true; - } - - private void closeSocket() { - if (mSocket != null) { - try { - SocketUtils.closeSocket(mSocket); - } catch (IOException ignored) { } - } - mSocket = null; - } - - private boolean isSocketValid() { - final FileDescriptor s = mSocket; - return (s != null) && s.valid(); - } - - private boolean isSuitableDestination(InetSocketAddress dest) { - if (mAllNodes.equals(dest)) { - return true; - } - - final InetAddress destip = dest.getAddress(); - return (destip instanceof Inet6Address) - && destip.isLinkLocalAddress() - && (((Inet6Address) destip).getScopeId() == mInterface.index); - } - - private void maybeSendRA(InetSocketAddress dest) { - if (dest == null || !isSuitableDestination(dest)) { - dest = mAllNodes; - } - - try { - synchronized (mLock) { - if (mRaLength < MIN_RA_HEADER_SIZE) { - // No actual RA to send. - return; - } - Os.sendto(mSocket, mRA, 0, mRaLength, 0, dest); - } - Log.d(TAG, "RA sendto " + dest.getAddress().getHostAddress()); - } catch (ErrnoException | SocketException e) { - if (isSocketValid()) { - Log.e(TAG, "sendto error: " + e); - } - } - } - - private final class UnicastResponder extends Thread { - private final InetSocketAddress mSolicitor = new InetSocketAddress(0); - // The recycled buffer for receiving Router Solicitations from clients. - // If the RS is larger than IPV6_MIN_MTU the packets are truncated. - // This is fine since currently only byte 0 is examined anyway. - private final byte[] mSolicitation = new byte[IPV6_MIN_MTU]; - - @Override - public void run() { - while (isSocketValid()) { - try { - // Blocking receive. - final int rval = Os.recvfrom( - mSocket, mSolicitation, 0, mSolicitation.length, 0, mSolicitor); - // Do the least possible amount of validation. - if (rval < 1 || mSolicitation[0] != ICMPV6_ND_ROUTER_SOLICIT) { - continue; - } - } catch (ErrnoException | SocketException e) { - if (isSocketValid()) { - Log.e(TAG, "recvfrom error: " + e); - } - continue; - } - - maybeSendRA(mSolicitor); - } - } - } - - // TODO: Consider moving this to run on a provided Looper as a Handler, - // with WakeupMessage-style messages providing the timer driven input. - private final class MulticastTransmitter extends Thread { - private final Random mRandom = new Random(); - private final AtomicInteger mUrgentAnnouncements = new AtomicInteger(0); - - @Override - public void run() { - while (isSocketValid()) { - try { - Thread.sleep(getNextMulticastTransmitDelayMs()); - } catch (InterruptedException ignored) { - // Stop sleeping, immediately send an RA, and continue. - } - - maybeSendRA(mAllNodes); - synchronized (mLock) { - if (mDeprecatedInfoTracker.decrementCounters()) { - // At least one deprecated PIO has been removed; - // reassemble the RA. - assembleRaLocked(); - } - } - } - } - - public void hup() { - // Set to one fewer that the desired number, because as soon as - // the thread interrupt is processed we immediately send an RA - // and mUrgentAnnouncements is not examined until the subsequent - // sleep interval computation (i.e. this way we send 3 and not 4). - mUrgentAnnouncements.set(MAX_URGENT_RTR_ADVERTISEMENTS - 1); - interrupt(); - } - - private int getNextMulticastTransmitDelaySec() { - boolean deprecationInProgress = false; - synchronized (mLock) { - if (mRaLength < MIN_RA_HEADER_SIZE) { - // No actual RA to send; just sleep for 1 day. - return DAY_IN_SECONDS; - } - deprecationInProgress = !mDeprecatedInfoTracker.isEmpty(); - } - - final int urgentPending = mUrgentAnnouncements.getAndDecrement(); - if ((urgentPending > 0) || deprecationInProgress) { - return MIN_DELAY_BETWEEN_RAS_SEC; - } - - return MIN_RTR_ADV_INTERVAL_SEC + mRandom.nextInt( - MAX_RTR_ADV_INTERVAL_SEC - MIN_RTR_ADV_INTERVAL_SEC); - } - - private long getNextMulticastTransmitDelayMs() { - return 1000 * (long) getNextMulticastTransmitDelaySec(); - } - } -} diff --git a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java b/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java deleted file mode 100644 index b1ffdb01f5f3..000000000000 --- a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net.util; - -import android.net.INetdUnsolicitedEventListener; - -import androidx.annotation.NonNull; - -/** - * Base {@link INetdUnsolicitedEventListener} that provides no-op implementations which can be - * overridden. - */ -public class BaseNetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub { - - @Override - public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, - int uid) { } - - @Override - public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) { } - - @Override - public void onInterfaceDnsServerInfo(@NonNull String ifName, long lifetimeS, - @NonNull String[] servers) { } - - @Override - public void onInterfaceAddressUpdated(@NonNull String addr, String ifName, int flags, - int scope) { } - - @Override - public void onInterfaceAddressRemoved(@NonNull String addr, @NonNull String ifName, int flags, - int scope) { } - - @Override - public void onInterfaceAdded(@NonNull String ifName) { } - - @Override - public void onInterfaceRemoved(@NonNull String ifName) { } - - @Override - public void onInterfaceChanged(@NonNull String ifName, boolean up) { } - - @Override - public void onInterfaceLinkStateChanged(@NonNull String ifName, boolean up) { } - - @Override - public void onRouteChanged(boolean updated, @NonNull String route, @NonNull String gateway, - @NonNull String ifName) { } - - @Override - public void onStrictCleartextDetected(int uid, @NonNull String hex) { } - - @Override - public int getInterfaceVersion() { - return INetdUnsolicitedEventListener.VERSION; - } - - @Override - public String getInterfaceHash() { - return INetdUnsolicitedEventListener.HASH; - } -} diff --git a/packages/Tethering/src/android/net/util/InterfaceSet.java b/packages/Tethering/src/android/net/util/InterfaceSet.java deleted file mode 100644 index 758978711bd4..000000000000 --- a/packages/Tethering/src/android/net/util/InterfaceSet.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 android.net.util; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.StringJoiner; - - -/** - * @hide - */ -public class InterfaceSet { - public final Set ifnames; - - public InterfaceSet(String... names) { - final Set nameSet = new HashSet<>(); - for (String name : names) { - if (name != null) nameSet.add(name); - } - ifnames = Collections.unmodifiableSet(nameSet); - } - - @Override - public String toString() { - final StringJoiner sj = new StringJoiner(",", "[", "]"); - for (String ifname : ifnames) sj.add(ifname); - return sj.toString(); - } - - @Override - public boolean equals(Object obj) { - return obj != null - && obj instanceof InterfaceSet - && ifnames.equals(((InterfaceSet) obj).ifnames); - } -} diff --git a/packages/Tethering/src/android/net/util/PrefixUtils.java b/packages/Tethering/src/android/net/util/PrefixUtils.java deleted file mode 100644 index f203e9995f3d..000000000000 --- a/packages/Tethering/src/android/net/util/PrefixUtils.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2017 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 android.net.util; - -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - - -/** - * @hide - */ -public class PrefixUtils { - private static final IpPrefix[] MIN_NON_FORWARDABLE_PREFIXES = { - pfx("127.0.0.0/8"), // IPv4 loopback - pfx("169.254.0.0/16"), // IPv4 link-local, RFC3927#section-8 - pfx("::/3"), - pfx("fe80::/64"), // IPv6 link-local - pfx("fc00::/7"), // IPv6 ULA - pfx("ff02::/8"), // IPv6 link-local multicast - }; - - public static final IpPrefix DEFAULT_WIFI_P2P_PREFIX = pfx("192.168.49.0/24"); - - /** Get non forwardable prefixes. */ - public static Set getNonForwardablePrefixes() { - final HashSet prefixes = new HashSet<>(); - addNonForwardablePrefixes(prefixes); - return prefixes; - } - - /** Add non forwardable prefixes. */ - public static void addNonForwardablePrefixes(Set prefixes) { - Collections.addAll(prefixes, MIN_NON_FORWARDABLE_PREFIXES); - } - - /** Get local prefixes from |lp|. */ - public static Set localPrefixesFrom(LinkProperties lp) { - final HashSet localPrefixes = new HashSet<>(); - if (lp == null) return localPrefixes; - - for (LinkAddress addr : lp.getAllLinkAddresses()) { - if (addr.getAddress().isLinkLocalAddress()) continue; - localPrefixes.add(asIpPrefix(addr)); - } - // TODO: Add directly-connected routes as well (ones from which we did - // not also form a LinkAddress)? - - return localPrefixes; - } - - /** Convert LinkAddress |addr| to IpPrefix. */ - public static IpPrefix asIpPrefix(LinkAddress addr) { - return new IpPrefix(addr.getAddress(), addr.getPrefixLength()); - } - - /** Convert InetAddress |ip| to IpPrefix. */ - public static IpPrefix ipAddressAsPrefix(InetAddress ip) { - final int bitLength = (ip instanceof Inet4Address) - ? NetworkConstants.IPV4_ADDR_BITS - : NetworkConstants.IPV6_ADDR_BITS; - return new IpPrefix(ip, bitLength); - } - - private static IpPrefix pfx(String prefixStr) { - return new IpPrefix(prefixStr); - } -} diff --git a/packages/Tethering/src/android/net/util/TetheringMessageBase.java b/packages/Tethering/src/android/net/util/TetheringMessageBase.java deleted file mode 100644 index 29c0a817b6f4..000000000000 --- a/packages/Tethering/src/android/net/util/TetheringMessageBase.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net.util; - -/** - * This class defines Message.what base addresses for various state machine. - */ -public class TetheringMessageBase { - public static final int BASE_MAIN_SM = 0; - public static final int BASE_IPSERVER = 100; - -} diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java deleted file mode 100644 index 53b54f7de05d..000000000000 --- a/packages/Tethering/src/android/net/util/TetheringUtils.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net.util; - -import android.net.TetherStatsParcel; -import android.net.TetheringRequestParcel; -import android.util.Log; - -import androidx.annotation.NonNull; - -import java.io.FileDescriptor; -import java.net.Inet6Address; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.Objects; - -/** - * The classes and the methods for tethering utilization. - * - * {@hide} - */ -public class TetheringUtils { - public static final byte[] ALL_NODES = new byte[] { - (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 - }; - - /** - * Configures a socket for receiving and sending ICMPv6 neighbor advertisments. - * @param fd the socket's {@link FileDescriptor}. - */ - public static native void setupNaSocket(FileDescriptor fd) - throws SocketException; - - /** - * Configures a socket for receiving and sending ICMPv6 neighbor solicitations. - * @param fd the socket's {@link FileDescriptor}. - */ - public static native void setupNsSocket(FileDescriptor fd) - throws SocketException; - - /** - * The object which records offload Tx/Rx forwarded bytes/packets. - * TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with - * this class as well. - */ - public static class ForwardedStats { - public final long rxBytes; - public final long rxPackets; - public final long txBytes; - public final long txPackets; - - public ForwardedStats() { - rxBytes = 0; - rxPackets = 0; - txBytes = 0; - txPackets = 0; - } - - public ForwardedStats(long rxBytes, long txBytes) { - this.rxBytes = rxBytes; - this.rxPackets = 0; - this.txBytes = txBytes; - this.txPackets = 0; - } - - public ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets) { - this.rxBytes = rxBytes; - this.rxPackets = rxPackets; - this.txBytes = txBytes; - this.txPackets = txPackets; - } - - public ForwardedStats(@NonNull TetherStatsParcel tetherStats) { - rxBytes = tetherStats.rxBytes; - rxPackets = tetherStats.rxPackets; - txBytes = tetherStats.txBytes; - txPackets = tetherStats.txPackets; - } - - public ForwardedStats(@NonNull ForwardedStats other) { - rxBytes = other.rxBytes; - rxPackets = other.rxPackets; - txBytes = other.txBytes; - txPackets = other.txPackets; - } - - /** Add Tx/Rx bytes/packets and return the result as a new object. */ - @NonNull - public ForwardedStats add(@NonNull ForwardedStats other) { - return new ForwardedStats(rxBytes + other.rxBytes, rxPackets + other.rxPackets, - txBytes + other.txBytes, txPackets + other.txPackets); - } - - /** Subtract Tx/Rx bytes/packets and return the result as a new object. */ - @NonNull - public ForwardedStats subtract(@NonNull ForwardedStats other) { - // TODO: Perhaps throw an exception if any negative difference value just in case. - final long rxBytesDiff = Math.max(rxBytes - other.rxBytes, 0); - final long rxPacketsDiff = Math.max(rxPackets - other.rxPackets, 0); - final long txBytesDiff = Math.max(txBytes - other.txBytes, 0); - final long txPacketsDiff = Math.max(txPackets - other.txPackets, 0); - return new ForwardedStats(rxBytesDiff, rxPacketsDiff, txBytesDiff, txPacketsDiff); - } - - /** Returns the string representation of this object. */ - @NonNull - public String toString() { - return String.format("ForwardedStats(rxb: %d, rxp: %d, txb: %d, txp: %d)", rxBytes, - rxPackets, txBytes, txPackets); - } - } - - /** - * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. - * @param fd the socket's {@link FileDescriptor}. - * @param ifIndex the interface index. - */ - public static native void setupRaSocket(FileDescriptor fd, int ifIndex) - throws SocketException; - - /** - * Read s as an unsigned 16-bit integer. - */ - public static int uint16(short s) { - return s & 0xffff; - } - - /** Check whether two TetheringRequestParcels are the same. */ - public static boolean isTetheringRequestEquals(final TetheringRequestParcel request, - final TetheringRequestParcel otherRequest) { - if (request == otherRequest) return true; - - return request != null && otherRequest != null - && request.tetheringType == otherRequest.tetheringType - && Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address) - && Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress) - && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck - && request.showProvisioningUi == otherRequest.showProvisioningUi; - } - - /** Get inet6 address for all nodes given scope ID. */ - public static Inet6Address getAllNodesForScopeId(int scopeId) { - try { - return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId); - } catch (UnknownHostException uhe) { - Log.wtf("TetheringUtils", "Failed to construct Inet6Address from " - + Arrays.toString(ALL_NODES) + " and scopedId " + scopeId); - return null; - } - } -} diff --git a/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java b/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java deleted file mode 100644 index e2804abd752b..000000000000 --- a/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2017 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 android.net.util; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Handler; -import android.util.Log; - -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - - -/** - * A utility class that runs the provided callback on the provided handler when - * intents matching the provided filter arrive. Intents received by a stale - * receiver are safely ignored. - * - * Calls to startListening() and stopListening() must happen on the same thread. - * - * @hide - */ -public class VersionedBroadcastListener { - private static final boolean DBG = false; - - private final String mTag; - private final Context mContext; - private final Handler mHandler; - private final IntentFilter mFilter; - private final Consumer mCallback; - private final AtomicInteger mGenerationNumber; - private BroadcastReceiver mReceiver; - - public VersionedBroadcastListener(String tag, Context ctx, Handler handler, - IntentFilter filter, Consumer callback) { - mTag = tag; - mContext = ctx; - mHandler = handler; - mFilter = filter; - mCallback = callback; - mGenerationNumber = new AtomicInteger(0); - } - - /** Start listening to intent broadcast. */ - public void startListening() { - if (DBG) Log.d(mTag, "startListening"); - if (mReceiver != null) return; - - mReceiver = new Receiver(mTag, mGenerationNumber, mCallback); - mContext.registerReceiver(mReceiver, mFilter, null, mHandler); - } - - /** Stop listening to intent broadcast. */ - public void stopListening() { - if (DBG) Log.d(mTag, "stopListening"); - if (mReceiver == null) return; - - mGenerationNumber.incrementAndGet(); - mContext.unregisterReceiver(mReceiver); - mReceiver = null; - } - - private static class Receiver extends BroadcastReceiver { - public final String tag; - public final AtomicInteger atomicGenerationNumber; - public final Consumer callback; - // Used to verify this receiver is still current. - public final int generationNumber; - - Receiver(String tag, AtomicInteger atomicGenerationNumber, Consumer callback) { - this.tag = tag; - this.atomicGenerationNumber = atomicGenerationNumber; - this.callback = callback; - generationNumber = atomicGenerationNumber.incrementAndGet(); - } - - @Override - public void onReceive(Context context, Intent intent) { - final int currentGenerationNumber = atomicGenerationNumber.get(); - - if (DBG) { - Log.d(tag, "receiver generationNumber=" + generationNumber - + ", current generationNumber=" + currentGenerationNumber); - } - if (generationNumber != currentGenerationNumber) return; - - callback.accept(intent); - } - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java deleted file mode 100644 index 20f30ea7a460..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ /dev/null @@ -1,778 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStats.UID_TETHERING; -import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; - -import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; - -import android.app.usage.NetworkStatsManager; -import android.net.INetd; -import android.net.MacAddress; -import android.net.NetworkStats; -import android.net.NetworkStats.Entry; -import android.net.TetherOffloadRuleParcel; -import android.net.TetherStatsParcel; -import android.net.ip.IpServer; -import android.net.netstats.provider.NetworkStatsProvider; -import android.net.util.SharedLog; -import android.net.util.TetheringUtils.ForwardedStats; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.RemoteException; -import android.os.ServiceSpecificException; -import android.text.TextUtils; -import android.util.Log; -import android.util.SparseArray; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; - -import java.net.Inet6Address; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; - -/** - * This coordinator is responsible for providing BPF offload relevant functionality. - * - Get tethering stats. - * - Set data limit. - * - Set global alert. - * - Add/remove forwarding rules. - * - * @hide - */ -public class BpfCoordinator { - private static final String TAG = BpfCoordinator.class.getSimpleName(); - private static final int DUMP_TIMEOUT_MS = 10_000; - - @VisibleForTesting - enum StatsType { - STATS_PER_IFACE, - STATS_PER_UID, - } - - @NonNull - private final Handler mHandler; - @NonNull - private final INetd mNetd; - @NonNull - private final SharedLog mLog; - @NonNull - private final Dependencies mDeps; - @Nullable - private final BpfTetherStatsProvider mStatsProvider; - - // True if BPF offload is supported, false otherwise. The BPF offload could be disabled by - // a runtime resource overlay package or device configuration. This flag is only initialized - // in the constructor because it is hard to unwind all existing change once device - // configuration is changed. Especially the forwarding rules. Keep the same setting - // to make it simpler. See also TetheringConfiguration. - private final boolean mIsBpfEnabled; - - // Tracks whether BPF tethering is started or not. This is set by tethering before it - // starts the first IpServer and is cleared by tethering shortly before the last IpServer - // is stopped. Note that rule updates (especially deletions, but sometimes additions as - // well) may arrive when this is false. If they do, they must be communicated to netd. - // Changes in data limits may also arrive when this is false, and if they do, they must - // also be communicated to netd. - private boolean mPollingStarted = false; - - // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert - // quota is interface independent and global for tether offload. - private long mRemainingAlertQuota = QUOTA_UNLIMITED; - - // Maps upstream interface index to offloaded traffic statistics. - // Always contains the latest total bytes/packets, since each upstream was started, received - // from the BPF maps for each interface. - private final SparseArray mStats = new SparseArray<>(); - - // Maps upstream interface names to interface quotas. - // Always contains the latest value received from the framework for each interface, regardless - // of whether offload is currently running (or is even supported) on that interface. Only - // includes interfaces that have a quota set. Note that this map is used for storing the quota - // which is set from the service. Because the service uses the interface name to present the - // interface, this map uses the interface name to be the mapping index. - private final HashMap mInterfaceQuotas = new HashMap<>(); - - // Maps upstream interface index to interface names. - // Store all interface name since boot. Used for lookup what interface name it is from the - // tether stats got from netd because netd reports interface index to present an interface. - // TODO: Remove the unused interface name. - private final SparseArray mInterfaceNames = new SparseArray<>(); - - // Map of downstream rule maps. Each of these maps represents the IPv6 forwarding rules for a - // given downstream. Each map: - // - Is owned by the IpServer that is responsible for that downstream. - // - Must only be modified by that IpServer. - // - Is created when the IpServer adds its first rule, and deleted when the IpServer deletes - // its last rule (or clears its rules). - // TODO: Perhaps seal the map and rule operations which communicates with netd into a class. - // TODO: Does this need to be a LinkedHashMap or can it just be a HashMap? Also, could it be - // a ConcurrentHashMap, in order to avoid the copies in tetherOffloadRuleClear - // and tetherOffloadRuleUpdate? - // TODO: Perhaps use one-dimensional map and access specific downstream rules via downstream - // index. For doing that, IpServer must guarantee that it always has a valid IPv6 downstream - // interface index while calling function to clear all rules. IpServer may be calling clear - // rules function without a valid IPv6 downstream interface index even if it may have one - // before. IpServer would need to call getInterfaceParams() in the constructor instead of when - // startIpv6() is called, and make mInterfaceParams final. - private final HashMap> - mIpv6ForwardingRules = new LinkedHashMap<>(); - - // Runnable that used by scheduling next polling of stats. - private final Runnable mScheduledPollingTask = () -> { - updateForwardedStatsFromNetd(); - maybeSchedulePollingStats(); - }; - - @VisibleForTesting - public abstract static class Dependencies { - /** Get handler. */ - @NonNull public abstract Handler getHandler(); - - /** Get netd. */ - @NonNull public abstract INetd getNetd(); - - /** Get network stats manager. */ - @NonNull public abstract NetworkStatsManager getNetworkStatsManager(); - - /** Get shared log. */ - @NonNull public abstract SharedLog getSharedLog(); - - /** Get tethering configuration. */ - @Nullable public abstract TetheringConfiguration getTetherConfig(); - } - - @VisibleForTesting - public BpfCoordinator(@NonNull Dependencies deps) { - mDeps = deps; - mHandler = mDeps.getHandler(); - mNetd = mDeps.getNetd(); - mLog = mDeps.getSharedLog().forSubComponent(TAG); - mIsBpfEnabled = isBpfEnabled(); - BpfTetherStatsProvider provider = new BpfTetherStatsProvider(); - try { - mDeps.getNetworkStatsManager().registerNetworkStatsProvider( - getClass().getSimpleName(), provider); - } catch (RuntimeException e) { - // TODO: Perhaps not allow to use BPF offload because the reregistration failure - // implied that no data limit could be applies on a metered upstream if any. - Log.wtf(TAG, "Cannot register offload stats provider: " + e); - provider = null; - } - mStatsProvider = provider; - } - - /** - * Start BPF tethering offload stats polling when the first upstream is started. - * Note that this can be only called on handler thread. - * TODO: Perhaps check BPF support before starting. - * TODO: Start the stats polling only if there is any client on the downstream. - */ - public void startPolling() { - if (mPollingStarted) return; - - if (!mIsBpfEnabled) { - mLog.i("Offload disabled"); - return; - } - - mPollingStarted = true; - maybeSchedulePollingStats(); - - mLog.i("Polling started"); - } - - /** - * Stop BPF tethering offload stats polling. - * The data limit cleanup and the tether stats maps cleanup are not implemented here. - * These cleanups rely on all IpServers calling #tetherOffloadRuleRemove. After the - * last rule is removed from the upstream, #tetherOffloadRuleRemove does the cleanup - * functionality. - * Note that this can be only called on handler thread. - */ - public void stopPolling() { - if (!mPollingStarted) return; - - // Stop scheduled polling tasks and poll the latest stats from BPF maps. - if (mHandler.hasCallbacks(mScheduledPollingTask)) { - mHandler.removeCallbacks(mScheduledPollingTask); - } - updateForwardedStatsFromNetd(); - mPollingStarted = false; - - mLog.i("Polling stopped"); - } - - /** - * Add forwarding rule. After adding the first rule on a given upstream, must add the data - * limit on the given upstream. - * Note that this can be only called on handler thread. - */ - public void tetherOffloadRuleAdd( - @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { - if (!mIsBpfEnabled) return; - - try { - // TODO: Perhaps avoid to add a duplicate rule. - mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel()); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Could not add IPv6 forwarding rule: ", e); - return; - } - - if (!mIpv6ForwardingRules.containsKey(ipServer)) { - mIpv6ForwardingRules.put(ipServer, new LinkedHashMap()); - } - LinkedHashMap rules = mIpv6ForwardingRules.get(ipServer); - - // Setup the data limit on the given upstream if the first rule is added. - final int upstreamIfindex = rule.upstreamIfindex; - if (!isAnyRuleOnUpstream(upstreamIfindex)) { - // If failed to set a data limit, probably should not use this upstream, because - // the upstream may not want to blow through the data limit that was told to apply. - // TODO: Perhaps stop the coordinator. - boolean success = updateDataLimit(upstreamIfindex); - if (!success) { - final String iface = mInterfaceNames.get(upstreamIfindex); - mLog.e("Setting data limit for " + iface + " failed."); - } - } - - // Must update the adding rule after calling #isAnyRuleOnUpstream because it needs to - // check if it is about adding a first rule for a given upstream. - rules.put(rule.address, rule); - } - - /** - * Remove forwarding rule. After removing the last rule on a given upstream, must clear - * data limit, update the last tether stats and remove the tether stats in the BPF maps. - * Note that this can be only called on handler thread. - */ - public void tetherOffloadRuleRemove( - @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { - if (!mIsBpfEnabled) return; - - try { - // TODO: Perhaps avoid to remove a non-existent rule. - mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel()); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Could not remove IPv6 forwarding rule: ", e); - return; - } - - LinkedHashMap rules = mIpv6ForwardingRules.get(ipServer); - if (rules == null) return; - - // Must remove rules before calling #isAnyRuleOnUpstream because it needs to check if - // the last rule is removed for a given upstream. If no rule is removed, return early. - // Avoid unnecessary work on a non-existent rule which may have never been added or - // removed already. - if (rules.remove(rule.address) == null) return; - - // Remove the downstream entry if it has no more rule. - if (rules.isEmpty()) { - mIpv6ForwardingRules.remove(ipServer); - } - - // Do cleanup functionality if there is no more rule on the given upstream. - final int upstreamIfindex = rule.upstreamIfindex; - if (!isAnyRuleOnUpstream(upstreamIfindex)) { - try { - final TetherStatsParcel stats = - mNetd.tetherOffloadGetAndClearStats(upstreamIfindex); - // Update the last stats delta and delete the local cache for a given upstream. - updateQuotaAndStatsFromSnapshot(new TetherStatsParcel[] {stats}); - mStats.remove(upstreamIfindex); - } catch (RemoteException | ServiceSpecificException e) { - Log.wtf(TAG, "Exception when cleanup tether stats for upstream index " - + upstreamIfindex + ": ", e); - } - } - } - - /** - * Clear all forwarding rules for a given downstream. - * Note that this can be only called on handler thread. - */ - public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) { - if (!mIsBpfEnabled) return; - - final LinkedHashMap rules = mIpv6ForwardingRules.get( - ipServer); - if (rules == null) return; - - // Need to build a rule list because the rule map may be changed in the iteration. - for (final Ipv6ForwardingRule rule : new ArrayList(rules.values())) { - tetherOffloadRuleRemove(ipServer, rule); - } - } - - /** - * Update existing forwarding rules to new upstream for a given downstream. - * Note that this can be only called on handler thread. - */ - public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) { - if (!mIsBpfEnabled) return; - - final LinkedHashMap rules = mIpv6ForwardingRules.get( - ipServer); - if (rules == null) return; - - // Need to build a rule list because the rule map may be changed in the iteration. - for (final Ipv6ForwardingRule rule : new ArrayList(rules.values())) { - // Remove the old rule before adding the new one because the map uses the same key for - // both rules. Reversing the processing order causes that the new rule is removed as - // unexpected. - // TODO: Add new rule first to reduce the latency which has no rule. - tetherOffloadRuleRemove(ipServer, rule); - tetherOffloadRuleAdd(ipServer, rule.onNewUpstream(newUpstreamIfindex)); - } - } - - /** - * Add upstream name to lookup table. The lookup table is used for tether stats interface name - * lookup because the netd only reports interface index in BPF tether stats but the service - * expects the interface name in NetworkStats object. - * Note that this can be only called on handler thread. - */ - public void addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface) { - if (!mIsBpfEnabled) return; - - if (upstreamIfindex == 0 || TextUtils.isEmpty(upstreamIface)) return; - - // The same interface index to name mapping may be added by different IpServer objects or - // re-added by reconnection on the same upstream interface. Ignore the duplicate one. - final String iface = mInterfaceNames.get(upstreamIfindex); - if (iface == null) { - mInterfaceNames.put(upstreamIfindex, upstreamIface); - } else if (!TextUtils.equals(iface, upstreamIface)) { - Log.wtf(TAG, "The upstream interface name " + upstreamIface - + " is different from the existing interface name " - + iface + " for index " + upstreamIfindex); - } - } - - /** - * Dump information. - * Block the function until all the data are dumped on the handler thread or timed-out. The - * reason is that dumpsys invokes this function on the thread of caller and the data may only - * be allowed to be accessed on the handler thread. - */ - public void dump(@NonNull IndentingPrintWriter pw) { - final ConditionVariable dumpDone = new ConditionVariable(); - mHandler.post(() -> { - pw.println("mIsBpfEnabled: " + mIsBpfEnabled); - pw.println("Polling " + (mPollingStarted ? "started" : "not started")); - pw.println("Stats provider " + (mStatsProvider != null - ? "registered" : "not registered")); - pw.println("Upstream quota: " + mInterfaceQuotas.toString()); - pw.println("Polling interval: " + getPollingInterval() + " ms"); - - pw.println("Forwarding stats:"); - pw.increaseIndent(); - if (mStats.size() == 0) { - pw.println(""); - } else { - dumpStats(pw); - } - pw.decreaseIndent(); - - pw.println("Forwarding rules:"); - pw.increaseIndent(); - if (mIpv6ForwardingRules.size() == 0) { - pw.println(""); - } else { - dumpIpv6ForwardingRules(pw); - } - pw.decreaseIndent(); - - dumpDone.open(); - }); - if (!dumpDone.block(DUMP_TIMEOUT_MS)) { - pw.println("... dump timed-out after " + DUMP_TIMEOUT_MS + "ms"); - } - } - - private void dumpStats(@NonNull IndentingPrintWriter pw) { - for (int i = 0; i < mStats.size(); i++) { - final int upstreamIfindex = mStats.keyAt(i); - final ForwardedStats stats = mStats.get(upstreamIfindex); - pw.println(String.format("%d(%s) - %s", upstreamIfindex, mInterfaceNames.get( - upstreamIfindex), stats.toString())); - } - } - - private void dumpIpv6ForwardingRules(@NonNull IndentingPrintWriter pw) { - for (Map.Entry> entry : - mIpv6ForwardingRules.entrySet()) { - IpServer ipServer = entry.getKey(); - // The rule downstream interface index is paired with the interface name from - // IpServer#interfaceName. See #startIPv6, #updateIpv6ForwardingRules in IpServer. - final String downstreamIface = ipServer.interfaceName(); - pw.println("[" + downstreamIface + "]: iif(iface) oif(iface) v6addr srcmac dstmac"); - - pw.increaseIndent(); - LinkedHashMap rules = entry.getValue(); - for (Ipv6ForwardingRule rule : rules.values()) { - final int upstreamIfindex = rule.upstreamIfindex; - pw.println(String.format("%d(%s) %d(%s) %s %s %s", upstreamIfindex, - mInterfaceNames.get(upstreamIfindex), rule.downstreamIfindex, - downstreamIface, rule.address, rule.srcMac, rule.dstMac)); - } - pw.decreaseIndent(); - } - } - - /** IPv6 forwarding rule class. */ - public static class Ipv6ForwardingRule { - public final int upstreamIfindex; - public final int downstreamIfindex; - - @NonNull - public final Inet6Address address; - @NonNull - public final MacAddress srcMac; - @NonNull - public final MacAddress dstMac; - - public Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, - @NonNull Inet6Address address, @NonNull MacAddress srcMac, - @NonNull MacAddress dstMac) { - this.upstreamIfindex = upstreamIfindex; - this.downstreamIfindex = downstreamIfIndex; - this.address = address; - this.srcMac = srcMac; - this.dstMac = dstMac; - } - - /** Return a new rule object which updates with new upstream index. */ - @NonNull - public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) { - return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac, - dstMac); - } - - /** - * Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream() - * would be error-prone due to generated stable AIDL classes not having a copy constructor. - */ - @NonNull - public TetherOffloadRuleParcel toTetherOffloadRuleParcel() { - final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel(); - parcel.inputInterfaceIndex = upstreamIfindex; - parcel.outputInterfaceIndex = downstreamIfindex; - parcel.destination = address.getAddress(); - parcel.prefixLength = 128; - parcel.srcL2Address = srcMac.toByteArray(); - parcel.dstL2Address = dstMac.toByteArray(); - return parcel; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Ipv6ForwardingRule)) return false; - Ipv6ForwardingRule that = (Ipv6ForwardingRule) o; - return this.upstreamIfindex == that.upstreamIfindex - && this.downstreamIfindex == that.downstreamIfindex - && Objects.equals(this.address, that.address) - && Objects.equals(this.srcMac, that.srcMac) - && Objects.equals(this.dstMac, that.dstMac); - } - - @Override - public int hashCode() { - // TODO: if this is ever used in production code, don't pass ifindices - // to Objects.hash() to avoid autoboxing overhead. - return Objects.hash(upstreamIfindex, downstreamIfindex, address, srcMac, dstMac); - } - } - - /** - * A BPF tethering stats provider to provide network statistics to the system. - * Note that this class' data may only be accessed on the handler thread. - */ - @VisibleForTesting - class BpfTetherStatsProvider extends NetworkStatsProvider { - // The offloaded traffic statistics per interface that has not been reported since the - // last call to pushTetherStats. Only the interfaces that were ever tethering upstreams - // and has pending tether stats delta are included in this NetworkStats object. - private NetworkStats mIfaceStats = new NetworkStats(0L, 0); - - // The same stats as above, but counts network stats per uid. - private NetworkStats mUidStats = new NetworkStats(0L, 0); - - @Override - public void onRequestStatsUpdate(int token) { - mHandler.post(() -> pushTetherStats()); - } - - @Override - public void onSetAlert(long quotaBytes) { - mHandler.post(() -> updateAlertQuota(quotaBytes)); - } - - @Override - public void onSetLimit(@NonNull String iface, long quotaBytes) { - if (quotaBytes < QUOTA_UNLIMITED) { - throw new IllegalArgumentException("invalid quota value " + quotaBytes); - } - - mHandler.post(() -> { - final Long curIfaceQuota = mInterfaceQuotas.get(iface); - - if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return; - - if (quotaBytes == QUOTA_UNLIMITED) { - mInterfaceQuotas.remove(iface); - } else { - mInterfaceQuotas.put(iface, quotaBytes); - } - maybeUpdateDataLimit(iface); - }); - } - - @VisibleForTesting - void pushTetherStats() { - try { - // The token is not used for now. See b/153606961. - notifyStatsUpdated(0 /* token */, mIfaceStats, mUidStats); - - // Clear the accumulated tether stats delta after reported. Note that create a new - // empty object because NetworkStats#clear is @hide. - mIfaceStats = new NetworkStats(0L, 0); - mUidStats = new NetworkStats(0L, 0); - } catch (RuntimeException e) { - mLog.e("Cannot report network stats: ", e); - } - } - - private void accumulateDiff(@NonNull NetworkStats ifaceDiff, - @NonNull NetworkStats uidDiff) { - mIfaceStats = mIfaceStats.add(ifaceDiff); - mUidStats = mUidStats.add(uidDiff); - } - } - - private boolean isBpfEnabled() { - final TetheringConfiguration config = mDeps.getTetherConfig(); - return (config != null) ? config.isBpfOffloadEnabled() : true /* default value */; - } - - private int getInterfaceIndexFromRules(@NonNull String ifName) { - for (LinkedHashMap rules : mIpv6ForwardingRules - .values()) { - for (Ipv6ForwardingRule rule : rules.values()) { - final int upstreamIfindex = rule.upstreamIfindex; - if (TextUtils.equals(ifName, mInterfaceNames.get(upstreamIfindex))) { - return upstreamIfindex; - } - } - } - return 0; - } - - private long getQuotaBytes(@NonNull String iface) { - final Long limit = mInterfaceQuotas.get(iface); - final long quotaBytes = (limit != null) ? limit : QUOTA_UNLIMITED; - - return quotaBytes; - } - - private boolean sendDataLimitToNetd(int ifIndex, long quotaBytes) { - if (ifIndex == 0) { - Log.wtf(TAG, "Invalid interface index."); - return false; - } - - try { - mNetd.tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Exception when updating quota " + quotaBytes + ": ", e); - return false; - } - - return true; - } - - // Handle the data limit update from the service which is the stats provider registered for. - private void maybeUpdateDataLimit(@NonNull String iface) { - // Set data limit only on a given upstream which has at least one rule. If we can't get - // an interface index for a given interface name, it means either there is no rule for - // a given upstream or the interface name is not an upstream which is monitored by the - // coordinator. - final int ifIndex = getInterfaceIndexFromRules(iface); - if (ifIndex == 0) return; - - final long quotaBytes = getQuotaBytes(iface); - sendDataLimitToNetd(ifIndex, quotaBytes); - } - - // Handle the data limit update while adding forwarding rules. - private boolean updateDataLimit(int ifIndex) { - final String iface = mInterfaceNames.get(ifIndex); - if (iface == null) { - mLog.e("Fail to get the interface name for index " + ifIndex); - return false; - } - final long quotaBytes = getQuotaBytes(iface); - return sendDataLimitToNetd(ifIndex, quotaBytes); - } - - private boolean isAnyRuleOnUpstream(int upstreamIfindex) { - for (LinkedHashMap rules : mIpv6ForwardingRules - .values()) { - for (Ipv6ForwardingRule rule : rules.values()) { - if (upstreamIfindex == rule.upstreamIfindex) return true; - } - } - return false; - } - - @NonNull - private NetworkStats buildNetworkStats(@NonNull StatsType type, int ifIndex, - @NonNull final ForwardedStats diff) { - NetworkStats stats = new NetworkStats(0L, 0); - final String iface = mInterfaceNames.get(ifIndex); - if (iface == null) { - // TODO: Use Log.wtf once the coordinator owns full control of tether stats from netd. - // For now, netd may add the empty stats for the upstream which is not monitored by - // the coordinator. Silently ignore it. - return stats; - } - final int uid = (type == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL; - // Note that the argument 'metered', 'roaming' and 'defaultNetwork' are not recorded for - // network stats snapshot. See NetworkStatsRecorder#recordSnapshotLocked. - return stats.addEntry(new Entry(iface, uid, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, diff.rxBytes, diff.rxPackets, - diff.txBytes, diff.txPackets, 0L /* operations */)); - } - - private void updateAlertQuota(long newQuota) { - if (newQuota < QUOTA_UNLIMITED) { - throw new IllegalArgumentException("invalid quota value " + newQuota); - } - if (mRemainingAlertQuota == newQuota) return; - - mRemainingAlertQuota = newQuota; - if (mRemainingAlertQuota == 0) { - mLog.i("onAlertReached"); - if (mStatsProvider != null) mStatsProvider.notifyAlertReached(); - } - } - - private void updateQuotaAndStatsFromSnapshot( - @NonNull final TetherStatsParcel[] tetherStatsList) { - long usedAlertQuota = 0; - for (TetherStatsParcel tetherStats : tetherStatsList) { - final Integer ifIndex = tetherStats.ifIndex; - final ForwardedStats curr = new ForwardedStats(tetherStats); - final ForwardedStats base = mStats.get(ifIndex); - final ForwardedStats diff = (base != null) ? curr.subtract(base) : curr; - usedAlertQuota += diff.rxBytes + diff.txBytes; - - // Update the local cache for counting tether stats delta. - mStats.put(ifIndex, curr); - - // Update the accumulated tether stats delta to the stats provider for the service - // querying. - if (mStatsProvider != null) { - try { - mStatsProvider.accumulateDiff( - buildNetworkStats(StatsType.STATS_PER_IFACE, ifIndex, diff), - buildNetworkStats(StatsType.STATS_PER_UID, ifIndex, diff)); - } catch (ArrayIndexOutOfBoundsException e) { - Log.wtf(TAG, "Fail to update the accumulated stats delta for interface index " - + ifIndex + " : ", e); - } - } - } - - if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) { - // Trim to zero if overshoot. - final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0); - updateAlertQuota(newQuota); - } - - // TODO: Count the used limit quota for notifying data limit reached. - } - - private void updateForwardedStatsFromNetd() { - final TetherStatsParcel[] tetherStatsList; - try { - // The reported tether stats are total data usage for all currently-active upstream - // interfaces since tethering start. - tetherStatsList = mNetd.tetherOffloadGetStats(); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Problem fetching tethering stats: ", e); - return; - } - updateQuotaAndStatsFromSnapshot(tetherStatsList); - } - - @VisibleForTesting - int getPollingInterval() { - // The valid range of interval is DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long. - // Ignore the config value is less than the minimum polling interval. Note that the - // minimum interval definition is invoked as OffloadController#isPollingStatsNeeded does. - // TODO: Perhaps define a minimum polling interval constant. - final TetheringConfiguration config = mDeps.getTetherConfig(); - final int configInterval = (config != null) ? config.getOffloadPollInterval() : 0; - return Math.max(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, configInterval); - } - - private void maybeSchedulePollingStats() { - if (!mPollingStarted) return; - - if (mHandler.hasCallbacks(mScheduledPollingTask)) { - mHandler.removeCallbacks(mScheduledPollingTask); - } - - mHandler.postDelayed(mScheduledPollingTask, getPollingInterval()); - } - - // Return forwarding rule map. This is used for testing only. - // Note that this can be only called on handler thread. - @NonNull - @VisibleForTesting - final HashMap> - getForwardingRulesForTesting() { - return mIpv6ForwardingRules; - } - - // Return upstream interface name map. This is used for testing only. - // Note that this can be only called on handler thread. - @NonNull - @VisibleForTesting - final SparseArray getInterfaceNamesForTesting() { - return mInterfaceNames; - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java b/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java deleted file mode 100644 index 8a96988ae1d1..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.TetheringManager.TETHERING_WIFI; - -import android.net.MacAddress; -import android.net.TetheredClient; -import android.net.TetheredClient.AddressInfo; -import android.net.ip.IpServer; -import android.net.wifi.WifiClient; -import android.os.SystemClock; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Tracker for clients connected to downstreams. - * - *

This class is not thread safe, it is intended to be used only from the tethering handler - * thread. - */ -public class ConnectedClientsTracker { - private final Clock mClock; - - @NonNull - private List mLastWifiClients = Collections.emptyList(); - @NonNull - private List mLastTetheredClients = Collections.emptyList(); - - @VisibleForTesting - static class Clock { - public long elapsedRealtime() { - return SystemClock.elapsedRealtime(); - } - } - - public ConnectedClientsTracker() { - this(new Clock()); - } - - @VisibleForTesting - ConnectedClientsTracker(Clock clock) { - mClock = clock; - } - - /** - * Update the tracker with new connected clients. - * - *

The new list can be obtained through {@link #getLastTetheredClients()}. - * @param ipServers The IpServers used to assign addresses to clients. - * @param wifiClients The list of L2-connected WiFi clients. Null for no change since last - * update. - * @return True if the list of clients changed since the last calculation. - */ - public boolean updateConnectedClients( - Iterable ipServers, @Nullable List wifiClients) { - final long now = mClock.elapsedRealtime(); - - if (wifiClients != null) { - mLastWifiClients = wifiClients; - } - final Set wifiClientMacs = getClientMacs(mLastWifiClients); - - // Build the list of non-expired leases from all IpServers, grouped by mac address - final Map clientsMap = new HashMap<>(); - for (IpServer server : ipServers) { - for (TetheredClient client : server.getAllLeases()) { - if (client.getTetheringType() == TETHERING_WIFI - && !wifiClientMacs.contains(client.getMacAddress())) { - // Skip leases of WiFi clients that are not (or no longer) L2-connected - continue; - } - final TetheredClient prunedClient = pruneExpired(client, now); - if (prunedClient == null) continue; // All addresses expired - - addLease(clientsMap, prunedClient); - } - } - - // TODO: add IPv6 addresses from netlink - - // Add connected WiFi clients that do not have any known address - for (MacAddress client : wifiClientMacs) { - if (clientsMap.containsKey(client)) continue; - clientsMap.put(client, new TetheredClient( - client, Collections.emptyList() /* addresses */, TETHERING_WIFI)); - } - - final HashSet clients = new HashSet<>(clientsMap.values()); - final boolean clientsChanged = clients.size() != mLastTetheredClients.size() - || !clients.containsAll(mLastTetheredClients); - mLastTetheredClients = Collections.unmodifiableList(new ArrayList<>(clients)); - return clientsChanged; - } - - private static void addLease(Map clientsMap, TetheredClient lease) { - final TetheredClient aggregateClient = clientsMap.getOrDefault( - lease.getMacAddress(), lease); - if (aggregateClient == lease) { - // This is the first lease with this mac address - clientsMap.put(lease.getMacAddress(), lease); - return; - } - - // Only add the address info; this assumes that the tethering type is the same when the mac - // address is the same. If a client is connected through different tethering types with the - // same mac address, connected clients callbacks will report all of its addresses under only - // one of these tethering types. This keeps the API simple considering that such a scenario - // would really be a rare edge case. - clientsMap.put(lease.getMacAddress(), aggregateClient.addAddresses(lease)); - } - - /** - * Get the last list of tethered clients, as calculated in {@link #updateConnectedClients}. - * - *

The returned list is immutable. - */ - @NonNull - public List getLastTetheredClients() { - return mLastTetheredClients; - } - - private static boolean hasExpiredAddress(List addresses, long now) { - for (AddressInfo info : addresses) { - if (info.getExpirationTime() <= now) { - return true; - } - } - return false; - } - - @Nullable - private static TetheredClient pruneExpired(TetheredClient client, long now) { - final List addresses = client.getAddresses(); - if (addresses.size() == 0) return null; - if (!hasExpiredAddress(addresses, now)) return client; - - final ArrayList newAddrs = new ArrayList<>(addresses.size() - 1); - for (AddressInfo info : addresses) { - if (info.getExpirationTime() > now) { - newAddrs.add(info); - } - } - - if (newAddrs.size() == 0) { - return null; - } - return new TetheredClient(client.getMacAddress(), newAddrs, client.getTetheringType()); - } - - @NonNull - private static Set getClientMacs(@NonNull List clients) { - final Set macs = new HashSet<>(clients.size()); - for (WifiClient c : clients) { - macs.add(c.getMacAddress()); - } - return macs; - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java deleted file mode 100644 index bb7322f2a0d2..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ /dev/null @@ -1,646 +0,0 @@ -/* - * 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.networkstack.tethering; - -import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; -import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; -import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; -import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; -import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; -import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; -import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_INVALID; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.util.SharedLog; -import android.os.Bundle; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.Parcel; -import android.os.PersistableBundle; -import android.os.ResultReceiver; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.provider.Settings; -import android.telephony.CarrierConfigManager; -import android.util.SparseIntArray; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.PrintWriter; -import java.util.BitSet; - -/** - * Re-check tethering provisioning for enabled downstream tether types. - * Reference TetheringManager.TETHERING_{@code *} for each tether type. - * - * All methods of this class must be accessed from the thread of tethering - * state machine. - * @hide - */ -public class EntitlementManager { - private static final String TAG = EntitlementManager.class.getSimpleName(); - private static final boolean DBG = false; - - @VisibleForTesting - protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; - private static final String ACTION_PROVISIONING_ALARM = - "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; - - private final ComponentName mSilentProvisioningService; - private static final int MS_PER_HOUR = 60 * 60 * 1000; - private static final int DUMP_TIMEOUT = 10_000; - - // The BitSet is the bit map of each enabled downstream types, ex: - // {@link TetheringManager.TETHERING_WIFI} - // {@link TetheringManager.TETHERING_USB} - // {@link TetheringManager.TETHERING_BLUETOOTH} - private final BitSet mCurrentDownstreams; - private final BitSet mExemptedDownstreams; - private final Context mContext; - private final SharedLog mLog; - private final SparseIntArray mEntitlementCacheValue; - private final Handler mHandler; - // Key: TetheringManager.TETHERING_*(downstream). - // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result). - private final SparseIntArray mCurrentEntitlementResults; - private final Runnable mPermissionChangeCallback; - private PendingIntent mProvisioningRecheckAlarm; - private boolean mLastCellularUpstreamPermitted = true; - private boolean mUsingCellularAsUpstream = false; - private boolean mNeedReRunProvisioningUi = false; - private OnUiEntitlementFailedListener mListener; - private TetheringConfigurationFetcher mFetcher; - - public EntitlementManager(Context ctx, Handler h, SharedLog log, - Runnable callback) { - mContext = ctx; - mLog = log.forSubComponent(TAG); - mCurrentDownstreams = new BitSet(); - mExemptedDownstreams = new BitSet(); - mCurrentEntitlementResults = new SparseIntArray(); - mEntitlementCacheValue = new SparseIntArray(); - mPermissionChangeCallback = callback; - mHandler = h; - mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM), - null, mHandler); - mSilentProvisioningService = ComponentName.unflattenFromString( - mContext.getResources().getString(R.string.config_wifi_tether_enable)); - } - - public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) { - mListener = listener; - } - - /** Callback fired when UI entitlement failed. */ - public interface OnUiEntitlementFailedListener { - /** - * Ui entitlement check fails in |downstream|. - * - * @param downstream tethering type from TetheringManager.TETHERING_{@code *}. - */ - void onUiEntitlementFailed(int downstream); - } - - public void setTetheringConfigurationFetcher(final TetheringConfigurationFetcher fetcher) { - mFetcher = fetcher; - } - - /** Interface to fetch TetheringConfiguration. */ - public interface TetheringConfigurationFetcher { - /** - * Fetch current tethering configuration. This will be called to ensure whether entitlement - * check is needed. - * @return TetheringConfiguration instance. - */ - TetheringConfiguration fetchTetheringConfiguration(); - } - - /** - * Check if cellular upstream is permitted. - */ - public boolean isCellularUpstreamPermitted() { - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - - return isCellularUpstreamPermitted(config); - } - - private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) { - if (!isTetherProvisioningRequired(config)) return true; - - // If provisioning is required and EntitlementManager doesn't know any downstreams, cellular - // upstream should not be enabled. Enable cellular upstream for exempted downstreams only - // when there is no non-exempted downstream. - if (mCurrentDownstreams.isEmpty()) return !mExemptedDownstreams.isEmpty(); - - return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1; - } - - /** - * Set exempted downstream type. If there is only exempted downstream type active, - * corresponding entitlement check will not be run and cellular upstream will be permitted - * by default. If a privileged app enables tethering without a provisioning check, and then - * another app enables tethering of the same type but does not disable the provisioning check, - * then the downstream immediately loses exempt status and a provisioning check is run. - * If any non-exempted downstream type is active, the cellular upstream will be gated by the - * result of entitlement check from non-exempted downstreams. If entitlement check is still - * in progress on non-exempt downstreams, ceullar upstream would default be disabled. When any - * non-exempted downstream gets positive entitlement result, ceullar upstream will be enabled. - */ - public void setExemptedDownstreamType(final int type) { - mExemptedDownstreams.set(type, true); - } - - /** - * This is called when tethering starts. - * Launch provisioning app if upstream is cellular. - * - * @param downstreamType tethering type from TetheringManager.TETHERING_{@code *} - * @param showProvisioningUi a boolean indicating whether to show the - * provisioning app UI if there is one. - */ - public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) { - if (!isValidDownstreamType(downstreamType)) return; - - mCurrentDownstreams.set(downstreamType, true); - - mExemptedDownstreams.set(downstreamType, false); - - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - if (!isTetherProvisioningRequired(config)) return; - - // If upstream is not cellular, provisioning app would not be launched - // till upstream change to cellular. - if (mUsingCellularAsUpstream) { - if (showProvisioningUi) { - runUiTetherProvisioning(downstreamType, config); - } else { - runSilentTetherProvisioning(downstreamType, config); - } - mNeedReRunProvisioningUi = false; - } else { - mNeedReRunProvisioningUi |= showProvisioningUi; - } - } - - /** - * Tell EntitlementManager that a given type of tethering has been disabled - * - * @param type tethering type from TetheringManager.TETHERING_{@code *} - */ - public void stopProvisioningIfNeeded(int downstreamType) { - if (!isValidDownstreamType(downstreamType)) return; - - mCurrentDownstreams.set(downstreamType, false); - // There are lurking bugs where the notion of "provisioning required" or - // "tethering supported" may change without without tethering being notified properly. - // Remove the mapping all the time no matter provisioning is required or not. - removeDownstreamMapping(downstreamType); - mExemptedDownstreams.set(downstreamType, false); - } - - /** - * Notify EntitlementManager if upstream is cellular or not. - * - * @param isCellular whether tethering upstream is cellular. - */ - public void notifyUpstream(boolean isCellular) { - if (DBG) { - mLog.i("notifyUpstream: " + isCellular - + ", mLastCellularUpstreamPermitted: " + mLastCellularUpstreamPermitted - + ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi); - } - mUsingCellularAsUpstream = isCellular; - - if (mUsingCellularAsUpstream) { - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - maybeRunProvisioning(config); - } - } - - /** Run provisioning if needed */ - public void maybeRunProvisioning() { - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - maybeRunProvisioning(config); - } - - private void maybeRunProvisioning(final TetheringConfiguration config) { - if (mCurrentDownstreams.isEmpty() || !isTetherProvisioningRequired(config)) { - return; - } - - // Whenever any entitlement value changes, all downstreams will re-evaluate whether they - // are allowed. Therefore even if the silent check here ends in a failure and the UI later - // yields success, then the downstream that got a failure will re-evaluate as a result of - // the change and get the new correct value. - for (int downstream = mCurrentDownstreams.nextSetBit(0); downstream >= 0; - downstream = mCurrentDownstreams.nextSetBit(downstream + 1)) { - if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) { - if (mNeedReRunProvisioningUi) { - mNeedReRunProvisioningUi = false; - runUiTetherProvisioning(downstream, config); - } else { - runSilentTetherProvisioning(downstream, config); - } - } - } - } - - /** - * Check if the device requires a provisioning check in order to enable tethering. - * - * @param config an object that encapsulates the various tethering configuration elements. - * @return a boolean - {@code true} indicating tether provisioning is required by the carrier. - */ - @VisibleForTesting - protected boolean isTetherProvisioningRequired(final TetheringConfiguration config) { - if (SystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false) - || config.provisioningApp.length == 0) { - return false; - } - if (carrierConfigAffirmsEntitlementCheckNotRequired(config)) { - return false; - } - return (config.provisioningApp.length == 2); - } - - /** - * Re-check tethering provisioning for all enabled tether types. - * Reference TetheringManager.TETHERING_{@code *} for each tether type. - * - * @param config an object that encapsulates the various tethering configuration elements. - * Note: this method is only called from @{link Tethering.TetherMainSM} on the handler thread. - * If there are new callers from different threads, the logic should move to - * @{link Tethering.TetherMainSM} handler to avoid race conditions. - */ - public void reevaluateSimCardProvisioning(final TetheringConfiguration config) { - if (DBG) mLog.i("reevaluateSimCardProvisioning"); - - if (!mHandler.getLooper().isCurrentThread()) { - // Except for test, this log should not appear in normal flow. - mLog.log("reevaluateSimCardProvisioning() don't run in TetherMainSM thread"); - } - mEntitlementCacheValue.clear(); - mCurrentEntitlementResults.clear(); - - // TODO: refine provisioning check to isTetherProvisioningRequired() ?? - if (!config.hasMobileHotspotProvisionApp() - || carrierConfigAffirmsEntitlementCheckNotRequired(config)) { - evaluateCellularPermission(config); - return; - } - - if (mUsingCellularAsUpstream) { - maybeRunProvisioning(config); - } - } - - /** - * Get carrier configuration bundle. - * @param config an object that encapsulates the various tethering configuration elements. - * */ - public PersistableBundle getCarrierConfig(final TetheringConfiguration config) { - final CarrierConfigManager configManager = (CarrierConfigManager) mContext - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager == null) return null; - - final PersistableBundle carrierConfig = configManager.getConfigForSubId( - config.activeDataSubId); - - if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) { - return carrierConfig; - } - - return null; - } - - // The logic here is aimed solely at confirming that a CarrierConfig exists - // and affirms that entitlement checks are not required. - // - // TODO: find a better way to express this, or alter the checking process - // entirely so that this is more intuitive. - private boolean carrierConfigAffirmsEntitlementCheckNotRequired( - final TetheringConfiguration config) { - // Check carrier config for entitlement checks - final PersistableBundle carrierConfig = getCarrierConfig(config); - if (carrierConfig == null) return false; - - // A CarrierConfigManager was found and it has a config. - final boolean isEntitlementCheckRequired = carrierConfig.getBoolean( - CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL); - return !isEntitlementCheckRequired; - } - - /** - * Run no UI tethering provisioning check. - * @param type tethering type from TetheringManager.TETHERING_{@code *} - * @param subId default data subscription ID. - */ - @VisibleForTesting - protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) { - if (DBG) mLog.i("runSilentTetherProvisioning: " + type); - // For silent provisioning, settings would stop tethering when entitlement fail. - ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null); - - Intent intent = new Intent(); - intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); - intent.putExtra(EXTRA_RUN_PROVISION, true); - intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi); - intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse); - intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); - intent.setComponent(mSilentProvisioningService); - // Only admin user can change tethering and SilentTetherProvisioning don't need to - // show UI, it is fine to always start setting's background service as system user. - mContext.startService(intent); - return intent; - } - - private void runUiTetherProvisioning(int type, final TetheringConfiguration config) { - ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null); - runUiTetherProvisioning(type, config, receiver); - } - - /** - * Run the UI-enabled tethering provisioning check. - * @param type tethering type from TetheringManager.TETHERING_{@code *} - * @param subId default data subscription ID. - * @param receiver to receive entitlement check result. - */ - @VisibleForTesting - protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config, - ResultReceiver receiver) { - if (DBG) mLog.i("runUiTetherProvisioning: " + type); - - Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI); - intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); - intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp); - intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - // Only launch entitlement UI for system user. Entitlement UI should not appear for other - // user because only admin user is allowed to change tethering. - mContext.startActivity(intent); - return intent; - } - - // Not needed to check if this don't run on the handler thread because it's private. - private void scheduleProvisioningRechecks(final TetheringConfiguration config) { - if (mProvisioningRecheckAlarm == null) { - final int period = config.provisioningCheckPeriod; - if (period <= 0) return; - - Intent intent = new Intent(ACTION_PROVISIONING_ALARM); - mProvisioningRecheckAlarm = PendingIntent.getBroadcast(mContext, 0, intent, 0); - AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( - Context.ALARM_SERVICE); - long periodMs = period * MS_PER_HOUR; - long firstAlarmTime = SystemClock.elapsedRealtime() + periodMs; - alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, firstAlarmTime, periodMs, - mProvisioningRecheckAlarm); - } - } - - private void cancelTetherProvisioningRechecks() { - if (mProvisioningRecheckAlarm != null) { - AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( - Context.ALARM_SERVICE); - alarmManager.cancel(mProvisioningRecheckAlarm); - mProvisioningRecheckAlarm = null; - } - } - - private void evaluateCellularPermission(final TetheringConfiguration config) { - final boolean permitted = isCellularUpstreamPermitted(config); - - if (DBG) { - mLog.i("Cellular permission change from " + mLastCellularUpstreamPermitted - + " to " + permitted); - } - - if (mLastCellularUpstreamPermitted != permitted) { - mLog.log("Cellular permission change: " + permitted); - mPermissionChangeCallback.run(); - } - // Only schedule periodic re-check when tether is provisioned - // and the result is ok. - if (permitted && mCurrentEntitlementResults.size() > 0) { - scheduleProvisioningRechecks(config); - } else { - cancelTetherProvisioningRechecks(); - } - mLastCellularUpstreamPermitted = permitted; - } - - /** - * Add the mapping between provisioning result and tethering type. - * Notify UpstreamNetworkMonitor if Cellular permission changes. - * - * @param type tethering type from TetheringManager.TETHERING_{@code *} - * @param resultCode Provisioning result - */ - protected void addDownstreamMapping(int type, int resultCode) { - mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode - + " ,TetherTypeRequested: " + mCurrentDownstreams.get(type)); - if (!mCurrentDownstreams.get(type)) return; - - mCurrentEntitlementResults.put(type, resultCode); - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - evaluateCellularPermission(config); - } - - /** - * Remove the mapping for input tethering type. - * @param type tethering type from TetheringManager.TETHERING_{@code *} - */ - protected void removeDownstreamMapping(int type) { - mLog.i("removeDownstreamMapping: " + type); - mCurrentEntitlementResults.delete(type); - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - evaluateCellularPermission(config); - } - - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (ACTION_PROVISIONING_ALARM.equals(intent.getAction())) { - mLog.log("Received provisioning alarm"); - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - reevaluateSimCardProvisioning(config); - } - } - }; - - private static boolean isValidDownstreamType(int type) { - switch (type) { - case TETHERING_BLUETOOTH: - case TETHERING_ETHERNET: - case TETHERING_USB: - case TETHERING_WIFI: - return true; - default: - return false; - } - } - - /** - * Dump the infromation of EntitlementManager. - * @param pw {@link PrintWriter} is used to print formatted - */ - public void dump(PrintWriter pw) { - final ConditionVariable mWaiting = new ConditionVariable(); - mHandler.post(() -> { - pw.print("isCellularUpstreamPermitted: "); - pw.println(isCellularUpstreamPermitted()); - for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0; - type = mCurrentDownstreams.nextSetBit(type + 1)) { - pw.print("Type: "); - pw.print(typeString(type)); - if (mCurrentEntitlementResults.indexOfKey(type) > -1) { - pw.print(", Value: "); - pw.println(errorString(mCurrentEntitlementResults.get(type))); - } else { - pw.println(", Value: empty"); - } - } - mWaiting.open(); - }); - if (!mWaiting.block(DUMP_TIMEOUT)) { - pw.println("... dump timed out after " + DUMP_TIMEOUT + "ms"); - } - pw.print("Exempted: ["); - for (int type = mExemptedDownstreams.nextSetBit(0); type >= 0; - type = mExemptedDownstreams.nextSetBit(type + 1)) { - pw.print(typeString(type)); - pw.print(", "); - } - pw.println("]"); - } - - private static String typeString(int type) { - switch (type) { - case TETHERING_BLUETOOTH: return "TETHERING_BLUETOOTH"; - case TETHERING_INVALID: return "TETHERING_INVALID"; - case TETHERING_USB: return "TETHERING_USB"; - case TETHERING_WIFI: return "TETHERING_WIFI"; - default: - return String.format("TETHERING UNKNOWN TYPE (%d)", type); - } - } - - private static String errorString(int value) { - switch (value) { - case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN"; - case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR"; - case TETHER_ERROR_PROVISIONING_FAILED: return "TETHER_ERROR_PROVISIONING_FAILED"; - default: - return String.format("UNKNOWN ERROR (%d)", value); - } - } - - private ResultReceiver buildProxyReceiver(int type, boolean notifyFail, - final ResultReceiver receiver) { - ResultReceiver rr = new ResultReceiver(mHandler) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - int updatedCacheValue = updateEntitlementCacheValue(type, resultCode); - addDownstreamMapping(type, updatedCacheValue); - if (updatedCacheValue == TETHER_ERROR_PROVISIONING_FAILED && notifyFail) { - mListener.onUiEntitlementFailed(type); - } - if (receiver != null) receiver.send(updatedCacheValue, null); - } - }; - - return writeToParcel(rr); - } - - // Instances of ResultReceiver need to be public classes for remote processes to be able - // to load them (otherwise, ClassNotFoundException). For private classes, this method - // performs a trick : round-trip parceling any instance of ResultReceiver will return a - // vanilla instance of ResultReceiver sharing the binder token with the original receiver. - // The binder token has a reference to the original instance of the private class and will - // still call its methods, and can be sent over. However it cannot be used for anything - // else than sending over a Binder call. - // While round-trip parceling is not great, there is currently no other way of generating - // a vanilla instance of ResultReceiver because all its fields are private. - private ResultReceiver writeToParcel(final ResultReceiver receiver) { - Parcel parcel = Parcel.obtain(); - receiver.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel); - parcel.recycle(); - return receiverForSending; - } - - /** - * Update the last entitlement value to internal cache - * - * @param type tethering type from TetheringManager.TETHERING_{@code *} - * @param resultCode last entitlement value - * @return the last updated entitlement value - */ - private int updateEntitlementCacheValue(int type, int resultCode) { - if (DBG) { - mLog.i("updateEntitlementCacheValue: " + type + ", result: " + resultCode); - } - if (resultCode == TETHER_ERROR_NO_ERROR) { - mEntitlementCacheValue.put(type, resultCode); - return resultCode; - } else { - mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISIONING_FAILED); - return TETHER_ERROR_PROVISIONING_FAILED; - } - } - - /** Get the last value of the tethering entitlement check. */ - public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver, - boolean showEntitlementUi) { - if (!isValidDownstreamType(downstream)) { - receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null); - return; - } - - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - if (!isTetherProvisioningRequired(config)) { - receiver.send(TETHER_ERROR_NO_ERROR, null); - return; - } - - final int cacheValue = mEntitlementCacheValue.get( - downstream, TETHER_ERROR_ENTITLEMENT_UNKNOWN); - if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) { - receiver.send(cacheValue, null); - } else { - ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); - runUiTetherProvisioning(downstream, config, proxy); - } - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java deleted file mode 100644 index f3dcaa2529e7..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.RouteInfo; -import android.net.ip.IpServer; -import android.net.util.NetworkConstants; -import android.net.util.SharedLog; -import android.util.Log; - -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.Random; - - -/** - * IPv6 tethering is rather different from IPv4 owing to the absence of NAT. - * This coordinator is responsible for evaluating the dedicated prefixes - * assigned to the device and deciding how to divvy them up among downstream - * interfaces. - * - * @hide - */ -public class IPv6TetheringCoordinator { - private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName(); - private static final boolean DBG = false; - private static final boolean VDBG = false; - - private static class Downstream { - public final IpServer ipServer; - public final int mode; // IpServer.STATE_* - // Used to append to a ULA /48, constructing a ULA /64 for local use. - public final short subnetId; - - Downstream(IpServer ipServer, int mode, short subnetId) { - this.ipServer = ipServer; - this.mode = mode; - this.subnetId = subnetId; - } - } - - private final ArrayList mNotifyList; - private final SharedLog mLog; - // NOTE: mActiveDownstreams is a list and not a hash data structure because - // we keep active downstreams in arrival order. This is done so /64s can - // be parceled out on a "first come, first served" basis and a /64 used by - // a downstream that is no longer active can be redistributed to any next - // waiting active downstream (again, in arrival order). - private final LinkedList mActiveDownstreams; - private final byte[] mUniqueLocalPrefix; - private short mNextSubnetId; - private UpstreamNetworkState mUpstreamNetworkState; - - public IPv6TetheringCoordinator(ArrayList notifyList, SharedLog log) { - mNotifyList = notifyList; - mLog = log.forSubComponent(TAG); - mActiveDownstreams = new LinkedList<>(); - mUniqueLocalPrefix = generateUniqueLocalPrefix(); - mNextSubnetId = 0; - } - - /** Add active downstream to ipv6 tethering candidate list. */ - public void addActiveDownstream(IpServer downstream, int mode) { - if (findDownstream(downstream) == null) { - // Adding a new downstream appends it to the list. Adding a - // downstream a second time without first removing it has no effect. - // We never change the mode of a downstream except by first removing - // it and then re-adding it (with its new mode specified); - if (mActiveDownstreams.offer(new Downstream(downstream, mode, mNextSubnetId))) { - // Make sure subnet IDs are always positive. They are appended - // to a ULA /48 to make a ULA /64 for local use. - mNextSubnetId = (short) Math.max(0, mNextSubnetId + 1); - } - updateIPv6TetheringInterfaces(); - } - } - - /** Remove downstream from ipv6 tethering candidate list. */ - public void removeActiveDownstream(IpServer downstream) { - stopIPv6TetheringOn(downstream); - if (mActiveDownstreams.remove(findDownstream(downstream))) { - updateIPv6TetheringInterfaces(); - } - - // When tethering is stopping we can reset the subnet counter. - if (mNotifyList.isEmpty()) { - if (!mActiveDownstreams.isEmpty()) { - Log.wtf(TAG, "Tethering notify list empty, IPv6 downstreams non-empty."); - } - mNextSubnetId = 0; - } - } - - /** - * Call when UpstreamNetworkState may be changed. - * If upstream has ipv6 for tethering, update this new UpstreamNetworkState - * to IpServer. Otherwise stop ipv6 tethering on downstream interfaces. - */ - public void updateUpstreamNetworkState(UpstreamNetworkState ns) { - if (VDBG) { - Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns)); - } - if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) { - stopIPv6TetheringOnAllInterfaces(); - setUpstreamNetworkState(null); - return; - } - - if (mUpstreamNetworkState != null - && !ns.network.equals(mUpstreamNetworkState.network)) { - stopIPv6TetheringOnAllInterfaces(); - } - - setUpstreamNetworkState(ns); - updateIPv6TetheringInterfaces(); - } - - private void stopIPv6TetheringOnAllInterfaces() { - for (IpServer ipServer : mNotifyList) { - stopIPv6TetheringOn(ipServer); - } - } - - private void setUpstreamNetworkState(UpstreamNetworkState ns) { - if (ns == null) { - mUpstreamNetworkState = null; - } else { - // Make a deep copy of the parts we need. - mUpstreamNetworkState = new UpstreamNetworkState( - new LinkProperties(ns.linkProperties), - new NetworkCapabilities(ns.networkCapabilities), - new Network(ns.network)); - } - - mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState)); - } - - private void updateIPv6TetheringInterfaces() { - for (IpServer ipServer : mNotifyList) { - final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer); - ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, getTtlAdjustment(), 0, lp); - break; - } - } - - private int getTtlAdjustment() { - if (mUpstreamNetworkState == null || mUpstreamNetworkState.networkCapabilities == null) { - return 0; - } - - // If upstream is cellular, set the TTL in Router Advertisements to "network-set TTL" - 1 - // for carrier requirement. - if (mUpstreamNetworkState.networkCapabilities.hasTransport( - NetworkCapabilities.TRANSPORT_CELLULAR)) { - return -1; - } - - // For other non-cellular upstream, set TTL as "network-set TTL" + 1 to preventing arbitrary - // distinction between tethered and untethered traffic. - return 1; - } - - private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) { - final Downstream ds = findDownstream(ipServer); - if (ds == null) return null; - - if (ds.mode == IpServer.STATE_LOCAL_ONLY) { - // Build a Unique Locally-assigned Prefix configuration. - return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId); - } - - // This downstream is in IpServer.STATE_TETHERED mode. - if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) { - return null; - } - - // NOTE: Here, in future, we would have policies to decide how to divvy - // up the available dedicated prefixes among downstream interfaces. - // At this time we have no such mechanism--we only support tethering - // IPv6 toward the oldest (first requested) active downstream. - - final Downstream currentActive = mActiveDownstreams.peek(); - if (currentActive != null && currentActive.ipServer == ipServer) { - final LinkProperties lp = getIPv6OnlyLinkProperties( - mUpstreamNetworkState.linkProperties); - if (lp.hasIpv6DefaultRoute() && lp.hasGlobalIpv6Address()) { - return lp; - } - } - - return null; - } - - Downstream findDownstream(IpServer ipServer) { - for (Downstream ds : mActiveDownstreams) { - if (ds.ipServer == ipServer) return ds; - } - return null; - } - - private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) { - final LinkProperties v6only = new LinkProperties(); - if (lp == null) { - return v6only; - } - - // NOTE: At this time we don't copy over any information about any - // stacked links. No current stacked link configuration has IPv6. - - v6only.setInterfaceName(lp.getInterfaceName()); - - v6only.setMtu(lp.getMtu()); - - for (LinkAddress linkAddr : lp.getLinkAddresses()) { - if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) { - v6only.addLinkAddress(linkAddr); - } - } - - for (RouteInfo routeInfo : lp.getRoutes()) { - final IpPrefix destination = routeInfo.getDestination(); - if ((destination.getAddress() instanceof Inet6Address) - && (destination.getPrefixLength() <= 64)) { - v6only.addRoute(routeInfo); - } - } - - for (InetAddress dnsServer : lp.getDnsServers()) { - if (isIPv6GlobalAddress(dnsServer)) { - // For now we include ULAs. - v6only.addDnsServer(dnsServer); - } - } - - v6only.setDomains(lp.getDomains()); - - return v6only; - } - - // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we - // announce our own IPv6 address as DNS server. - private static boolean isIPv6GlobalAddress(InetAddress ip) { - return (ip instanceof Inet6Address) - && !ip.isAnyLocalAddress() - && !ip.isLoopbackAddress() - && !ip.isLinkLocalAddress() - && !ip.isSiteLocalAddress() - && !ip.isMulticastAddress(); - } - - private static LinkProperties getUniqueLocalConfig(byte[] ulp, short subnetId) { - final LinkProperties lp = new LinkProperties(); - - final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48); - lp.addRoute(new RouteInfo(local48, null, null, RouteInfo.RTN_UNICAST)); - - final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64); - // Because this is a locally-generated ULA, we don't have an upstream - // address. But because the downstream IP address management code gets - // its prefix from the upstream's IP address, we create a fake one here. - lp.addLinkAddress(new LinkAddress(local64.getAddress(), 64)); - - lp.setMtu(NetworkConstants.ETHER_MTU); - return lp; - } - - private static IpPrefix makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen) { - final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length); - bytes[7] = (byte) (subnetId >> 8); - bytes[8] = (byte) subnetId; - final InetAddress addr; - try { - addr = InetAddress.getByAddress(bytes); - } catch (UnknownHostException e) { - throw new IllegalStateException("Invalid address length: " + bytes.length, e); - } - return new IpPrefix(addr, prefixlen); - } - - // Generates a Unique Locally-assigned Prefix: - // - // https://tools.ietf.org/html/rfc4193#section-3.1 - // - // The result is a /48 that can be used for local-only communications. - private static byte[] generateUniqueLocalPrefix() { - final byte[] ulp = new byte[6]; // 6 = 48bits / 8bits/byte - (new Random()).nextBytes(ulp); - - final byte[] in6addr = Arrays.copyOf(ulp, NetworkConstants.IPV6_ADDR_LEN); - in6addr[0] = (byte) 0xfd; // fc00::/7 and L=1 - - return in6addr; - } - - private static String toDebugString(UpstreamNetworkState ns) { - if (ns == null) { - return "UpstreamNetworkState{null}"; - } - return ns.toString(); - } - - private static void stopIPv6TetheringOn(IpServer ipServer) { - ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java deleted file mode 100644 index 88c77b07e7e3..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java +++ /dev/null @@ -1,811 +0,0 @@ -/* - * Copyright (C) 2017 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.networkstack.tethering; - -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStats.UID_TETHERING; -import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; -import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; - -import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.usage.NetworkStatsManager; -import android.content.ContentResolver; -import android.net.InetAddresses; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.NetworkStats; -import android.net.NetworkStats.Entry; -import android.net.RouteInfo; -import android.net.netlink.ConntrackMessage; -import android.net.netlink.NetlinkConstants; -import android.net.netlink.NetlinkSocket; -import android.net.netstats.provider.NetworkStatsProvider; -import android.net.util.SharedLog; -import android.os.Handler; -import android.provider.Settings; -import android.system.ErrnoException; -import android.system.OsConstants; -import android.text.TextUtils; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * A class to encapsulate the business logic of programming the tethering - * hardware offload interface. - * - * @hide - */ -public class OffloadController { - private static final String TAG = OffloadController.class.getSimpleName(); - private static final boolean DBG = false; - private static final String ANYIP = "0.0.0.0"; - private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); - - @VisibleForTesting - enum StatsType { - STATS_PER_IFACE, - STATS_PER_UID, - } - - private enum UpdateType { IF_NEEDED, FORCE }; - - private final Handler mHandler; - private final OffloadHardwareInterface mHwInterface; - private final ContentResolver mContentResolver; - @Nullable - private final OffloadTetheringStatsProvider mStatsProvider; - private final SharedLog mLog; - private final HashMap mDownstreams; - private boolean mConfigInitialized; - private boolean mControlInitialized; - private LinkProperties mUpstreamLinkProperties; - // The complete set of offload-exempt prefixes passed in via Tethering from - // all upstream and downstream sources. - private Set mExemptPrefixes; - // A strictly "smaller" set of prefixes, wherein offload-approved prefixes - // (e.g. downstream on-link prefixes) have been removed and replaced with - // prefixes representing only the locally-assigned IP addresses. - private Set mLastLocalPrefixStrs; - - // Maps upstream interface names to offloaded traffic statistics. - // Always contains the latest value received from the hardware for each interface, regardless of - // whether offload is currently running on that interface. - private ConcurrentHashMap mForwardedStats = - new ConcurrentHashMap<>(16, 0.75F, 1); - - // Maps upstream interface names to interface quotas. - // Always contains the latest value received from the framework for each interface, regardless - // of whether offload is currently running (or is even supported) on that interface. Only - // includes upstream interfaces that have a quota set. - private HashMap mInterfaceQuotas = new HashMap<>(); - - // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert - // quota is interface independent and global for tether offload. Note that this is only - // accessed on the handler thread and in the constructor. - private long mRemainingAlertQuota = QUOTA_UNLIMITED; - // Runnable that used to schedule the next stats poll. - private final Runnable mScheduledPollingTask = () -> { - updateStatsForCurrentUpstream(); - maybeSchedulePollingStats(); - }; - - private int mNatUpdateCallbacksReceived; - private int mNatUpdateNetlinkErrors; - - @NonNull - private final Dependencies mDeps; - - // TODO: Put more parameters in constructor into dependency object. - interface Dependencies { - @NonNull - TetheringConfiguration getTetherConfig(); - } - - public OffloadController(Handler h, OffloadHardwareInterface hwi, - ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log, - @NonNull Dependencies deps) { - mHandler = h; - mHwInterface = hwi; - mContentResolver = contentResolver; - mLog = log.forSubComponent(TAG); - mDownstreams = new HashMap<>(); - mExemptPrefixes = new HashSet<>(); - mLastLocalPrefixStrs = new HashSet<>(); - OffloadTetheringStatsProvider provider = new OffloadTetheringStatsProvider(); - try { - nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider); - } catch (RuntimeException e) { - Log.wtf(TAG, "Cannot register offload stats provider: " + e); - provider = null; - } - mStatsProvider = provider; - mDeps = deps; - } - - /** Start hardware offload. */ - public boolean start() { - if (started()) return true; - - if (isOffloadDisabled()) { - mLog.i("tethering offload disabled"); - return false; - } - - if (!mConfigInitialized) { - mConfigInitialized = mHwInterface.initOffloadConfig(); - if (!mConfigInitialized) { - mLog.i("tethering offload config not supported"); - stop(); - return false; - } - } - - mControlInitialized = mHwInterface.initOffloadControl( - // OffloadHardwareInterface guarantees that these callback - // methods are called on the handler passed to it, which is the - // same as mHandler, as coordinated by the setup in Tethering. - new OffloadHardwareInterface.ControlCallback() { - @Override - public void onStarted() { - if (!started()) return; - mLog.log("onStarted"); - } - - @Override - public void onStoppedError() { - if (!started()) return; - mLog.log("onStoppedError"); - } - - @Override - public void onStoppedUnsupported() { - if (!started()) return; - mLog.log("onStoppedUnsupported"); - // Poll for statistics and trigger a sweep of tethering - // stats by observers. This might not succeed, but it's - // worth trying anyway. We need to do this because from - // this point on we continue with software forwarding, - // and we need to synchronize stats and limits between - // software and hardware forwarding. - updateStatsForAllUpstreams(); - if (mStatsProvider != null) mStatsProvider.pushTetherStats(); - } - - @Override - public void onSupportAvailable() { - if (!started()) return; - mLog.log("onSupportAvailable"); - - // [1] Poll for statistics and trigger a sweep of stats - // by observers. We need to do this to ensure that any - // limits set take into account any software tethering - // traffic that has been happening in the meantime. - updateStatsForAllUpstreams(); - if (mStatsProvider != null) mStatsProvider.pushTetherStats(); - // [2] (Re)Push all state. - computeAndPushLocalPrefixes(UpdateType.FORCE); - pushAllDownstreamState(); - pushUpstreamParameters(null); - } - - @Override - public void onStoppedLimitReached() { - if (!started()) return; - mLog.log("onStoppedLimitReached"); - - // We cannot reliably determine on which interface the limit was reached, - // because the HAL interface does not specify it. We cannot just use the - // current upstream, because that might have changed since the time that - // the HAL queued the callback. - // TODO: rev the HAL so that it provides an interface name. - - updateStatsForCurrentUpstream(); - if (mStatsProvider != null) { - mStatsProvider.pushTetherStats(); - // Push stats to service does not cause the service react to it - // immediately. Inform the service about limit reached. - mStatsProvider.notifyLimitReached(); - } - } - - @Override - public void onNatTimeoutUpdate(int proto, - String srcAddr, int srcPort, - String dstAddr, int dstPort) { - if (!started()) return; - updateNatTimeout(proto, srcAddr, srcPort, dstAddr, dstPort); - } - }); - - final boolean isStarted = started(); - if (!isStarted) { - mLog.i("tethering offload control not supported"); - stop(); - } else { - mLog.log("tethering offload started"); - mNatUpdateCallbacksReceived = 0; - mNatUpdateNetlinkErrors = 0; - maybeSchedulePollingStats(); - } - return isStarted; - } - - /** Stop hardware offload. */ - public void stop() { - // Completely stops tethering offload. After this method is called, it is no longer safe to - // call any HAL method, no callbacks from the hardware will be delivered, and any in-flight - // callbacks must be ignored. Offload may be started again by calling start(). - final boolean wasStarted = started(); - updateStatsForCurrentUpstream(); - mUpstreamLinkProperties = null; - mHwInterface.stopOffloadControl(); - mControlInitialized = false; - mConfigInitialized = false; - if (mHandler.hasCallbacks(mScheduledPollingTask)) { - mHandler.removeCallbacks(mScheduledPollingTask); - } - if (wasStarted) mLog.log("tethering offload stopped"); - } - - private boolean started() { - return mConfigInitialized && mControlInitialized; - } - - @VisibleForTesting - class OffloadTetheringStatsProvider extends NetworkStatsProvider { - // These stats must only ever be touched on the handler thread. - @NonNull - private NetworkStats mIfaceStats = new NetworkStats(0L, 0); - @NonNull - private NetworkStats mUidStats = new NetworkStats(0L, 0); - - /** - * A helper function that collect tether stats from local hashmap. Note that this does not - * invoke binder call. - */ - @VisibleForTesting - @NonNull - NetworkStats getTetherStats(@NonNull StatsType how) { - NetworkStats stats = new NetworkStats(0L, 0); - final int uid = (how == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL; - - for (final Map.Entry kv : mForwardedStats.entrySet()) { - final ForwardedStats value = kv.getValue(); - final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L); - stats = stats.addEntry(entry); - } - - return stats; - } - - @Override - public void onSetLimit(String iface, long quotaBytes) { - // Listen for all iface is necessary since upstream might be changed after limit - // is set. - mHandler.post(() -> { - final Long curIfaceQuota = mInterfaceQuotas.get(iface); - - // If the quota is set to unlimited, the value set to HAL is Long.MAX_VALUE, - // which is ~8.4 x 10^6 TiB, no one can actually reach it. Thus, it is not - // useful to set it multiple times. - // Otherwise, the quota needs to be updated to tell HAL to re-count from now even - // if the quota is the same as the existing one. - if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return; - - if (quotaBytes == QUOTA_UNLIMITED) { - mInterfaceQuotas.remove(iface); - } else { - mInterfaceQuotas.put(iface, quotaBytes); - } - maybeUpdateDataLimit(iface); - }); - } - - /** - * Push stats to service, but does not cause a force polling. Note that this can only be - * called on the handler thread. - */ - public void pushTetherStats() { - // TODO: remove the accumulated stats and report the diff from HAL directly. - final NetworkStats ifaceDiff = - getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats); - final NetworkStats uidDiff = - getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats); - try { - notifyStatsUpdated(0 /* token */, ifaceDiff, uidDiff); - mIfaceStats = mIfaceStats.add(ifaceDiff); - mUidStats = mUidStats.add(uidDiff); - } catch (RuntimeException e) { - mLog.e("Cannot report network stats: ", e); - } - } - - @Override - public void onRequestStatsUpdate(int token) { - // Do not attempt to update stats by querying the offload HAL - // synchronously from a different thread than the Handler thread. http://b/64771555. - mHandler.post(() -> { - updateStatsForCurrentUpstream(); - pushTetherStats(); - }); - } - - @Override - public void onSetAlert(long quotaBytes) { - // TODO: Ask offload HAL to notify alert without stopping traffic. - // Post it to handler thread since it access remaining quota bytes. - mHandler.post(() -> { - updateAlertQuota(quotaBytes); - maybeSchedulePollingStats(); - }); - } - } - - private String currentUpstreamInterface() { - return (mUpstreamLinkProperties != null) - ? mUpstreamLinkProperties.getInterfaceName() : null; - } - - private void maybeUpdateStats(String iface) { - if (TextUtils.isEmpty(iface)) { - return; - } - - // Always called on the handler thread. - // - // Use get()/put() instead of updating ForwardedStats in place because we can be called - // concurrently with getTetherStats. In combination with the guarantees provided by - // ConcurrentHashMap, this ensures that getTetherStats always gets the most recent copy of - // the stats for each interface, and does not observe partial writes where rxBytes is - // updated and txBytes is not. - ForwardedStats diff = mHwInterface.getForwardedStats(iface); - final long usedAlertQuota = diff.rxBytes + diff.txBytes; - ForwardedStats base = mForwardedStats.get(iface); - if (base != null) { - diff.add(base); - } - - // Update remaining alert quota if it is still positive. - if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) { - // Trim to zero if overshoot. - final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0); - updateAlertQuota(newQuota); - } - - mForwardedStats.put(iface, diff); - // diff is a new object, just created by getForwardedStats(). Therefore, anyone reading from - // mForwardedStats (i.e., any caller of getTetherStats) will see the new stats immediately. - } - - /** - * Update remaining alert quota, fire the {@link NetworkStatsProvider#notifyAlertReached()} - * callback when it reaches zero. This can be invoked either from service setting the alert, or - * {@code maybeUpdateStats} when updating stats. Note that this can be only called on - * handler thread. - * - * @param newQuota non-negative value to indicate the new quota, or - * {@link NetworkStatsProvider#QUOTA_UNLIMITED} to indicate there is no - * quota. - */ - private void updateAlertQuota(long newQuota) { - if (newQuota < QUOTA_UNLIMITED) { - throw new IllegalArgumentException("invalid quota value " + newQuota); - } - if (mRemainingAlertQuota == newQuota) return; - - mRemainingAlertQuota = newQuota; - if (mRemainingAlertQuota == 0) { - mLog.i("notifyAlertReached"); - if (mStatsProvider != null) mStatsProvider.notifyAlertReached(); - } - } - - /** - * Schedule polling if needed, this will be stopped if offload has been - * stopped or remaining quota reaches zero or upstream is empty. - * Note that this can be only called on handler thread. - */ - private void maybeSchedulePollingStats() { - if (!isPollingStatsNeeded()) return; - - if (mHandler.hasCallbacks(mScheduledPollingTask)) { - mHandler.removeCallbacks(mScheduledPollingTask); - } - mHandler.postDelayed(mScheduledPollingTask, - mDeps.getTetherConfig().getOffloadPollInterval()); - } - - private boolean isPollingStatsNeeded() { - return started() && mRemainingAlertQuota > 0 - && !TextUtils.isEmpty(currentUpstreamInterface()) - && mDeps.getTetherConfig() != null - && mDeps.getTetherConfig().getOffloadPollInterval() - >= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; - } - - private boolean maybeUpdateDataLimit(String iface) { - // setDataLimit may only be called while offload is occurring on this upstream. - if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) { - return true; - } - - Long limit = mInterfaceQuotas.get(iface); - if (limit == null) { - limit = Long.MAX_VALUE; - } - - return mHwInterface.setDataLimit(iface, limit); - } - - private void updateStatsForCurrentUpstream() { - maybeUpdateStats(currentUpstreamInterface()); - } - - private void updateStatsForAllUpstreams() { - // In practice, there should only ever be a single digit number of - // upstream interfaces over the lifetime of an active tethering session. - // Roughly speaking, imagine a very ambitious one or two of each of the - // following interface types: [ "rmnet_data", "wlan", "eth", "rndis" ]. - for (Map.Entry kv : mForwardedStats.entrySet()) { - maybeUpdateStats(kv.getKey()); - } - } - - /** Set current tethering upstream LinkProperties. */ - public void setUpstreamLinkProperties(LinkProperties lp) { - if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return; - - final String prevUpstream = currentUpstreamInterface(); - - mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null; - // Make sure we record this interface in the ForwardedStats map. - final String iface = currentUpstreamInterface(); - if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS); - - maybeSchedulePollingStats(); - - // TODO: examine return code and decide what to do if programming - // upstream parameters fails (probably just wait for a subsequent - // onOffloadEvent() callback to tell us offload is available again and - // then reapply all state). - computeAndPushLocalPrefixes(UpdateType.IF_NEEDED); - pushUpstreamParameters(prevUpstream); - } - - /** Set local prefixes. */ - public void setLocalPrefixes(Set localPrefixes) { - mExemptPrefixes = localPrefixes; - - if (!started()) return; - computeAndPushLocalPrefixes(UpdateType.IF_NEEDED); - } - - /** Update current downstream LinkProperties. */ - public void notifyDownstreamLinkProperties(LinkProperties lp) { - final String ifname = lp.getInterfaceName(); - final LinkProperties oldLp = mDownstreams.put(ifname, new LinkProperties(lp)); - if (Objects.equals(oldLp, lp)) return; - - if (!started()) return; - pushDownstreamState(oldLp, lp); - } - - private void pushDownstreamState(LinkProperties oldLp, LinkProperties newLp) { - final String ifname = newLp.getInterfaceName(); - final List oldRoutes = - (oldLp != null) ? oldLp.getRoutes() : Collections.EMPTY_LIST; - final List newRoutes = newLp.getRoutes(); - - // For each old route, if not in new routes: remove. - for (RouteInfo ri : oldRoutes) { - if (shouldIgnoreDownstreamRoute(ri)) continue; - if (!newRoutes.contains(ri)) { - mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString()); - } - } - - // For each new route, if not in old routes: add. - for (RouteInfo ri : newRoutes) { - if (shouldIgnoreDownstreamRoute(ri)) continue; - if (!oldRoutes.contains(ri)) { - mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString()); - } - } - } - - private void pushAllDownstreamState() { - for (LinkProperties lp : mDownstreams.values()) { - pushDownstreamState(null, lp); - } - } - - /** Remove downstream interface from offload hardware. */ - public void removeDownstreamInterface(String ifname) { - final LinkProperties lp = mDownstreams.remove(ifname); - if (lp == null) return; - - if (!started()) return; - - for (RouteInfo route : lp.getRoutes()) { - if (shouldIgnoreDownstreamRoute(route)) continue; - mHwInterface.removeDownstreamPrefix(ifname, route.getDestination().toString()); - } - } - - private boolean isOffloadDisabled() { - final int defaultDisposition = mHwInterface.getDefaultTetherOffloadDisabled(); - return (Settings.Global.getInt( - mContentResolver, TETHER_OFFLOAD_DISABLED, defaultDisposition) != 0); - } - - private boolean pushUpstreamParameters(String prevUpstream) { - final String iface = currentUpstreamInterface(); - - if (TextUtils.isEmpty(iface)) { - final boolean rval = mHwInterface.setUpstreamParameters("", ANYIP, ANYIP, null); - // Update stats after we've told the hardware to stop forwarding so - // we don't miss packets. - maybeUpdateStats(prevUpstream); - return rval; - } - - // A stacked interface cannot be an upstream for hardware offload. - // Consequently, we examine only the primary interface name, look at - // getAddresses() rather than getAllAddresses(), and check getRoutes() - // rather than getAllRoutes(). - final ArrayList v6gateways = new ArrayList<>(); - String v4addr = null; - String v4gateway = null; - - for (InetAddress ip : mUpstreamLinkProperties.getAddresses()) { - if (ip instanceof Inet4Address) { - v4addr = ip.getHostAddress(); - break; - } - } - - // Find the gateway addresses of all default routes of either address family. - for (RouteInfo ri : mUpstreamLinkProperties.getRoutes()) { - if (!ri.hasGateway()) continue; - - final String gateway = ri.getGateway().getHostAddress(); - final InetAddress address = ri.getDestination().getAddress(); - if (ri.isDefaultRoute() && address instanceof Inet4Address) { - v4gateway = gateway; - } else if (ri.isDefaultRoute() && address instanceof Inet6Address) { - v6gateways.add(gateway); - } - } - - boolean success = mHwInterface.setUpstreamParameters( - iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways)); - - if (!success) { - return success; - } - - // Update stats after we've told the hardware to change routing so we don't miss packets. - maybeUpdateStats(prevUpstream); - - // Data limits can only be set once offload is running on the upstream. - success = maybeUpdateDataLimit(iface); - if (!success) { - // If we failed to set a data limit, don't use this upstream, because we don't want to - // blow through the data limit that we were told to apply. - mLog.log("Setting data limit for " + iface + " failed, disabling offload."); - stop(); - } - - return success; - } - - private boolean computeAndPushLocalPrefixes(UpdateType how) { - final boolean force = (how == UpdateType.FORCE); - final Set localPrefixStrs = computeLocalPrefixStrings( - mExemptPrefixes, mUpstreamLinkProperties); - if (!force && mLastLocalPrefixStrs.equals(localPrefixStrs)) return true; - - mLastLocalPrefixStrs = localPrefixStrs; - return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs)); - } - - // TODO: Factor in downstream LinkProperties once that information is available. - private static Set computeLocalPrefixStrings( - Set localPrefixes, LinkProperties upstreamLinkProperties) { - // Create an editable copy. - final Set prefixSet = new HashSet<>(localPrefixes); - - // TODO: If a downstream interface (not currently passed in) is reusing - // the /64 of the upstream (64share) then: - // - // [a] remove that /64 from the local prefixes - // [b] add in /128s for IP addresses on the downstream interface - // [c] add in /128s for IP addresses on the upstream interface - // - // Until downstream information is available here, simply add /128s from - // the upstream network; they'll just be redundant with their /64. - if (upstreamLinkProperties != null) { - for (LinkAddress linkAddr : upstreamLinkProperties.getLinkAddresses()) { - if (!linkAddr.isGlobalPreferred()) continue; - final InetAddress ip = linkAddr.getAddress(); - if (!(ip instanceof Inet6Address)) continue; - prefixSet.add(new IpPrefix(ip, 128)); - } - } - - final HashSet localPrefixStrs = new HashSet<>(); - for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString()); - return localPrefixStrs; - } - - private static boolean shouldIgnoreDownstreamRoute(RouteInfo route) { - // Ignore any link-local routes. - final IpPrefix destination = route.getDestination(); - final LinkAddress linkAddr = new LinkAddress(destination.getAddress(), - destination.getPrefixLength()); - if (!linkAddr.isGlobalPreferred()) return true; - - return false; - } - - /** Dump information. */ - public void dump(IndentingPrintWriter pw) { - if (isOffloadDisabled()) { - pw.println("Offload disabled"); - return; - } - final boolean isStarted = started(); - pw.println("Offload HALs " + (isStarted ? "started" : "not started")); - LinkProperties lp = mUpstreamLinkProperties; - String upstream = (lp != null) ? lp.getInterfaceName() : null; - pw.println("Current upstream: " + upstream); - pw.println("Exempt prefixes: " + mLastLocalPrefixStrs); - pw.println("NAT timeout update callbacks received during the " - + (isStarted ? "current" : "last") - + " offload session: " - + mNatUpdateCallbacksReceived); - pw.println("NAT timeout update netlink errors during the " - + (isStarted ? "current" : "last") - + " offload session: " - + mNatUpdateNetlinkErrors); - } - - private void updateNatTimeout( - int proto, String srcAddr, int srcPort, String dstAddr, int dstPort) { - final String protoName = protoNameFor(proto); - if (protoName == null) { - mLog.e("Unknown NAT update callback protocol: " + proto); - return; - } - - final Inet4Address src = parseIPv4Address(srcAddr); - if (src == null) { - mLog.e("Failed to parse IPv4 address: " + srcAddr); - return; - } - - if (!isValidUdpOrTcpPort(srcPort)) { - mLog.e("Invalid src port: " + srcPort); - return; - } - - final Inet4Address dst = parseIPv4Address(dstAddr); - if (dst == null) { - mLog.e("Failed to parse IPv4 address: " + dstAddr); - return; - } - - if (!isValidUdpOrTcpPort(dstPort)) { - mLog.e("Invalid dst port: " + dstPort); - return; - } - - mNatUpdateCallbacksReceived++; - final String natDescription = String.format("%s (%s, %s) -> (%s, %s)", - protoName, srcAddr, srcPort, dstAddr, dstPort); - if (DBG) { - mLog.log("NAT timeout update: " + natDescription); - } - - final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto); - final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest( - proto, src, srcPort, dst, dstPort, timeoutSec); - - try { - NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg); - } catch (ErrnoException e) { - mNatUpdateNetlinkErrors++; - mLog.e("Error updating NAT conntrack entry >" + natDescription + "<: " + e - + ", msg: " + NetlinkConstants.hexify(msg)); - mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived); - mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors); - } - } - - private static Inet4Address parseIPv4Address(String addrString) { - try { - final InetAddress ip = InetAddresses.parseNumericAddress(addrString); - // TODO: Consider other sanitization steps here, including perhaps: - // not eql to 0.0.0.0 - // not within 169.254.0.0/16 - // not within ::ffff:0.0.0.0/96 - // not within ::/96 - // et cetera. - if (ip instanceof Inet4Address) { - return (Inet4Address) ip; - } - } catch (IllegalArgumentException iae) { } - return null; - } - - private static String protoNameFor(int proto) { - // OsConstants values are not constant expressions; no switch statement. - if (proto == OsConstants.IPPROTO_UDP) { - return "UDP"; - } else if (proto == OsConstants.IPPROTO_TCP) { - return "TCP"; - } - return null; - } - - private static int connectionTimeoutUpdateSecondsFor(int proto) { - // TODO: Replace this with more thoughtful work, perhaps reading from - // and maybe writing to any required - // - // /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_* - // /proc/sys/net/netfilter/nf_conntrack_udp_timeout{,_stream} - // - // entries. TBD. - if (proto == OsConstants.IPPROTO_TCP) { - // Cf. /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established - return 432000; - } else { - // Cf. /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream - return 180; - } - } - - private static boolean isValidUdpOrTcpPort(int port) { - return port > 0 && port < 65536; - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java deleted file mode 100644 index da5f25b2a596..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Copyright (C) 2017 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.networkstack.tethering; - -import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; -import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; -import static android.net.util.TetheringUtils.uint16; - -import android.annotation.NonNull; -import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; -import android.hardware.tetheroffload.control.V1_0.IOffloadControl; -import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; -import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; -import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; -import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; -import android.net.netlink.NetlinkSocket; -import android.net.netlink.StructNfGenMsg; -import android.net.netlink.StructNlMsgHdr; -import android.net.util.SharedLog; -import android.net.util.SocketUtils; -import android.os.Handler; -import android.os.NativeHandle; -import android.os.RemoteException; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.SocketAddress; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.NoSuchElementException; - - -/** - * Capture tethering dependencies, for injection. - * - * @hide - */ -public class OffloadHardwareInterface { - private static final String TAG = OffloadHardwareInterface.class.getSimpleName(); - private static final String YIELDS = " -> "; - // Change this value to control whether tether offload is enabled or - // disabled by default in the absence of an explicit Settings value. - // See accompanying unittest to distinguish 0 from non-0 values. - private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0; - private static final String NO_INTERFACE_NAME = ""; - private static final String NO_IPV4_ADDRESS = ""; - private static final String NO_IPV4_GATEWAY = ""; - // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h - public static final int NF_NETLINK_CONNTRACK_NEW = 1; - public static final int NF_NETLINK_CONNTRACK_UPDATE = 2; - public static final int NF_NETLINK_CONNTRACK_DESTROY = 4; - // Reference libnetfilter_conntrack/linux_nfnetlink_conntrack.h - public static final short NFNL_SUBSYS_CTNETLINK = 1; - public static final short IPCTNL_MSG_CT_NEW = 0; - public static final short IPCTNL_MSG_CT_GET = 1; - - private final long NETLINK_MESSAGE_TIMEOUT_MS = 500; - - private final Handler mHandler; - private final SharedLog mLog; - private final Dependencies mDeps; - private IOffloadControl mOffloadControl; - private TetheringOffloadCallback mTetheringOffloadCallback; - private ControlCallback mControlCallback; - - /** The callback to notify status of offload management process. */ - public static class ControlCallback { - /** Offload started. */ - public void onStarted() {} - /** - * Offload stopped because an error has occurred in lower layer. - */ - public void onStoppedError() {} - /** - * Offload stopped because the device has moved to a bearer on which hardware offload is - * not supported. Subsequent calls to setUpstreamParameters and add/removeDownstream will - * likely fail and cannot be presumed to be saved inside of the hardware management process. - * Upon receiving #onSupportAvailable(), the caller should reprogram the hardware to begin - * offload again. - */ - public void onStoppedUnsupported() {} - /** Indicate that offload is able to proivde support for this time. */ - public void onSupportAvailable() {} - /** Offload stopped because of usage limit reached. */ - public void onStoppedLimitReached() {} - - /** Indicate to update NAT timeout. */ - public void onNatTimeoutUpdate(int proto, - String srcAddr, int srcPort, - String dstAddr, int dstPort) {} - } - - /** The object which records Tx/Rx forwarded bytes. */ - public static class ForwardedStats { - public long rxBytes; - public long txBytes; - - public ForwardedStats() { - rxBytes = 0; - txBytes = 0; - } - - @VisibleForTesting - public ForwardedStats(long rxBytes, long txBytes) { - this.rxBytes = rxBytes; - this.txBytes = txBytes; - } - - /** Add Tx/Rx bytes. */ - public void add(ForwardedStats other) { - rxBytes += other.rxBytes; - txBytes += other.txBytes; - } - - /** Returns the string representation of this object. */ - public String toString() { - return String.format("rx:%s tx:%s", rxBytes, txBytes); - } - } - - public OffloadHardwareInterface(Handler h, SharedLog log) { - this(h, log, new Dependencies(log)); - } - - OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) { - mHandler = h; - mLog = log.forSubComponent(TAG); - mDeps = deps; - } - - /** Capture OffloadHardwareInterface dependencies, for injection. */ - static class Dependencies { - private final SharedLog mLog; - - Dependencies(SharedLog log) { - mLog = log; - } - - public IOffloadConfig getOffloadConfig() { - try { - return IOffloadConfig.getService(true /*retry*/); - } catch (RemoteException | NoSuchElementException e) { - mLog.e("getIOffloadConfig error " + e); - return null; - } - } - - public IOffloadControl getOffloadControl() { - try { - return IOffloadControl.getService(true /*retry*/); - } catch (RemoteException | NoSuchElementException e) { - mLog.e("tethering offload control not supported: " + e); - return null; - } - } - - public NativeHandle createConntrackSocket(final int groups) { - final FileDescriptor fd; - try { - fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER); - } catch (ErrnoException e) { - mLog.e("Unable to create conntrack socket " + e); - return null; - } - - final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups); - try { - Os.bind(fd, sockAddr); - } catch (ErrnoException | SocketException e) { - mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e); - try { - SocketUtils.closeSocket(fd); - } catch (IOException ie) { - // Nothing we can do here - } - return null; - } - try { - Os.connect(fd, sockAddr); - } catch (ErrnoException | SocketException e) { - mLog.e("connect to kernel fail for groups " + groups + " error: " + e); - try { - SocketUtils.closeSocket(fd); - } catch (IOException ie) { - // Nothing we can do here - } - return null; - } - - return new NativeHandle(fd, true); - } - } - - /** Get default value indicating whether offload is supported. */ - public int getDefaultTetherOffloadDisabled() { - return DEFAULT_TETHER_OFFLOAD_DISABLED; - } - - /** - * Offload management process need to know conntrack rules to support NAT, but it may not have - * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and - * share them with offload management process. - */ - public boolean initOffloadConfig() { - final IOffloadConfig offloadConfig = mDeps.getOffloadConfig(); - if (offloadConfig == null) { - mLog.e("Could not find IOffloadConfig service"); - return false; - } - // Per the IConfigOffload definition: - // - // h1 provides a file descriptor bound to the following netlink groups - // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY). - // - // h2 provides a file descriptor bound to the following netlink groups - // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY). - final NativeHandle h1 = mDeps.createConntrackSocket( - NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); - if (h1 == null) return false; - - sendIpv4NfGenMsg(h1, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET), - (short) (NLM_F_REQUEST | NLM_F_DUMP)); - - final NativeHandle h2 = mDeps.createConntrackSocket( - NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); - if (h2 == null) { - closeFdInNativeHandle(h1); - return false; - } - - final CbResults results = new CbResults(); - try { - offloadConfig.setHandles(h1, h2, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record("initOffloadConfig, setHandles fail", e); - return false; - } - // Explicitly close FDs. - closeFdInNativeHandle(h1); - closeFdInNativeHandle(h2); - - record("initOffloadConfig, setHandles results:", results); - return results.mSuccess; - } - - @VisibleForTesting - public void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) { - final int length = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE; - final byte[] msg = new byte[length]; - final ByteBuffer byteBuffer = ByteBuffer.wrap(msg); - byteBuffer.order(ByteOrder.nativeOrder()); - - final StructNlMsgHdr nlh = new StructNlMsgHdr(); - nlh.nlmsg_len = length; - nlh.nlmsg_type = type; - nlh.nlmsg_flags = flags; - nlh.nlmsg_seq = 0; - nlh.pack(byteBuffer); - - // Header needs to be added to buffer since a generic netlink request is being sent. - final StructNfGenMsg nfh = new StructNfGenMsg((byte) OsConstants.AF_INET); - nfh.pack(byteBuffer); - - try { - NetlinkSocket.sendMessage(handle.getFileDescriptor(), msg, 0 /* offset */, length, - NETLINK_MESSAGE_TIMEOUT_MS); - } catch (ErrnoException | InterruptedIOException e) { - mLog.e("Unable to send netfilter message, error: " + e); - } - } - - private void closeFdInNativeHandle(final NativeHandle h) { - try { - h.close(); - } catch (IOException | IllegalStateException e) { - // IllegalStateException means fd is already closed, do nothing here. - // Also nothing we can do if IOException. - } - } - - /** Initialize the tethering offload HAL. */ - public boolean initOffloadControl(ControlCallback controlCb) { - mControlCallback = controlCb; - - if (mOffloadControl == null) { - mOffloadControl = mDeps.getOffloadControl(); - if (mOffloadControl == null) { - mLog.e("tethering IOffloadControl.getService() returned null"); - return false; - } - } - - final String logmsg = String.format("initOffloadControl(%s)", - (controlCb == null) ? "null" - : "0x" + Integer.toHexString(System.identityHashCode(controlCb))); - - mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback, mLog); - final CbResults results = new CbResults(); - try { - mOffloadControl.initOffload( - mTetheringOffloadCallback, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - /** Stop IOffloadControl. */ - public void stopOffloadControl() { - if (mOffloadControl != null) { - try { - mOffloadControl.stopOffload( - (boolean success, String errMsg) -> { - if (!success) mLog.e("stopOffload failed: " + errMsg); - }); - } catch (RemoteException e) { - mLog.e("failed to stopOffload: " + e); - } - } - mOffloadControl = null; - mTetheringOffloadCallback = null; - mControlCallback = null; - mLog.log("stopOffloadControl()"); - } - - /** Get Tx/Rx usage from last query. */ - public ForwardedStats getForwardedStats(String upstream) { - final String logmsg = String.format("getForwardedStats(%s)", upstream); - - final ForwardedStats stats = new ForwardedStats(); - try { - mOffloadControl.getForwardedStats( - upstream, - (long rxBytes, long txBytes) -> { - stats.rxBytes = (rxBytes > 0) ? rxBytes : 0; - stats.txBytes = (txBytes > 0) ? txBytes : 0; - }); - } catch (RemoteException e) { - record(logmsg, e); - return stats; - } - - return stats; - } - - /** Set local prefixes to offload management process. */ - public boolean setLocalPrefixes(ArrayList localPrefixes) { - final String logmsg = String.format("setLocalPrefixes([%s])", - String.join(",", localPrefixes)); - - final CbResults results = new CbResults(); - try { - mOffloadControl.setLocalPrefixes(localPrefixes, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - /** Set data limit value to offload management process. */ - public boolean setDataLimit(String iface, long limit) { - - final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit); - - final CbResults results = new CbResults(); - try { - mOffloadControl.setDataLimit( - iface, limit, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - /** Set upstream parameters to offload management process. */ - public boolean setUpstreamParameters( - String iface, String v4addr, String v4gateway, ArrayList v6gws) { - iface = (iface != null) ? iface : NO_INTERFACE_NAME; - v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS; - v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY; - v6gws = (v6gws != null) ? v6gws : new ArrayList<>(); - - final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])", - iface, v4addr, v4gateway, String.join(",", v6gws)); - - final CbResults results = new CbResults(); - try { - mOffloadControl.setUpstreamParameters( - iface, v4addr, v4gateway, v6gws, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - /** Add downstream prefix to offload management process. */ - public boolean addDownstreamPrefix(String ifname, String prefix) { - final String logmsg = String.format("addDownstreamPrefix(%s, %s)", ifname, prefix); - - final CbResults results = new CbResults(); - try { - mOffloadControl.addDownstream(ifname, prefix, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - /** Remove downstream prefix from offload management process. */ - public boolean removeDownstreamPrefix(String ifname, String prefix) { - final String logmsg = String.format("removeDownstreamPrefix(%s, %s)", ifname, prefix); - - final CbResults results = new CbResults(); - try { - mOffloadControl.removeDownstream(ifname, prefix, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - private void record(String msg, Throwable t) { - mLog.e(msg + YIELDS + "exception: " + t); - } - - private void record(String msg, CbResults results) { - final String logmsg = msg + YIELDS + results; - if (!results.mSuccess) { - mLog.e(logmsg); - } else { - mLog.log(logmsg); - } - } - - private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub { - public final Handler handler; - public final ControlCallback controlCb; - public final SharedLog log; - - TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) { - handler = h; - controlCb = cb; - log = sharedLog; - } - - @Override - public void onEvent(int event) { - handler.post(() -> { - switch (event) { - case OffloadCallbackEvent.OFFLOAD_STARTED: - controlCb.onStarted(); - break; - case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR: - controlCb.onStoppedError(); - break; - case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED: - controlCb.onStoppedUnsupported(); - break; - case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE: - controlCb.onSupportAvailable(); - break; - case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED: - controlCb.onStoppedLimitReached(); - break; - default: - log.e("Unsupported OffloadCallbackEvent: " + event); - } - }); - } - - @Override - public void updateTimeout(NatTimeoutUpdate params) { - handler.post(() -> { - controlCb.onNatTimeoutUpdate( - networkProtocolToOsConstant(params.proto), - params.src.addr, uint16(params.src.port), - params.dst.addr, uint16(params.dst.port)); - }); - } - } - - private static int networkProtocolToOsConstant(int proto) { - switch (proto) { - case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP; - case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP; - default: - // The caller checks this value and will log an error. Just make - // sure it won't collide with valid OsContants.IPPROTO_* values. - return -Math.abs(proto); - } - } - - private static class CbResults { - boolean mSuccess; - String mErrMsg; - - @Override - public String toString() { - if (mSuccess) { - return "ok"; - } else { - return "fail: " + mErrMsg; - } - } - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java deleted file mode 100644 index 4f616cdff086..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.networkstack.tethering; - -import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.util.PrefixUtils.asIpPrefix; - -import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH; -import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; -import static com.android.net.module.util.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH; - -import static java.util.Arrays.asList; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.Network; -import android.net.ip.IpServer; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.SparseArray; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.Set; - -/** - * This class coordinate IP addresses conflict problem. - * - * Tethering downstream IP addresses may conflict with network assigned addresses. This - * coordinator is responsible for recording all of network assigned addresses and dispatched - * free address to downstream interfaces. - * - * This class is not thread-safe and should be accessed on the same tethering internal thread. - * @hide - */ -public class PrivateAddressCoordinator { - public static final int PREFIX_LENGTH = 24; - - // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream - // address may be requested before coordinator get current upstream notification. To ensure - // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared - // when tethering is down. Instead tethering would remove all deprecated upstreams from - // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprecatedUpstreams(). - private final ArrayMap> mUpstreamPrefixMap; - private final ArraySet mDownstreams; - private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24"; - private static final String LEGACY_BLUETOOTH_IFACE_ADDRESS = "192.168.44.1/24"; - private final List mTetheringPrefixes; - private final ConnectivityManager mConnectivityMgr; - private final TetheringConfiguration mConfig; - // keyed by downstream type(TetheringManager.TETHERING_*). - private final SparseArray mCachedAddresses; - - public PrivateAddressCoordinator(Context context, TetheringConfiguration config) { - mDownstreams = new ArraySet<>(); - mUpstreamPrefixMap = new ArrayMap<>(); - mConnectivityMgr = (ConnectivityManager) context.getSystemService( - Context.CONNECTIVITY_SERVICE); - mConfig = config; - mCachedAddresses = new SparseArray<>(); - // Reserved static addresses for bluetooth and wifi p2p. - mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS)); - mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS)); - - mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16"))); - if (config.isSelectAllPrefixRangeEnabled()) { - mTetheringPrefixes.add(new IpPrefix("172.16.0.0/12")); - mTetheringPrefixes.add(new IpPrefix("10.0.0.0/8")); - } - } - - /** - * Record a new upstream IpPrefix which may conflict with tethering downstreams. - * The downstreams will be notified if a conflict is found. When updateUpstreamPrefix is called, - * UpstreamNetworkState must have an already populated LinkProperties. - */ - public void updateUpstreamPrefix(final UpstreamNetworkState ns) { - // Do not support VPN as upstream. Normally, networkCapabilities is not expected to be null, - // but just checking to be sure. - if (ns.networkCapabilities != null && ns.networkCapabilities.hasTransport(TRANSPORT_VPN)) { - removeUpstreamPrefix(ns.network); - return; - } - - final ArrayList ipv4Prefixes = getIpv4Prefixes( - ns.linkProperties.getAllLinkAddresses()); - if (ipv4Prefixes.isEmpty()) { - removeUpstreamPrefix(ns.network); - return; - } - - mUpstreamPrefixMap.put(ns.network, ipv4Prefixes); - handleMaybePrefixConflict(ipv4Prefixes); - } - - private ArrayList getIpv4Prefixes(final List linkAddresses) { - final ArrayList list = new ArrayList<>(); - for (LinkAddress address : linkAddresses) { - if (!address.isIpv4()) continue; - - list.add(asIpPrefix(address)); - } - - return list; - } - - private void handleMaybePrefixConflict(final List prefixes) { - for (IpServer downstream : mDownstreams) { - final IpPrefix target = getDownstreamPrefix(downstream); - - for (IpPrefix source : prefixes) { - if (isConflictPrefix(source, target)) { - downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - break; - } - } - } - } - - /** Remove IpPrefix records corresponding to input network. */ - public void removeUpstreamPrefix(final Network network) { - mUpstreamPrefixMap.remove(network); - } - - /** - * Maybe remove deprecated upstream records, this would be called once tethering started without - * any exiting tethered downstream. - */ - public void maybeRemoveDeprecatedUpstreams() { - if (mUpstreamPrefixMap.isEmpty()) return; - - // Remove all upstreams that are no longer valid networks - final Set toBeRemoved = new HashSet<>(mUpstreamPrefixMap.keySet()); - toBeRemoved.removeAll(asList(mConnectivityMgr.getAllNetworks())); - - mUpstreamPrefixMap.removeAll(toBeRemoved); - } - - /** - * Pick a random available address and mark its prefix as in use for the provided IpServer, - * returns null if there is no available address. - */ - @Nullable - public LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) { - if (mConfig.shouldEnableWifiP2pDedicatedIp() - && ipServer.interfaceType() == TETHERING_WIFI_P2P) { - return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS); - } - - final LinkAddress cachedAddress = mCachedAddresses.get(ipServer.interfaceType()); - if (useLastAddress && cachedAddress != null - && !isConflictWithUpstream(asIpPrefix(cachedAddress))) { - mDownstreams.add(ipServer); - return cachedAddress; - } - - for (IpPrefix prefixRange : mTetheringPrefixes) { - final LinkAddress newAddress = chooseDownstreamAddress(prefixRange); - if (newAddress != null) { - mDownstreams.add(ipServer); - mCachedAddresses.put(ipServer.interfaceType(), newAddress); - return newAddress; - } - } - - // No available address. - return null; - } - - private int getPrefixBaseAddress(final IpPrefix prefix) { - return inet4AddressToIntHTH((Inet4Address) prefix.getAddress()); - } - - /** - * Check whether input prefix conflict with upstream prefixes or in-use downstream prefixes. - * If yes, return one of them. - */ - private IpPrefix getConflictPrefix(final IpPrefix prefix) { - final IpPrefix upstream = getConflictWithUpstream(prefix); - if (upstream != null) return upstream; - - return getInUseDownstreamPrefix(prefix); - } - - // Get the next non-conflict sub prefix. E.g: To get next sub prefix from 10.0.0.0/8, if the - // previously selected prefix is 10.20.42.0/24(subPrefix: 0.20.42.0) and the conflicting prefix - // is 10.16.0.0/20 (10.16.0.0 ~ 10.16.15.255), then the max address under subPrefix is - // 0.16.15.255 and the next subPrefix is 0.16.16.255/24 (0.16.15.255 + 0.0.1.0). - // Note: the sub address 0.0.0.255 here is fine to be any value that it will be replaced as - // selected random sub address later. - private int getNextSubPrefix(final IpPrefix conflictPrefix, final int prefixRangeMask) { - final int suffixMask = ~prefixLengthToV4NetmaskIntHTH(conflictPrefix.getPrefixLength()); - // The largest offset within the prefix assignment block that still conflicts with - // conflictPrefix. - final int maxConflict = - (getPrefixBaseAddress(conflictPrefix) | suffixMask) & ~prefixRangeMask; - - final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH); - // Pick a sub prefix a full prefix (1 << (32 - PREFIX_LENGTH) addresses) greater than - // maxConflict. This ensures that the selected prefix never overlaps with conflictPrefix. - // There is no need to mask the result with PREFIX_LENGTH bits because this is done by - // findAvailablePrefixFromRange when it constructs the prefix. - return maxConflict + (1 << (32 - PREFIX_LENGTH)); - } - - private LinkAddress chooseDownstreamAddress(final IpPrefix prefixRange) { - // The netmask of the prefix assignment block (e.g., 0xfff00000 for 172.16.0.0/12). - final int prefixRangeMask = prefixLengthToV4NetmaskIntHTH(prefixRange.getPrefixLength()); - - // The zero address in the block (e.g., 0xac100000 for 172.16.0.0/12). - final int baseAddress = getPrefixBaseAddress(prefixRange); - - // The subnet mask corresponding to PREFIX_LENGTH. - final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH); - - // The offset within prefixRange of a randomly-selected prefix of length PREFIX_LENGTH. - // This may not be the prefix of the address returned by this method: - // - If it is already in use, the method will return an address in another prefix. - // - If all prefixes within prefixRange are in use, the method will return null. For - // example, for a /24 prefix within 172.26.0.0/12, this will be a multiple of 256 in - // [0, 1048576). In other words, a random 32-bit number with mask 0x000fff00. - // - // prefixRangeMask is required to ensure no wrapping. For example, consider: - // - prefixRange 127.0.0.0/8 - // - randomPrefixStart 127.255.255.0 - // - A conflicting prefix of 127.255.254.0/23 - // In this case without prefixRangeMask, getNextSubPrefix would return 128.0.0.0, which - // means the "start < end" check in findAvailablePrefixFromRange would not reject the prefix - // because Java doesn't have unsigned integers, so 128.0.0.0 = 0x80000000 = -2147483648 - // is less than 127.0.0.0 = 0x7f000000 = 2130706432. - // - // Additionally, it makes debug output easier to read by making the numbers smaller. - final int randomPrefixStart = getRandomInt() & ~prefixRangeMask & prefixMask; - - // A random offset within the prefix. Used to determine the local address once the prefix - // is selected. It does not result in an IPv4 address ending in .0, .1, or .255 - // For a PREFIX_LENGTH of 255, this is a number between 2 and 254. - final int subAddress = getSanitizedSubAddr(~prefixMask); - - // Find a prefix length PREFIX_LENGTH between randomPrefixStart and the end of the block, - // such that the prefix does not conflict with any upstream. - IpPrefix downstreamPrefix = findAvailablePrefixFromRange( - randomPrefixStart, (~prefixRangeMask) + 1, baseAddress, prefixRangeMask); - if (downstreamPrefix != null) return getLinkAddress(downstreamPrefix, subAddress); - - // If that failed, do the same, but between 0 and randomPrefixStart. - downstreamPrefix = findAvailablePrefixFromRange( - 0, randomPrefixStart, baseAddress, prefixRangeMask); - - return getLinkAddress(downstreamPrefix, subAddress); - } - - private LinkAddress getLinkAddress(final IpPrefix prefix, final int subAddress) { - if (prefix == null) return null; - - final InetAddress address = intToInet4AddressHTH(getPrefixBaseAddress(prefix) | subAddress); - return new LinkAddress(address, PREFIX_LENGTH); - } - - private IpPrefix findAvailablePrefixFromRange(final int start, final int end, - final int baseAddress, final int prefixRangeMask) { - int newSubPrefix = start; - while (newSubPrefix < end) { - final InetAddress address = intToInet4AddressHTH(baseAddress | newSubPrefix); - final IpPrefix prefix = new IpPrefix(address, PREFIX_LENGTH); - - final IpPrefix conflictPrefix = getConflictPrefix(prefix); - - if (conflictPrefix == null) return prefix; - - newSubPrefix = getNextSubPrefix(conflictPrefix, prefixRangeMask); - } - - return null; - } - - /** Get random int which could be used to generate random address. */ - @VisibleForTesting - public int getRandomInt() { - return (new Random()).nextInt(); - } - - /** Get random subAddress and avoid selecting x.x.x.0, x.x.x.1 and x.x.x.255 address. */ - private int getSanitizedSubAddr(final int subAddrMask) { - final int randomSubAddr = getRandomInt() & subAddrMask; - // If prefix length > 30, the selecting speace would be less than 4 which may be hard to - // avoid 3 consecutive address. - if (PREFIX_LENGTH > 30) return randomSubAddr; - - // TODO: maybe it is not necessary to avoid .0, .1 and .255 address because tethering - // address would not be conflicted. This code only works because PREFIX_LENGTH is not longer - // than 24 - final int candidate = randomSubAddr & 0xff; - if (candidate == 0 || candidate == 1 || candidate == 255) { - return (randomSubAddr & 0xfffffffc) + 2; - } - - return randomSubAddr; - } - - /** Release downstream record for IpServer. */ - public void releaseDownstream(final IpServer ipServer) { - mDownstreams.remove(ipServer); - } - - /** Clear current upstream prefixes records. */ - public void clearUpstreamPrefixes() { - mUpstreamPrefixMap.clear(); - } - - private IpPrefix getConflictWithUpstream(final IpPrefix prefix) { - for (int i = 0; i < mUpstreamPrefixMap.size(); i++) { - final List list = mUpstreamPrefixMap.valueAt(i); - for (IpPrefix upstream : list) { - if (isConflictPrefix(prefix, upstream)) return upstream; - } - } - return null; - } - - private boolean isConflictWithUpstream(final IpPrefix prefix) { - return getConflictWithUpstream(prefix) != null; - } - - private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) { - if (prefix2.getPrefixLength() < prefix1.getPrefixLength()) { - return prefix2.contains(prefix1.getAddress()); - } - - return prefix1.contains(prefix2.getAddress()); - } - - // InUse Prefixes are prefixes of mCachedAddresses which are active downstream addresses, last - // downstream addresses(reserved for next time) and static addresses(e.g. bluetooth, wifi p2p). - private IpPrefix getInUseDownstreamPrefix(final IpPrefix prefix) { - for (int i = 0; i < mCachedAddresses.size(); i++) { - final IpPrefix downstream = asIpPrefix(mCachedAddresses.valueAt(i)); - if (isConflictPrefix(prefix, downstream)) return downstream; - } - - // IpServer may use manually-defined address (mStaticIpv4ServerAddr) which does not include - // in mCachedAddresses. - for (IpServer downstream : mDownstreams) { - final IpPrefix target = getDownstreamPrefix(downstream); - - if (isConflictPrefix(prefix, target)) return target; - } - - return null; - } - - @NonNull - private IpPrefix getDownstreamPrefix(final IpServer downstream) { - final LinkAddress address = downstream.getAddress(); - - return asIpPrefix(address); - } - - void dump(final IndentingPrintWriter pw) { - pw.println("mTetheringPrefixes:"); - pw.increaseIndent(); - for (IpPrefix prefix : mTetheringPrefixes) { - pw.println(prefix); - } - pw.decreaseIndent(); - - pw.println("mUpstreamPrefixMap:"); - pw.increaseIndent(); - for (int i = 0; i < mUpstreamPrefixMap.size(); i++) { - pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i)); - } - pw.decreaseIndent(); - - pw.println("mDownstreams:"); - pw.increaseIndent(); - for (IpServer ipServer : mDownstreams) { - pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress()); - } - pw.decreaseIndent(); - - pw.println("mCachedAddresses:"); - pw.increaseIndent(); - for (int i = 0; i < mCachedAddresses.size(); i++) { - pw.println(mCachedAddresses.keyAt(i) + " - " + mCachedAddresses.valueAt(i)); - } - pw.decreaseIndent(); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java deleted file mode 100644 index 5a0c5b0cff5f..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ /dev/null @@ -1,2427 +0,0 @@ -/* - * Copyright (C) 2010 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.networkstack.tethering; - -import static android.Manifest.permission.NETWORK_SETTINGS; -import static android.Manifest.permission.NETWORK_STACK; -import static android.content.pm.PackageManager.GET_ACTIVITIES; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.hardware.usb.UsbManager.USB_CONFIGURED; -import static android.hardware.usb.UsbManager.USB_CONNECTED; -import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; -import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; -import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; -import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; -import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; -import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; -import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; -import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; -import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; -import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; -import static android.net.TetheringManager.EXTRA_ERRORED_TETHER; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_INVALID; -import static android.net.TetheringManager.TETHERING_NCM; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.TetheringManager.TETHERING_WIGIG; -import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL; -import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE; -import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; -import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_TYPE; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED; -import static android.net.util.TetheringMessageBase.BASE_MAIN_SM; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; -import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; -import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; - -import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; - -import android.app.usage.NetworkStatsManager; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothPan; -import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothProfile.ServiceListener; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.hardware.usb.UsbManager; -import android.net.ConnectivityManager; -import android.net.EthernetManager; -import android.net.IIntResultListener; -import android.net.INetd; -import android.net.ITetheringEventCallback; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.TetherStatesParcel; -import android.net.TetheredClient; -import android.net.TetheringCallbackStartedParcel; -import android.net.TetheringConfigurationParcel; -import android.net.TetheringRequestParcel; -import android.net.ip.IpServer; -import android.net.shared.NetdUtils; -import android.net.util.BaseNetdUnsolicitedEventListener; -import android.net.util.InterfaceSet; -import android.net.util.PrefixUtils; -import android.net.util.SharedLog; -import android.net.util.TetheringUtils; -import android.net.util.VersionedBroadcastListener; -import android.net.wifi.WifiClient; -import android.net.wifi.WifiManager; -import android.net.wifi.p2p.WifiP2pGroup; -import android.net.wifi.p2p.WifiP2pInfo; -import android.net.wifi.p2p.WifiP2pManager; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.os.ServiceSpecificException; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; -import android.util.SparseArray; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.internal.util.MessageUtils; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.RejectedExecutionException; - -/** - * - * This class holds much of the business logic to allow Android devices - * to act as IP gateways via USB, BT, and WiFi interfaces. - */ -public class Tethering { - - private static final String TAG = Tethering.class.getSimpleName(); - private static final boolean DBG = false; - private static final boolean VDBG = false; - - private static final Class[] sMessageClasses = { - Tethering.class, TetherMainSM.class, IpServer.class - }; - private static final SparseArray sMagicDecoderRing = - MessageUtils.findMessageNames(sMessageClasses); - // Keep in sync with NETID_UNSET in system/netd/include/netid_client.h - private static final int NETID_UNSET = 0; - - private static class TetherState { - public final IpServer ipServer; - public int lastState; - public int lastError; - - TetherState(IpServer ipServer) { - this.ipServer = ipServer; - // Assume all state machines start out available and with no errors. - lastState = IpServer.STATE_AVAILABLE; - lastError = TETHER_ERROR_NO_ERROR; - } - - public boolean isCurrentlyServing() { - switch (lastState) { - case IpServer.STATE_TETHERED: - case IpServer.STATE_LOCAL_ONLY: - return true; - default: - return false; - } - } - } - - /** - * Cookie added when registering {@link android.net.TetheringManager.TetheringEventCallback}. - */ - private static class CallbackCookie { - public final boolean hasListClientsPermission; - - private CallbackCookie(boolean hasListClientsPermission) { - this.hasListClientsPermission = hasListClientsPermission; - } - } - - private final SharedLog mLog = new SharedLog(TAG); - private final RemoteCallbackList mTetheringEventCallbacks = - new RemoteCallbackList<>(); - // Currently active tethering requests per tethering type. Only one of each type can be - // requested at a time. After a tethering type is requested, the map keeps tethering parameters - // to be used after the interface comes up asynchronously. - private final SparseArray mActiveTetheringRequests = - new SparseArray<>(); - - // used to synchronize public access to members - // TODO(b/153621704): remove mPublicSync to make Tethering lock free - private final Object mPublicSync; - private final Context mContext; - private final ArrayMap mTetherStates; - private final BroadcastReceiver mStateReceiver; - private final Looper mLooper; - private final StateMachine mTetherMainSM; - private final OffloadController mOffloadController; - private final UpstreamNetworkMonitor mUpstreamNetworkMonitor; - // TODO: Figure out how to merge this and other downstream-tracking objects - // into a single coherent structure. - // Use LinkedHashSet for predictable ordering order for ConnectedClientsTracker. - private final LinkedHashSet mForwardedDownstreams; - private final VersionedBroadcastListener mCarrierConfigChange; - private final TetheringDependencies mDeps; - private final EntitlementManager mEntitlementMgr; - private final Handler mHandler; - private final INetd mNetd; - private final NetdCallback mNetdCallback; - private final UserRestrictionActionListener mTetheringRestriction; - private final ActiveDataSubIdListener mActiveDataSubIdListener; - private final ConnectedClientsTracker mConnectedClientsTracker; - private final TetheringThreadExecutor mExecutor; - private final TetheringNotificationUpdater mNotificationUpdater; - private final UserManager mUserManager; - private final BpfCoordinator mBpfCoordinator; - private final PrivateAddressCoordinator mPrivateAddressCoordinator; - private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; - // All the usage of mTetheringEventCallback should run in the same thread. - private ITetheringEventCallback mTetheringEventCallback = null; - - private volatile TetheringConfiguration mConfig; - private InterfaceSet mCurrentUpstreamIfaceSet; - - private boolean mRndisEnabled; // track the RNDIS function enabled state - // True iff. WiFi tethering should be started when soft AP is ready. - private boolean mWifiTetherRequested; - private Network mTetherUpstream; - private TetherStatesParcel mTetherStatesParcel; - private boolean mDataSaverEnabled = false; - private String mWifiP2pTetherInterface = null; - private int mOffloadStatus = TETHER_HARDWARE_OFFLOAD_STOPPED; - - @GuardedBy("mPublicSync") - private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest; - @GuardedBy("mPublicSync") - private String mConfiguredEthernetIface; - @GuardedBy("mPublicSync") - private EthernetCallback mEthernetCallback; - - public Tethering(TetheringDependencies deps) { - mLog.mark("Tethering.constructed"); - mDeps = deps; - mContext = mDeps.getContext(); - mNetd = mDeps.getINetd(mContext); - mLooper = mDeps.getTetheringLooper(); - mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper); - - mPublicSync = new Object(); - - mTetherStates = new ArrayMap<>(); - mConnectedClientsTracker = new ConnectedClientsTracker(); - - mTetherMainSM = new TetherMainSM("TetherMain", mLooper, deps); - mTetherMainSM.start(); - - mHandler = mTetherMainSM.getHandler(); - mOffloadController = mDeps.getOffloadController(mHandler, mLog, - new OffloadController.Dependencies() { - - @Override - public TetheringConfiguration getTetherConfig() { - return mConfig; - } - }); - mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMainSM, mLog, - TetherMainSM.EVENT_UPSTREAM_CALLBACK); - mForwardedDownstreams = new LinkedHashSet<>(); - - IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_CARRIER_CONFIG_CHANGED); - // EntitlementManager will send EVENT_UPSTREAM_PERMISSION_CHANGED when cellular upstream - // permission is changed according to entitlement check result. - mEntitlementMgr = mDeps.getEntitlementManager(mContext, mHandler, mLog, - () -> mTetherMainSM.sendMessage( - TetherMainSM.EVENT_UPSTREAM_PERMISSION_CHANGED)); - mEntitlementMgr.setOnUiEntitlementFailedListener((int downstream) -> { - mLog.log("OBSERVED UiEnitlementFailed"); - stopTethering(downstream); - }); - mEntitlementMgr.setTetheringConfigurationFetcher(() -> { - return mConfig; - }); - - mCarrierConfigChange = new VersionedBroadcastListener( - "CarrierConfigChangeListener", mContext, mHandler, filter, - (Intent ignored) -> { - mLog.log("OBSERVED carrier config change"); - updateConfiguration(); - mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); - }); - - mStateReceiver = new StateReceiver(); - - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mTetheringRestriction = new UserRestrictionActionListener( - mUserManager, this, mNotificationUpdater); - mExecutor = new TetheringThreadExecutor(mHandler); - mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); - mNetdCallback = new NetdCallback(); - - // Load tethering configuration. - updateConfiguration(); - // It is OK for the configuration to be passed to the PrivateAddressCoordinator at - // construction time because the only part of the configuration it uses is - // shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that. - mPrivateAddressCoordinator = mDeps.getPrivateAddressCoordinator(mContext, mConfig); - - // Must be initialized after tethering configuration is loaded because BpfCoordinator - // constructor needs to use the configuration. - mBpfCoordinator = mDeps.getBpfCoordinator( - new BpfCoordinator.Dependencies() { - @NonNull - public Handler getHandler() { - return mHandler; - } - - @NonNull - public INetd getNetd() { - return mNetd; - } - - @NonNull - public NetworkStatsManager getNetworkStatsManager() { - return mContext.getSystemService(NetworkStatsManager.class); - } - - @NonNull - public SharedLog getSharedLog() { - return mLog; - } - - @Nullable - public TetheringConfiguration getTetherConfig() { - return mConfig; - } - }); - - startStateMachineUpdaters(); - } - - /** - * Start to register callbacks. - * Call this function when tethering is ready to handle callback events. - */ - private void startStateMachineUpdaters() { - try { - mNetd.registerUnsolicitedEventListener(mNetdCallback); - } catch (RemoteException e) { - mLog.e("Unable to register netd UnsolicitedEventListener"); - } - mCarrierConfigChange.startListening(); - mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener, - PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); - - IntentFilter filter = new IntentFilter(); - filter.addAction(UsbManager.ACTION_USB_STATE); - filter.addAction(CONNECTIVITY_ACTION); - filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); - filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); - filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED); - filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED); - mContext.registerReceiver(mStateReceiver, filter, null, mHandler); - - final IntentFilter noUpstreamFilter = new IntentFilter(); - noUpstreamFilter.addAction(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING); - mContext.registerReceiver( - mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler); - - final WifiManager wifiManager = getWifiManager(); - if (wifiManager != null) { - wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback()); - } - - startTrackDefaultNetwork(); - } - - private class TetheringThreadExecutor implements Executor { - private final Handler mTetherHandler; - TetheringThreadExecutor(Handler handler) { - mTetherHandler = handler; - } - @Override - public void execute(Runnable command) { - if (!mTetherHandler.post(command)) { - throw new RejectedExecutionException(mTetherHandler + " is shutting down"); - } - } - } - - private class ActiveDataSubIdListener extends PhoneStateListener { - ActiveDataSubIdListener(Executor executor) { - super(executor); - } - - @Override - public void onActiveDataSubscriptionIdChanged(int subId) { - mLog.log("OBSERVED active data subscription change, from " + mActiveDataSubId - + " to " + subId); - if (subId == mActiveDataSubId) return; - - mActiveDataSubId = subId; - updateConfiguration(); - mNotificationUpdater.onActiveDataSubscriptionIdChanged(subId); - // To avoid launching unexpected provisioning checks, ignore re-provisioning - // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() - // will be triggered again when CarrierConfig is loaded. - if (mEntitlementMgr.getCarrierConfig(mConfig) != null) { - mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); - } else { - mLog.log("IGNORED reevaluate provisioning, no carrier config loaded"); - } - } - } - - private WifiManager getWifiManager() { - return (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); - } - - // NOTE: This is always invoked on the mLooper thread. - private void updateConfiguration() { - mConfig = mDeps.generateTetheringConfiguration(mContext, mLog, mActiveDataSubId); - mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired); - reportConfigurationChanged(mConfig.toStableParcelable()); - } - - private void maybeDunSettingChanged() { - final boolean isDunRequired = TetheringConfiguration.checkDunRequired(mContext); - if (isDunRequired == mConfig.isDunRequired) return; - updateConfiguration(); - } - - private class NetdCallback extends BaseNetdUnsolicitedEventListener { - @Override - public void onInterfaceChanged(String ifName, boolean up) { - mHandler.post(() -> interfaceStatusChanged(ifName, up)); - } - - @Override - public void onInterfaceLinkStateChanged(String ifName, boolean up) { - mHandler.post(() -> interfaceLinkStateChanged(ifName, up)); - } - - @Override - public void onInterfaceAdded(String ifName) { - mHandler.post(() -> interfaceAdded(ifName)); - } - - @Override - public void onInterfaceRemoved(String ifName) { - mHandler.post(() -> interfaceRemoved(ifName)); - } - } - - private class TetheringSoftApCallback implements WifiManager.SoftApCallback { - // TODO: Remove onStateChanged override when this method has default on - // WifiManager#SoftApCallback interface. - // Wifi listener for state change of the soft AP - @Override - public void onStateChanged(final int state, final int failureReason) { - // Nothing - } - - // Called by wifi when the number of soft AP clients changed. - @Override - public void onConnectedClientsChanged(final List clients) { - updateConnectedClients(clients); - } - } - - void interfaceStatusChanged(String iface, boolean up) { - // Never called directly: only called from interfaceLinkStateChanged. - // See NetlinkHandler.cpp: notifyInterfaceChanged. - if (VDBG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up); - synchronized (mPublicSync) { - if (up) { - maybeTrackNewInterfaceLocked(iface); - } else { - if (ifaceNameToType(iface) == TETHERING_BLUETOOTH - || ifaceNameToType(iface) == TETHERING_WIGIG) { - stopTrackingInterfaceLocked(iface); - } else { - // Ignore usb0 down after enabling RNDIS. - // We will handle disconnect in interfaceRemoved. - // Similarly, ignore interface down for WiFi. We monitor WiFi AP status - // through the WifiManager.WIFI_AP_STATE_CHANGED_ACTION intent. - if (VDBG) Log.d(TAG, "ignore interface down for " + iface); - } - } - } - } - - void interfaceLinkStateChanged(String iface, boolean up) { - interfaceStatusChanged(iface, up); - } - - private int ifaceNameToType(String iface) { - final TetheringConfiguration cfg = mConfig; - - if (cfg.isWifi(iface)) { - return TETHERING_WIFI; - } else if (cfg.isWigig(iface)) { - return TETHERING_WIGIG; - } else if (cfg.isWifiP2p(iface)) { - return TETHERING_WIFI_P2P; - } else if (cfg.isUsb(iface)) { - return TETHERING_USB; - } else if (cfg.isBluetooth(iface)) { - return TETHERING_BLUETOOTH; - } else if (cfg.isNcm(iface)) { - return TETHERING_NCM; - } - return TETHERING_INVALID; - } - - void interfaceAdded(String iface) { - if (VDBG) Log.d(TAG, "interfaceAdded " + iface); - synchronized (mPublicSync) { - maybeTrackNewInterfaceLocked(iface); - } - } - - void interfaceRemoved(String iface) { - if (VDBG) Log.d(TAG, "interfaceRemoved " + iface); - synchronized (mPublicSync) { - stopTrackingInterfaceLocked(iface); - } - } - - void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) { - mHandler.post(() -> { - final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get( - request.tetheringType); - // If tethering is already enabled with a different request, - // disable before re-enabling. - if (unfinishedRequest != null - && !TetheringUtils.isTetheringRequestEquals(unfinishedRequest, request)) { - enableTetheringInternal(request.tetheringType, false /* disabled */, null); - mEntitlementMgr.stopProvisioningIfNeeded(request.tetheringType); - } - mActiveTetheringRequests.put(request.tetheringType, request); - - if (request.exemptFromEntitlementCheck) { - mEntitlementMgr.setExemptedDownstreamType(request.tetheringType); - } else { - mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, - request.showProvisioningUi); - } - enableTetheringInternal(request.tetheringType, true /* enabled */, listener); - }); - } - - void stopTethering(int type) { - mHandler.post(() -> { - mActiveTetheringRequests.remove(type); - - enableTetheringInternal(type, false /* disabled */, null); - mEntitlementMgr.stopProvisioningIfNeeded(type); - }); - } - - /** - * Enables or disables tethering for the given type. If provisioning is required, it will - * schedule provisioning rechecks for the specified interface. - */ - private void enableTetheringInternal(int type, boolean enable, - final IIntResultListener listener) { - int result = TETHER_ERROR_NO_ERROR; - switch (type) { - case TETHERING_WIFI: - result = setWifiTethering(enable); - break; - case TETHERING_USB: - result = setUsbTethering(enable); - break; - case TETHERING_BLUETOOTH: - setBluetoothTethering(enable, listener); - break; - case TETHERING_NCM: - result = setNcmTethering(enable); - break; - case TETHERING_ETHERNET: - result = setEthernetTethering(enable); - break; - default: - Log.w(TAG, "Invalid tether type."); - result = TETHER_ERROR_UNKNOWN_TYPE; - } - - // The result of Bluetooth tethering will be sent by #setBluetoothTethering. - if (type != TETHERING_BLUETOOTH) { - sendTetherResult(listener, result, type); - } - } - - private void sendTetherResult(final IIntResultListener listener, final int result, - final int type) { - if (listener != null) { - try { - listener.onResult(result); - } catch (RemoteException e) { } - } - - // If changing tethering fail, remove corresponding request - // no matter who trigger the start/stop. - if (result != TETHER_ERROR_NO_ERROR) mActiveTetheringRequests.remove(type); - } - - private int setWifiTethering(final boolean enable) { - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mPublicSync) { - final WifiManager mgr = getWifiManager(); - if (mgr == null) { - mLog.e("setWifiTethering: failed to get WifiManager!"); - return TETHER_ERROR_SERVICE_UNAVAIL; - } - if ((enable && mgr.startTetheredHotspot(null /* use existing softap config */)) - || (!enable && mgr.stopSoftAp())) { - mWifiTetherRequested = enable; - return TETHER_ERROR_NO_ERROR; - } - } - } finally { - Binder.restoreCallingIdentity(ident); - } - - return TETHER_ERROR_INTERNAL_ERROR; - } - - private void setBluetoothTethering(final boolean enable, final IIntResultListener listener) { - final BluetoothAdapter adapter = mDeps.getBluetoothAdapter(); - if (adapter == null || !adapter.isEnabled()) { - Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " - + (adapter == null)); - sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL, TETHERING_BLUETOOTH); - return; - } - - adapter.getProfileProxy(mContext, new ServiceListener() { - @Override - public void onServiceDisconnected(int profile) { } - - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - // Clear identify is fine because caller already pass tethering permission at - // ConnectivityService#startTethering()(or stopTethering) before the control comes - // here. Bluetooth will check tethering permission again that there is - // Context#getOpPackageName() under BluetoothPan#setBluetoothTethering() to get - // caller's package name for permission check. - // Calling BluetoothPan#setBluetoothTethering() here means the package name always - // be system server. If calling identity is not cleared, that package's uid might - // not match calling uid and end up in permission denied. - final long identityToken = Binder.clearCallingIdentity(); - try { - ((BluetoothPan) proxy).setBluetoothTethering(enable); - } finally { - Binder.restoreCallingIdentity(identityToken); - } - // TODO: Enabling bluetooth tethering can fail asynchronously here. - // We should figure out a way to bubble up that failure instead of sending success. - final int result = (((BluetoothPan) proxy).isTetheringOn() == enable) - ? TETHER_ERROR_NO_ERROR - : TETHER_ERROR_INTERNAL_ERROR; - sendTetherResult(listener, result, TETHERING_BLUETOOTH); - adapter.closeProfileProxy(BluetoothProfile.PAN, proxy); - } - }, BluetoothProfile.PAN); - } - - private int setEthernetTethering(final boolean enable) { - final EthernetManager em = (EthernetManager) mContext.getSystemService( - Context.ETHERNET_SERVICE); - synchronized (mPublicSync) { - if (enable) { - if (mEthernetCallback != null) { - Log.d(TAG, "Ethernet tethering already started"); - return TETHER_ERROR_NO_ERROR; - } - - mEthernetCallback = new EthernetCallback(); - mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback); - } else { - stopEthernetTetheringLocked(); - } - } - return TETHER_ERROR_NO_ERROR; - } - - private void stopEthernetTetheringLocked() { - if (mConfiguredEthernetIface != null) { - stopTrackingInterfaceLocked(mConfiguredEthernetIface); - mConfiguredEthernetIface = null; - } - if (mEthernetCallback != null) { - mEthernetIfaceRequest.release(); - mEthernetCallback = null; - mEthernetIfaceRequest = null; - } - } - - private class EthernetCallback implements EthernetManager.TetheredInterfaceCallback { - @Override - public void onAvailable(String iface) { - synchronized (mPublicSync) { - if (this != mEthernetCallback) { - // Ethernet callback arrived after Ethernet tethering stopped. Ignore. - return; - } - maybeTrackNewInterfaceLocked(iface, TETHERING_ETHERNET); - changeInterfaceState(iface, IpServer.STATE_TETHERED); - mConfiguredEthernetIface = iface; - } - } - - @Override - public void onUnavailable() { - synchronized (mPublicSync) { - if (this != mEthernetCallback) { - // onAvailable called after stopping Ethernet tethering. - return; - } - stopEthernetTetheringLocked(); - } - } - } - - int tether(String iface) { - return tether(iface, IpServer.STATE_TETHERED); - } - - private int tether(String iface, int requestedState) { - if (DBG) Log.d(TAG, "Tethering " + iface); - synchronized (mPublicSync) { - TetherState tetherState = mTetherStates.get(iface); - if (tetherState == null) { - Log.e(TAG, "Tried to Tether an unknown iface: " + iface + ", ignoring"); - return TETHER_ERROR_UNKNOWN_IFACE; - } - // Ignore the error status of the interface. If the interface is available, - // the errors are referring to past tethering attempts anyway. - if (tetherState.lastState != IpServer.STATE_AVAILABLE) { - Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring"); - return TETHER_ERROR_UNAVAIL_IFACE; - } - // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet - // processed, this will be a no-op and it will not return an error. - // - // This code cannot race with untether() because they both synchronize on mPublicSync. - // TODO: reexamine the threading and messaging model to totally remove mPublicSync. - final int type = tetherState.ipServer.interfaceType(); - final TetheringRequestParcel request = mActiveTetheringRequests.get(type, null); - if (request != null) { - mActiveTetheringRequests.delete(type); - } - tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState, 0, - request); - return TETHER_ERROR_NO_ERROR; - } - } - - int untether(String iface) { - if (DBG) Log.d(TAG, "Untethering " + iface); - synchronized (mPublicSync) { - TetherState tetherState = mTetherStates.get(iface); - if (tetherState == null) { - Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring"); - return TETHER_ERROR_UNKNOWN_IFACE; - } - if (!tetherState.isCurrentlyServing()) { - Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring"); - return TETHER_ERROR_UNAVAIL_IFACE; - } - tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED); - return TETHER_ERROR_NO_ERROR; - } - } - - void untetherAll() { - stopTethering(TETHERING_WIFI); - stopTethering(TETHERING_WIFI_P2P); - stopTethering(TETHERING_USB); - stopTethering(TETHERING_BLUETOOTH); - stopTethering(TETHERING_ETHERNET); - } - - int getLastTetherError(String iface) { - synchronized (mPublicSync) { - TetherState tetherState = mTetherStates.get(iface); - if (tetherState == null) { - Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface - + ", ignoring"); - return TETHER_ERROR_UNKNOWN_IFACE; - } - return tetherState.lastError; - } - } - - private boolean isProvisioningNeededButUnavailable() { - return isTetherProvisioningRequired() && !doesEntitlementPackageExist(); - } - - boolean isTetherProvisioningRequired() { - final TetheringConfiguration cfg = mConfig; - return mEntitlementMgr.isTetherProvisioningRequired(cfg); - } - - private boolean doesEntitlementPackageExist() { - // provisioningApp must contain package and class name. - if (mConfig.provisioningApp.length != 2) { - return false; - } - - final PackageManager pm = mContext.getPackageManager(); - try { - pm.getPackageInfo(mConfig.provisioningApp[0], GET_ACTIVITIES); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - return true; - } - - // TODO: Figure out how to update for local hotspot mode interfaces. - private void sendTetherStateChangedBroadcast() { - if (!isTetheringSupported()) return; - - final ArrayList availableList = new ArrayList<>(); - final ArrayList tetherList = new ArrayList<>(); - final ArrayList localOnlyList = new ArrayList<>(); - final ArrayList erroredList = new ArrayList<>(); - final ArrayList lastErrorList = new ArrayList<>(); - - final TetheringConfiguration cfg = mConfig; - mTetherStatesParcel = new TetherStatesParcel(); - - int downstreamTypesMask = DOWNSTREAM_NONE; - synchronized (mPublicSync) { - for (int i = 0; i < mTetherStates.size(); i++) { - TetherState tetherState = mTetherStates.valueAt(i); - String iface = mTetherStates.keyAt(i); - if (tetherState.lastError != TETHER_ERROR_NO_ERROR) { - erroredList.add(iface); - lastErrorList.add(tetherState.lastError); - } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) { - availableList.add(iface); - } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) { - localOnlyList.add(iface); - } else if (tetherState.lastState == IpServer.STATE_TETHERED) { - if (cfg.isUsb(iface)) { - downstreamTypesMask |= (1 << TETHERING_USB); - } else if (cfg.isWifi(iface)) { - downstreamTypesMask |= (1 << TETHERING_WIFI); - } else if (cfg.isBluetooth(iface)) { - downstreamTypesMask |= (1 << TETHERING_BLUETOOTH); - } - tetherList.add(iface); - } - } - } - - mTetherStatesParcel.availableList = availableList.toArray(new String[0]); - mTetherStatesParcel.tetheredList = tetherList.toArray(new String[0]); - mTetherStatesParcel.localOnlyList = localOnlyList.toArray(new String[0]); - mTetherStatesParcel.erroredIfaceList = erroredList.toArray(new String[0]); - mTetherStatesParcel.lastErrorList = new int[lastErrorList.size()]; - Iterator iterator = lastErrorList.iterator(); - for (int i = 0; i < lastErrorList.size(); i++) { - mTetherStatesParcel.lastErrorList[i] = iterator.next().intValue(); - } - reportTetherStateChanged(mTetherStatesParcel); - - final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED); - bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList); - bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList); - bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList); - bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, erroredList); - mContext.sendStickyBroadcastAsUser(bcast, UserHandle.ALL); - if (DBG) { - Log.d(TAG, String.format( - "sendTetherStateChangedBroadcast %s=[%s] %s=[%s] %s=[%s] %s=[%s]", - "avail", TextUtils.join(",", availableList), - "local_only", TextUtils.join(",", localOnlyList), - "tether", TextUtils.join(",", tetherList), - "error", TextUtils.join(",", erroredList))); - } - - mNotificationUpdater.onDownstreamChanged(downstreamTypesMask); - } - - private class StateReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context content, Intent intent) { - final String action = intent.getAction(); - if (action == null) return; - - if (action.equals(UsbManager.ACTION_USB_STATE)) { - handleUsbAction(intent); - } else if (action.equals(CONNECTIVITY_ACTION)) { - handleConnectivityAction(intent); - } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { - handleWifiApAction(intent); - } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) { - handleWifiP2pAction(intent); - } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { - mLog.log("OBSERVED configuration changed"); - updateConfiguration(); - } else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) { - mLog.log("OBSERVED user restrictions changed"); - handleUserRestrictionAction(); - } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) { - mLog.log("OBSERVED data saver changed"); - handleDataSaverChanged(); - } else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) { - untetherAll(); - } - } - - private void handleConnectivityAction(Intent intent) { - final NetworkInfo networkInfo = - (NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO); - if (networkInfo == null - || networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED) { - return; - } - - if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION: " + networkInfo.toString()); - mTetherMainSM.sendMessage(TetherMainSM.CMD_UPSTREAM_CHANGED); - } - - private void handleUsbAction(Intent intent) { - final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false); - final boolean usbConfigured = intent.getBooleanExtra(USB_CONFIGURED, false); - final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false); - final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false); - - mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s", - usbConnected, usbConfigured, rndisEnabled)); - - // There are three types of ACTION_USB_STATE: - // - // - DISCONNECTED (USB_CONNECTED and USB_CONFIGURED are 0) - // Meaning: USB connection has ended either because of - // software reset or hard unplug. - // - // - CONNECTED (USB_CONNECTED is 1, USB_CONFIGURED is 0) - // Meaning: the first stage of USB protocol handshake has - // occurred but it is not complete. - // - // - CONFIGURED (USB_CONNECTED and USB_CONFIGURED are 1) - // Meaning: the USB handshake is completely done and all the - // functions are ready to use. - // - // For more explanation, see b/62552150 . - synchronized (Tethering.this.mPublicSync) { - if (!usbConnected && mRndisEnabled) { - // Turn off tethering if it was enabled and there is a disconnect. - tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB); - mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_USB); - } else if (usbConfigured && rndisEnabled) { - // Tether if rndis is enabled and usb is configured. - tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB); - } else if (usbConnected && ncmEnabled) { - tetherMatchingInterfaces(IpServer.STATE_LOCAL_ONLY, TETHERING_NCM); - } - mRndisEnabled = usbConfigured && rndisEnabled; - } - } - - private void handleWifiApAction(Intent intent) { - final int curState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED); - final String ifname = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME); - final int ipmode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, IFACE_IP_MODE_UNSPECIFIED); - - synchronized (Tethering.this.mPublicSync) { - switch (curState) { - case WifiManager.WIFI_AP_STATE_ENABLING: - // We can see this state on the way to both enabled and failure states. - break; - case WifiManager.WIFI_AP_STATE_ENABLED: - enableWifiIpServingLocked(ifname, ipmode); - break; - case WifiManager.WIFI_AP_STATE_DISABLING: - // We can see this state on the way to disabled. - break; - case WifiManager.WIFI_AP_STATE_DISABLED: - case WifiManager.WIFI_AP_STATE_FAILED: - default: - disableWifiIpServingLocked(ifname, curState); - break; - } - } - } - - private boolean isGroupOwner(WifiP2pGroup group) { - return group != null && group.isGroupOwner() - && !TextUtils.isEmpty(group.getInterface()); - } - - private void handleWifiP2pAction(Intent intent) { - if (mConfig.isWifiP2pLegacyTetheringMode()) return; - - final WifiP2pInfo p2pInfo = - (WifiP2pInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO); - final WifiP2pGroup group = - (WifiP2pGroup) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP); - - if (VDBG) { - Log.d(TAG, "WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group); - } - - synchronized (Tethering.this.mPublicSync) { - // if no group is formed, bring it down if needed. - if (p2pInfo == null || !p2pInfo.groupFormed) { - disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface); - mWifiP2pTetherInterface = null; - return; - } - - // If there is a group but the device is not the owner, bail out. - if (!isGroupOwner(group)) return; - - // If already serving from the correct interface, nothing to do. - if (group.getInterface().equals(mWifiP2pTetherInterface)) return; - - // If already serving from another interface, turn it down first. - if (!TextUtils.isEmpty(mWifiP2pTetherInterface)) { - mLog.w("P2P tethered interface " + mWifiP2pTetherInterface - + "is different from current interface " - + group.getInterface() + ", re-tether it"); - disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface); - } - - // Finally bring up serving on the new interface - mWifiP2pTetherInterface = group.getInterface(); - enableWifiIpServingLocked(mWifiP2pTetherInterface, IFACE_IP_MODE_LOCAL_ONLY); - } - } - - private void handleUserRestrictionAction() { - mTetheringRestriction.onUserRestrictionsChanged(); - } - - private void handleDataSaverChanged() { - final ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - final boolean isDataSaverEnabled = connMgr.getRestrictBackgroundStatus() - != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; - - if (mDataSaverEnabled == isDataSaverEnabled) return; - - mDataSaverEnabled = isDataSaverEnabled; - if (mDataSaverEnabled) { - untetherAll(); - } - } - } - - @VisibleForTesting - boolean isTetheringActive() { - return mActiveTetheringRequests.size() > 0; - } - - @VisibleForTesting - protected static class UserRestrictionActionListener { - private final UserManager mUserMgr; - private final Tethering mWrapper; - private final TetheringNotificationUpdater mNotificationUpdater; - public boolean mDisallowTethering; - - public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper, - @NonNull TetheringNotificationUpdater updater) { - mUserMgr = um; - mWrapper = wrapper; - mNotificationUpdater = updater; - mDisallowTethering = false; - } - - public void onUserRestrictionsChanged() { - // getUserRestrictions gets restriction for this process' user, which is the primary - // user. This is fine because DISALLOW_CONFIG_TETHERING can only be set on the primary - // user. See UserManager.DISALLOW_CONFIG_TETHERING. - final Bundle restrictions = mUserMgr.getUserRestrictions(); - final boolean newlyDisallowed = - restrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING); - final boolean prevDisallowed = mDisallowTethering; - mDisallowTethering = newlyDisallowed; - - final boolean tetheringDisallowedChanged = (newlyDisallowed != prevDisallowed); - if (!tetheringDisallowedChanged) { - return; - } - - if (!newlyDisallowed) { - // Clear the restricted notification when user is allowed to have tethering - // function. - mNotificationUpdater.tetheringRestrictionLifted(); - return; - } - - if (mWrapper.isTetheringActive()) { - // Restricted notification is shown when tethering function is disallowed on - // user's device. - mNotificationUpdater.notifyTetheringDisabledByRestriction(); - - // Untether from all downstreams since tethering is disallowed. - mWrapper.untetherAll(); - } - // TODO(b/148139325): send tetheringSupported on restriction change - } - } - - private void disableWifiIpServingLockedCommon(int tetheringType, String ifname, int apState) { - mLog.log("Canceling WiFi tethering request -" - + " type=" + tetheringType - + " interface=" + ifname - + " state=" + apState); - - if (!TextUtils.isEmpty(ifname)) { - final TetherState ts = mTetherStates.get(ifname); - if (ts != null) { - ts.ipServer.unwanted(); - return; - } - } - - for (int i = 0; i < mTetherStates.size(); i++) { - final IpServer ipServer = mTetherStates.valueAt(i).ipServer; - if (ipServer.interfaceType() == tetheringType) { - ipServer.unwanted(); - return; - } - } - - mLog.log("Error disabling Wi-Fi IP serving; " - + (TextUtils.isEmpty(ifname) ? "no interface name specified" - : "specified interface: " + ifname)); - } - - private void disableWifiIpServingLocked(String ifname, int apState) { - // Regardless of whether we requested this transition, the AP has gone - // down. Don't try to tether again unless we're requested to do so. - // TODO: Remove this altogether, once Wi-Fi reliably gives us an - // interface name with every broadcast. - mWifiTetherRequested = false; - - disableWifiIpServingLockedCommon(TETHERING_WIFI, ifname, apState); - } - - private void disableWifiP2pIpServingLockedIfNeeded(String ifname) { - if (TextUtils.isEmpty(ifname)) return; - - disableWifiIpServingLockedCommon(TETHERING_WIFI_P2P, ifname, /* fake */ 0); - } - - private void enableWifiIpServingLocked(String ifname, int wifiIpMode) { - // Map wifiIpMode values to IpServer.Callback serving states, inferring - // from mWifiTetherRequested as a final "best guess". - final int ipServingMode; - switch (wifiIpMode) { - case IFACE_IP_MODE_TETHERED: - ipServingMode = IpServer.STATE_TETHERED; - break; - case IFACE_IP_MODE_LOCAL_ONLY: - ipServingMode = IpServer.STATE_LOCAL_ONLY; - break; - default: - mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode); - return; - } - - if (!TextUtils.isEmpty(ifname)) { - maybeTrackNewInterfaceLocked(ifname); - changeInterfaceState(ifname, ipServingMode); - } else { - mLog.e(String.format( - "Cannot enable IP serving in mode %s on missing interface name", - ipServingMode)); - } - } - - // TODO: Consider renaming to something more accurate in its description. - // This method: - // - allows requesting either tethering or local hotspot serving states - // - handles both enabling and disabling serving states - // - only tethers the first matching interface in listInterfaces() - // order of a given type - private void tetherMatchingInterfaces(int requestedState, int interfaceType) { - if (VDBG) { - Log.d(TAG, "tetherMatchingInterfaces(" + requestedState + ", " + interfaceType + ")"); - } - - String[] ifaces = null; - try { - ifaces = mNetd.interfaceGetList(); - } catch (RemoteException | ServiceSpecificException e) { - Log.e(TAG, "Error listing Interfaces", e); - return; - } - String chosenIface = null; - if (ifaces != null) { - for (String iface : ifaces) { - if (ifaceNameToType(iface) == interfaceType) { - chosenIface = iface; - break; - } - } - } - if (chosenIface == null) { - Log.e(TAG, "could not find iface of type " + interfaceType); - return; - } - - changeInterfaceState(chosenIface, requestedState); - } - - private void changeInterfaceState(String ifname, int requestedState) { - final int result; - switch (requestedState) { - case IpServer.STATE_UNAVAILABLE: - case IpServer.STATE_AVAILABLE: - result = untether(ifname); - break; - case IpServer.STATE_TETHERED: - case IpServer.STATE_LOCAL_ONLY: - result = tether(ifname, requestedState); - break; - default: - Log.wtf(TAG, "Unknown interface state: " + requestedState); - return; - } - if (result != TETHER_ERROR_NO_ERROR) { - Log.e(TAG, "unable start or stop tethering on iface " + ifname); - return; - } - } - - TetheringConfiguration getTetheringConfiguration() { - return mConfig; - } - - boolean hasTetherableConfiguration() { - final TetheringConfiguration cfg = mConfig; - final boolean hasDownstreamConfiguration = - (cfg.tetherableUsbRegexs.length != 0) - || (cfg.tetherableWifiRegexs.length != 0) - || (cfg.tetherableBluetoothRegexs.length != 0); - final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty() - || cfg.chooseUpstreamAutomatically; - - return hasDownstreamConfiguration && hasUpstreamConfiguration; - } - - // TODO - update callers to use getTetheringConfiguration(), - // which has only final members. - String[] getTetherableUsbRegexs() { - return copy(mConfig.tetherableUsbRegexs); - } - - String[] getTetherableWifiRegexs() { - return copy(mConfig.tetherableWifiRegexs); - } - - String[] getTetherableBluetoothRegexs() { - return copy(mConfig.tetherableBluetoothRegexs); - } - - int setUsbTethering(boolean enable) { - if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")"); - UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); - if (usbManager == null) { - mLog.e("setUsbTethering: failed to get UsbManager!"); - return TETHER_ERROR_SERVICE_UNAVAIL; - } - - synchronized (mPublicSync) { - usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS - : UsbManager.FUNCTION_NONE); - } - return TETHER_ERROR_NO_ERROR; - } - - private int setNcmTethering(boolean enable) { - if (VDBG) Log.d(TAG, "setNcmTethering(" + enable + ")"); - UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); - synchronized (mPublicSync) { - usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM - : UsbManager.FUNCTION_NONE); - } - return TETHER_ERROR_NO_ERROR; - } - - // TODO review API - figure out how to delete these entirely. - String[] getTetheredIfaces() { - ArrayList list = new ArrayList(); - synchronized (mPublicSync) { - for (int i = 0; i < mTetherStates.size(); i++) { - TetherState tetherState = mTetherStates.valueAt(i); - if (tetherState.lastState == IpServer.STATE_TETHERED) { - list.add(mTetherStates.keyAt(i)); - } - } - } - return list.toArray(new String[list.size()]); - } - - String[] getTetherableIfaces() { - ArrayList list = new ArrayList(); - synchronized (mPublicSync) { - for (int i = 0; i < mTetherStates.size(); i++) { - TetherState tetherState = mTetherStates.valueAt(i); - if (tetherState.lastState == IpServer.STATE_AVAILABLE) { - list.add(mTetherStates.keyAt(i)); - } - } - } - return list.toArray(new String[list.size()]); - } - - String[] getTetheredDhcpRanges() { - // TODO: this is only valid for the old DHCP server. Latest search suggests it is only used - // by WifiP2pServiceImpl to start dnsmasq: remove/deprecate after migrating callers. - return mConfig.legacyDhcpRanges; - } - - String[] getErroredIfaces() { - ArrayList list = new ArrayList(); - synchronized (mPublicSync) { - for (int i = 0; i < mTetherStates.size(); i++) { - TetherState tetherState = mTetherStates.valueAt(i); - if (tetherState.lastError != TETHER_ERROR_NO_ERROR) { - list.add(mTetherStates.keyAt(i)); - } - } - } - return list.toArray(new String[list.size()]); - } - - private void logMessage(State state, int what) { - mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what))); - } - - private boolean upstreamWanted() { - if (!mForwardedDownstreams.isEmpty()) return true; - - synchronized (mPublicSync) { - return mWifiTetherRequested; - } - } - - // Needed because the canonical source of upstream truth is just the - // upstream interface set, |mCurrentUpstreamIfaceSet|. - private boolean pertainsToCurrentUpstream(UpstreamNetworkState ns) { - if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) { - for (String ifname : ns.linkProperties.getAllInterfaceNames()) { - if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) { - return true; - } - } - } - return false; - } - - class TetherMainSM extends StateMachine { - // an interface SM has requested Tethering/Local Hotspot - static final int EVENT_IFACE_SERVING_STATE_ACTIVE = BASE_MAIN_SM + 1; - // an interface SM has unrequested Tethering/Local Hotspot - static final int EVENT_IFACE_SERVING_STATE_INACTIVE = BASE_MAIN_SM + 2; - // upstream connection change - do the right thing - static final int CMD_UPSTREAM_CHANGED = BASE_MAIN_SM + 3; - // we don't have a valid upstream conn, check again after a delay - static final int CMD_RETRY_UPSTREAM = BASE_MAIN_SM + 4; - // Events from NetworkCallbacks that we process on the main state - // machine thread on behalf of the UpstreamNetworkMonitor. - static final int EVENT_UPSTREAM_CALLBACK = BASE_MAIN_SM + 5; - // we treated the error and want now to clear it - static final int CMD_CLEAR_ERROR = BASE_MAIN_SM + 6; - static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MAIN_SM + 7; - // Events from EntitlementManager to choose upstream again. - static final int EVENT_UPSTREAM_PERMISSION_CHANGED = BASE_MAIN_SM + 8; - private final State mInitialState; - private final State mTetherModeAliveState; - - private final State mSetIpForwardingEnabledErrorState; - private final State mSetIpForwardingDisabledErrorState; - private final State mStartTetheringErrorState; - private final State mStopTetheringErrorState; - private final State mSetDnsForwardersErrorState; - - // This list is a little subtle. It contains all the interfaces that currently are - // requesting tethering, regardless of whether these interfaces are still members of - // mTetherStates. This allows us to maintain the following predicates: - // - // 1) mTetherStates contains the set of all currently existing, tetherable, link state up - // interfaces. - // 2) mNotifyList contains all state machines that may have outstanding tethering state - // that needs to be torn down. - // - // Because we excise interfaces immediately from mTetherStates, we must maintain mNotifyList - // so that the garbage collector does not clean up the state machine before it has a chance - // to tear itself down. - private final ArrayList mNotifyList; - private final IPv6TetheringCoordinator mIPv6TetheringCoordinator; - private final OffloadWrapper mOffload; - - private static final int UPSTREAM_SETTLE_TIME_MS = 10000; - - TetherMainSM(String name, Looper looper, TetheringDependencies deps) { - super(name, looper); - - mInitialState = new InitialState(); - mTetherModeAliveState = new TetherModeAliveState(); - mSetIpForwardingEnabledErrorState = new SetIpForwardingEnabledErrorState(); - mSetIpForwardingDisabledErrorState = new SetIpForwardingDisabledErrorState(); - mStartTetheringErrorState = new StartTetheringErrorState(); - mStopTetheringErrorState = new StopTetheringErrorState(); - mSetDnsForwardersErrorState = new SetDnsForwardersErrorState(); - - addState(mInitialState); - addState(mTetherModeAliveState); - addState(mSetIpForwardingEnabledErrorState); - addState(mSetIpForwardingDisabledErrorState); - addState(mStartTetheringErrorState); - addState(mStopTetheringErrorState); - addState(mSetDnsForwardersErrorState); - - mNotifyList = new ArrayList<>(); - mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog); - mOffload = new OffloadWrapper(); - - setInitialState(mInitialState); - } - - class InitialState extends State { - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - switch (message.what) { - case EVENT_IFACE_SERVING_STATE_ACTIVE: { - final IpServer who = (IpServer) message.obj; - if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); - handleInterfaceServingStateActive(message.arg1, who); - transitionTo(mTetherModeAliveState); - break; - } - case EVENT_IFACE_SERVING_STATE_INACTIVE: { - final IpServer who = (IpServer) message.obj; - if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); - handleInterfaceServingStateInactive(who); - break; - } - case EVENT_IFACE_UPDATE_LINKPROPERTIES: - // Silently ignore these for now. - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - protected boolean turnOnMainTetherSettings() { - final TetheringConfiguration cfg = mConfig; - try { - mNetd.ipfwdEnableForwarding(TAG); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e(e); - transitionTo(mSetIpForwardingEnabledErrorState); - return false; - } - - // TODO: Randomize DHCPv4 ranges, especially in hotspot mode. - // Legacy DHCP server is disabled if passed an empty ranges array - final String[] dhcpRanges = cfg.enableLegacyDhcpServer - ? cfg.legacyDhcpRanges : new String[0]; - try { - NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges); - } catch (RemoteException | ServiceSpecificException e) { - try { - // Stop and retry. - mNetd.tetherStop(); - NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges); - } catch (RemoteException | ServiceSpecificException ee) { - mLog.e(ee); - transitionTo(mStartTetheringErrorState); - return false; - } - } - mLog.log("SET main tether settings: ON"); - return true; - } - - protected boolean turnOffMainTetherSettings() { - try { - mNetd.tetherStop(); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e(e); - transitionTo(mStopTetheringErrorState); - return false; - } - try { - mNetd.ipfwdDisableForwarding(TAG); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e(e); - transitionTo(mSetIpForwardingDisabledErrorState); - return false; - } - transitionTo(mInitialState); - mLog.log("SET main tether settings: OFF"); - return true; - } - - protected void chooseUpstreamType(boolean tryCell) { - // We rebuild configuration on ACTION_CONFIGURATION_CHANGED, but we - // do not currently know how to watch for changes in DUN settings. - maybeDunSettingChanged(); - - final TetheringConfiguration config = mConfig; - final UpstreamNetworkState ns = (config.chooseUpstreamAutomatically) - ? mUpstreamNetworkMonitor.getCurrentPreferredUpstream() - : mUpstreamNetworkMonitor.selectPreferredUpstreamType( - config.preferredUpstreamIfaceTypes); - if (ns == null) { - if (tryCell) { - mUpstreamNetworkMonitor.registerMobileNetworkRequest(); - // We think mobile should be coming up; don't set a retry. - } else { - sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS); - } - } - setUpstreamNetwork(ns); - final Network newUpstream = (ns != null) ? ns.network : null; - if (mTetherUpstream != newUpstream) { - mTetherUpstream = newUpstream; - mUpstreamNetworkMonitor.setCurrentUpstream(mTetherUpstream); - reportUpstreamChanged(ns); - } - } - - protected void setUpstreamNetwork(UpstreamNetworkState ns) { - InterfaceSet ifaces = null; - if (ns != null) { - // Find the interface with the default IPv4 route. It may be the - // interface described by linkProperties, or one of the interfaces - // stacked on top of it. - mLog.i("Looking for default routes on: " + ns.linkProperties); - ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns); - mLog.i("Found upstream interface(s): " + ifaces); - } - - if (ifaces != null) { - setDnsForwarders(ns.network, ns.linkProperties); - } - notifyDownstreamsOfNewUpstreamIface(ifaces); - if (ns != null && pertainsToCurrentUpstream(ns)) { - // If we already have UpstreamNetworkState for this network update it immediately. - handleNewUpstreamNetworkState(ns); - } else if (mCurrentUpstreamIfaceSet == null) { - // There are no available upstream networks. - handleNewUpstreamNetworkState(null); - } - } - - protected void setDnsForwarders(final Network network, final LinkProperties lp) { - // TODO: Set v4 and/or v6 DNS per available connectivity. - final Collection dnses = lp.getDnsServers(); - // TODO: Properly support the absence of DNS servers. - final String[] dnsServers; - if (dnses != null && !dnses.isEmpty()) { - dnsServers = new String[dnses.size()]; - int i = 0; - for (InetAddress dns : dnses) { - dnsServers[i++] = dns.getHostAddress(); - } - } else { - dnsServers = mConfig.defaultIPv4DNS; - } - final int netId = (network != null) ? network.getNetId() : NETID_UNSET; - try { - mNetd.tetherDnsSet(netId, dnsServers); - mLog.log(String.format( - "SET DNS forwarders: network=%s dnsServers=%s", - network, Arrays.toString(dnsServers))); - } catch (RemoteException | ServiceSpecificException e) { - // TODO: Investigate how this can fail and what exactly - // happens if/when such failures occur. - mLog.e("setting DNS forwarders failed, " + e); - transitionTo(mSetDnsForwardersErrorState); - } - } - - protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) { - mCurrentUpstreamIfaceSet = ifaces; - for (IpServer ipServer : mNotifyList) { - ipServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifaces); - } - } - - protected void handleNewUpstreamNetworkState(UpstreamNetworkState ns) { - mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns); - mOffload.updateUpstreamNetworkState(ns); - } - - private void handleInterfaceServingStateActive(int mode, IpServer who) { - if (mNotifyList.indexOf(who) < 0) { - mNotifyList.add(who); - mIPv6TetheringCoordinator.addActiveDownstream(who, mode); - } - - if (mode == IpServer.STATE_TETHERED) { - // No need to notify OffloadController just yet as there are no - // "offload-able" prefixes to pass along. This will handled - // when the TISM informs Tethering of its LinkProperties. - mForwardedDownstreams.add(who); - } else { - mOffload.excludeDownstreamInterface(who.interfaceName()); - mForwardedDownstreams.remove(who); - } - - // If this is a Wi-Fi interface, notify WifiManager of the active serving state. - if (who.interfaceType() == TETHERING_WIFI) { - final WifiManager mgr = getWifiManager(); - final String iface = who.interfaceName(); - switch (mode) { - case IpServer.STATE_TETHERED: - mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_TETHERED); - break; - case IpServer.STATE_LOCAL_ONLY: - mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_LOCAL_ONLY); - break; - default: - Log.wtf(TAG, "Unknown active serving mode: " + mode); - break; - } - } - } - - private void handleInterfaceServingStateInactive(IpServer who) { - mNotifyList.remove(who); - mIPv6TetheringCoordinator.removeActiveDownstream(who); - mOffload.excludeDownstreamInterface(who.interfaceName()); - mForwardedDownstreams.remove(who); - updateConnectedClients(null /* wifiClients */); - - // If this is a Wi-Fi interface, tell WifiManager of any errors - // or the inactive serving state. - if (who.interfaceType() == TETHERING_WIFI) { - if (who.lastError() != TETHER_ERROR_NO_ERROR) { - getWifiManager().updateInterfaceIpState( - who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR); - } else { - getWifiManager().updateInterfaceIpState( - who.interfaceName(), IFACE_IP_MODE_UNSPECIFIED); - } - } - } - - @VisibleForTesting - void handleUpstreamNetworkMonitorCallback(int arg1, Object o) { - if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) { - mOffload.sendOffloadExemptPrefixes((Set) o); - return; - } - - final UpstreamNetworkState ns = (UpstreamNetworkState) o; - switch (arg1) { - case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: - mPrivateAddressCoordinator.updateUpstreamPrefix(ns); - break; - case UpstreamNetworkMonitor.EVENT_ON_LOST: - mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network); - break; - } - - if (ns == null || !pertainsToCurrentUpstream(ns)) { - // TODO: In future, this is where upstream evaluation and selection - // could be handled for notifications which include sufficient data. - // For example, after CONNECTIVITY_ACTION listening is removed, here - // is where we could observe a Wi-Fi network becoming available and - // passing validation. - if (mCurrentUpstreamIfaceSet == null) { - // If we have no upstream interface, try to run through upstream - // selection again. If, for example, IPv4 connectivity has shown up - // after IPv6 (e.g., 464xlat became available) we want the chance to - // notice and act accordingly. - chooseUpstreamType(false); - } - return; - } - - switch (arg1) { - case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES: - if (ns.network.equals(mTetherUpstream)) { - mNotificationUpdater.onUpstreamCapabilitiesChanged(ns.networkCapabilities); - } - handleNewUpstreamNetworkState(ns); - break; - case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: - chooseUpstreamType(false); - break; - case UpstreamNetworkMonitor.EVENT_ON_LOST: - // TODO: Re-evaluate possible upstreams. Currently upstream - // reevaluation is triggered via received CONNECTIVITY_ACTION - // broadcasts that result in being passed a - // TetherMainSM.CMD_UPSTREAM_CHANGED. - handleNewUpstreamNetworkState(null); - break; - default: - mLog.e("Unknown arg1 value: " + arg1); - break; - } - } - - class TetherModeAliveState extends State { - boolean mUpstreamWanted = false; - boolean mTryCell = true; - - @Override - public void enter() { - // If turning on main tether settings fails, we have already - // transitioned to an error state; exit early. - if (!turnOnMainTetherSettings()) { - return; - } - - mPrivateAddressCoordinator.maybeRemoveDeprecatedUpstreams(); - mUpstreamNetworkMonitor.startObserveAllNetworks(); - - // TODO: De-duplicate with updateUpstreamWanted() below. - if (upstreamWanted()) { - mUpstreamWanted = true; - mOffload.start(); - chooseUpstreamType(true); - mTryCell = false; - } - - // TODO: Check the upstream interface if it is managed by BPF offload. - mBpfCoordinator.startPolling(); - } - - @Override - public void exit() { - mOffload.stop(); - mUpstreamNetworkMonitor.stop(); - notifyDownstreamsOfNewUpstreamIface(null); - handleNewUpstreamNetworkState(null); - if (mTetherUpstream != null) { - mTetherUpstream = null; - reportUpstreamChanged(null); - } - mBpfCoordinator.stopPolling(); - } - - private boolean updateUpstreamWanted() { - final boolean previousUpstreamWanted = mUpstreamWanted; - mUpstreamWanted = upstreamWanted(); - if (mUpstreamWanted != previousUpstreamWanted) { - if (mUpstreamWanted) { - mOffload.start(); - } else { - mOffload.stop(); - } - } - return previousUpstreamWanted; - } - - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - boolean retValue = true; - switch (message.what) { - case EVENT_IFACE_SERVING_STATE_ACTIVE: { - IpServer who = (IpServer) message.obj; - if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); - handleInterfaceServingStateActive(message.arg1, who); - who.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, - mCurrentUpstreamIfaceSet); - // If there has been a change and an upstream is now - // desired, kick off the selection process. - final boolean previousUpstreamWanted = updateUpstreamWanted(); - if (!previousUpstreamWanted && mUpstreamWanted) { - chooseUpstreamType(true); - } - break; - } - case EVENT_IFACE_SERVING_STATE_INACTIVE: { - IpServer who = (IpServer) message.obj; - if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); - handleInterfaceServingStateInactive(who); - - if (mNotifyList.isEmpty()) { - // This transitions us out of TetherModeAliveState, - // either to InitialState or an error state. - turnOffMainTetherSettings(); - break; - } - - if (DBG) { - Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() - + " live requests:"); - for (IpServer o : mNotifyList) { - Log.d(TAG, " " + o); - } - } - // If there has been a change and an upstream is no - // longer desired, release any mobile requests. - final boolean previousUpstreamWanted = updateUpstreamWanted(); - if (previousUpstreamWanted && !mUpstreamWanted) { - mUpstreamNetworkMonitor.releaseMobileNetworkRequest(); - } - break; - } - case EVENT_IFACE_UPDATE_LINKPROPERTIES: { - final LinkProperties newLp = (LinkProperties) message.obj; - if (message.arg1 == IpServer.STATE_TETHERED) { - mOffload.updateDownstreamLinkProperties(newLp); - } else { - mOffload.excludeDownstreamInterface(newLp.getInterfaceName()); - } - break; - } - case EVENT_UPSTREAM_PERMISSION_CHANGED: - case CMD_UPSTREAM_CHANGED: - updateUpstreamWanted(); - if (!mUpstreamWanted) break; - - // Need to try DUN immediately if Wi-Fi goes down. - chooseUpstreamType(true); - mTryCell = false; - break; - case CMD_RETRY_UPSTREAM: - updateUpstreamWanted(); - if (!mUpstreamWanted) break; - - chooseUpstreamType(mTryCell); - mTryCell = !mTryCell; - break; - case EVENT_UPSTREAM_CALLBACK: { - updateUpstreamWanted(); - if (mUpstreamWanted) { - handleUpstreamNetworkMonitorCallback(message.arg1, message.obj); - } - break; - } - default: - retValue = false; - break; - } - return retValue; - } - } - - class ErrorState extends State { - private int mErrorNotification; - - @Override - public boolean processMessage(Message message) { - boolean retValue = true; - switch (message.what) { - case EVENT_IFACE_SERVING_STATE_ACTIVE: - IpServer who = (IpServer) message.obj; - who.sendMessage(mErrorNotification); - break; - case CMD_CLEAR_ERROR: - mErrorNotification = TETHER_ERROR_NO_ERROR; - transitionTo(mInitialState); - break; - default: - retValue = false; - } - return retValue; - } - - void notify(int msgType) { - mErrorNotification = msgType; - for (IpServer ipServer : mNotifyList) { - ipServer.sendMessage(msgType); - } - } - - } - - class SetIpForwardingEnabledErrorState extends ErrorState { - @Override - public void enter() { - Log.e(TAG, "Error in setIpForwardingEnabled"); - notify(IpServer.CMD_IP_FORWARDING_ENABLE_ERROR); - } - } - - class SetIpForwardingDisabledErrorState extends ErrorState { - @Override - public void enter() { - Log.e(TAG, "Error in setIpForwardingDisabled"); - notify(IpServer.CMD_IP_FORWARDING_DISABLE_ERROR); - } - } - - class StartTetheringErrorState extends ErrorState { - @Override - public void enter() { - Log.e(TAG, "Error in startTethering"); - notify(IpServer.CMD_START_TETHERING_ERROR); - try { - mNetd.ipfwdDisableForwarding(TAG); - } catch (RemoteException | ServiceSpecificException e) { } - } - } - - class StopTetheringErrorState extends ErrorState { - @Override - public void enter() { - Log.e(TAG, "Error in stopTethering"); - notify(IpServer.CMD_STOP_TETHERING_ERROR); - try { - mNetd.ipfwdDisableForwarding(TAG); - } catch (RemoteException | ServiceSpecificException e) { } - } - } - - class SetDnsForwardersErrorState extends ErrorState { - @Override - public void enter() { - Log.e(TAG, "Error in setDnsForwarders"); - notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR); - try { - mNetd.tetherStop(); - } catch (RemoteException | ServiceSpecificException e) { } - try { - mNetd.ipfwdDisableForwarding(TAG); - } catch (RemoteException | ServiceSpecificException e) { } - } - } - - // A wrapper class to handle multiple situations where several calls to - // the OffloadController need to happen together. - // - // TODO: This suggests that the interface between OffloadController and - // Tethering is in need of improvement. Refactor these calls into the - // OffloadController implementation. - class OffloadWrapper { - public void start() { - final int status = mOffloadController.start() ? TETHER_HARDWARE_OFFLOAD_STARTED - : TETHER_HARDWARE_OFFLOAD_FAILED; - updateOffloadStatus(status); - sendOffloadExemptPrefixes(); - } - - public void stop() { - mOffloadController.stop(); - updateOffloadStatus(TETHER_HARDWARE_OFFLOAD_STOPPED); - } - - public void updateUpstreamNetworkState(UpstreamNetworkState ns) { - mOffloadController.setUpstreamLinkProperties( - (ns != null) ? ns.linkProperties : null); - } - - public void updateDownstreamLinkProperties(LinkProperties newLp) { - // Update the list of offload-exempt prefixes before adding - // new prefixes on downstream interfaces to the offload HAL. - sendOffloadExemptPrefixes(); - mOffloadController.notifyDownstreamLinkProperties(newLp); - } - - public void excludeDownstreamInterface(String ifname) { - // This and other interfaces may be in local-only hotspot mode; - // resend all local prefixes to the OffloadController. - sendOffloadExemptPrefixes(); - mOffloadController.removeDownstreamInterface(ifname); - } - - public void sendOffloadExemptPrefixes() { - sendOffloadExemptPrefixes(mUpstreamNetworkMonitor.getLocalPrefixes()); - } - - public void sendOffloadExemptPrefixes(final Set localPrefixes) { - // Add in well-known minimum set. - PrefixUtils.addNonForwardablePrefixes(localPrefixes); - // Add tragically hardcoded prefixes. - localPrefixes.add(PrefixUtils.DEFAULT_WIFI_P2P_PREFIX); - - // Maybe add prefixes or addresses for downstreams, depending on - // the IP serving mode of each. - for (IpServer ipServer : mNotifyList) { - final LinkProperties lp = ipServer.linkProperties(); - - switch (ipServer.servingMode()) { - case IpServer.STATE_UNAVAILABLE: - case IpServer.STATE_AVAILABLE: - // No usable LinkProperties in these states. - continue; - case IpServer.STATE_TETHERED: - // Only add IPv4 /32 and IPv6 /128 prefixes. The - // directly-connected prefixes will be sent as - // downstream "offload-able" prefixes. - for (LinkAddress addr : lp.getAllLinkAddresses()) { - final InetAddress ip = addr.getAddress(); - if (ip.isLinkLocalAddress()) continue; - localPrefixes.add(PrefixUtils.ipAddressAsPrefix(ip)); - } - break; - case IpServer.STATE_LOCAL_ONLY: - // Add prefixes covering all local IPs. - localPrefixes.addAll(PrefixUtils.localPrefixesFrom(lp)); - break; - } - } - - mOffloadController.setLocalPrefixes(localPrefixes); - } - - private void updateOffloadStatus(final int newStatus) { - if (newStatus == mOffloadStatus) return; - - mOffloadStatus = newStatus; - reportOffloadStatusChanged(mOffloadStatus); - } - } - } - - private void startTrackDefaultNetwork() { - mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest(), - mEntitlementMgr); - } - - /** Get the latest value of the tethering entitlement check. */ - void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver, - boolean showEntitlementUi) { - if (receiver == null) return; - - mHandler.post(() -> { - mEntitlementMgr.requestLatestTetheringEntitlementResult(type, receiver, - showEntitlementUi); - }); - } - - /** Register tethering event callback */ - void registerTetheringEventCallback(ITetheringEventCallback callback) { - final boolean hasListPermission = - hasCallingPermission(NETWORK_SETTINGS) - || hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK) - || hasCallingPermission(NETWORK_STACK); - mHandler.post(() -> { - mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission)); - final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel(); - parcel.tetheringSupported = isTetheringSupported(); - parcel.upstreamNetwork = mTetherUpstream; - parcel.config = mConfig.toStableParcelable(); - parcel.states = - mTetherStatesParcel != null ? mTetherStatesParcel : emptyTetherStatesParcel(); - parcel.tetheredClients = hasListPermission - ? mConnectedClientsTracker.getLastTetheredClients() - : Collections.emptyList(); - parcel.offloadStatus = mOffloadStatus; - try { - callback.onCallbackStarted(parcel); - } catch (RemoteException e) { - // Not really very much to do here. - } - }); - } - - private TetherStatesParcel emptyTetherStatesParcel() { - final TetherStatesParcel parcel = new TetherStatesParcel(); - parcel.availableList = new String[0]; - parcel.tetheredList = new String[0]; - parcel.localOnlyList = new String[0]; - parcel.erroredIfaceList = new String[0]; - parcel.lastErrorList = new int[0]; - - return parcel; - } - - private boolean hasCallingPermission(@NonNull String permission) { - return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED; - } - - /** Unregister tethering event callback */ - void unregisterTetheringEventCallback(ITetheringEventCallback callback) { - mHandler.post(() -> { - mTetheringEventCallbacks.unregister(callback); - }); - } - - private void reportUpstreamChanged(UpstreamNetworkState ns) { - final int length = mTetheringEventCallbacks.beginBroadcast(); - final Network network = (ns != null) ? ns.network : null; - final NetworkCapabilities capabilities = (ns != null) ? ns.networkCapabilities : null; - try { - for (int i = 0; i < length; i++) { - try { - mTetheringEventCallbacks.getBroadcastItem(i).onUpstreamChanged(network); - } catch (RemoteException e) { - // Not really very much to do here. - } - } - } finally { - mTetheringEventCallbacks.finishBroadcast(); - } - // Need to notify capabilities change after upstream network changed because new network's - // capabilities should be checked every time. - mNotificationUpdater.onUpstreamCapabilitiesChanged(capabilities); - } - - private void reportConfigurationChanged(TetheringConfigurationParcel config) { - final int length = mTetheringEventCallbacks.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - try { - mTetheringEventCallbacks.getBroadcastItem(i).onConfigurationChanged(config); - // TODO(b/148139325): send tetheringSupported on configuration change - } catch (RemoteException e) { - // Not really very much to do here. - } - } - } finally { - mTetheringEventCallbacks.finishBroadcast(); - } - } - - private void reportTetherStateChanged(TetherStatesParcel states) { - final int length = mTetheringEventCallbacks.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - try { - mTetheringEventCallbacks.getBroadcastItem(i).onTetherStatesChanged(states); - } catch (RemoteException e) { - // Not really very much to do here. - } - } - } finally { - mTetheringEventCallbacks.finishBroadcast(); - } - } - - private void reportTetherClientsChanged(List clients) { - final int length = mTetheringEventCallbacks.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - try { - final CallbackCookie cookie = - (CallbackCookie) mTetheringEventCallbacks.getBroadcastCookie(i); - if (!cookie.hasListClientsPermission) continue; - mTetheringEventCallbacks.getBroadcastItem(i).onTetherClientsChanged(clients); - } catch (RemoteException e) { - // Not really very much to do here. - } - } - } finally { - mTetheringEventCallbacks.finishBroadcast(); - } - } - - private void reportOffloadStatusChanged(final int status) { - final int length = mTetheringEventCallbacks.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - try { - mTetheringEventCallbacks.getBroadcastItem(i).onOffloadStatusChanged(status); - } catch (RemoteException e) { - // Not really very much to do here. - } - } - } finally { - mTetheringEventCallbacks.finishBroadcast(); - } - } - - // if ro.tether.denied = true we default to no tethering - // gservices could set the secure setting to 1 though to enable it on a build where it - // had previously been turned off. - boolean isTetheringSupported() { - final int defaultVal = mDeps.isTetheringDenied() ? 0 : 1; - final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; - final boolean tetherEnabledInSettings = tetherSupported - && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); - - return tetherEnabledInSettings && hasTetherableConfiguration() - && !isProvisioningNeededButUnavailable(); - } - - void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { - // Binder.java closes the resource for us. - @SuppressWarnings("resource") - final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump."); - return; - } - - pw.println("Tethering:"); - pw.increaseIndent(); - - pw.println("Configuration:"); - pw.increaseIndent(); - final TetheringConfiguration cfg = mConfig; - cfg.dump(pw); - pw.decreaseIndent(); - - pw.println("Entitlement:"); - pw.increaseIndent(); - mEntitlementMgr.dump(pw); - pw.decreaseIndent(); - - synchronized (mPublicSync) { - pw.println("Tether state:"); - pw.increaseIndent(); - for (int i = 0; i < mTetherStates.size(); i++) { - final String iface = mTetherStates.keyAt(i); - final TetherState tetherState = mTetherStates.valueAt(i); - pw.print(iface + " - "); - - switch (tetherState.lastState) { - case IpServer.STATE_UNAVAILABLE: - pw.print("UnavailableState"); - break; - case IpServer.STATE_AVAILABLE: - pw.print("AvailableState"); - break; - case IpServer.STATE_TETHERED: - pw.print("TetheredState"); - break; - case IpServer.STATE_LOCAL_ONLY: - pw.print("LocalHotspotState"); - break; - default: - pw.print("UnknownState"); - break; - } - pw.println(" - lastError = " + tetherState.lastError); - } - pw.println("Upstream wanted: " + upstreamWanted()); - pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet); - pw.decreaseIndent(); - } - - pw.println("Hardware offload:"); - pw.increaseIndent(); - mOffloadController.dump(pw); - pw.decreaseIndent(); - - pw.println("BPF offload:"); - pw.increaseIndent(); - mBpfCoordinator.dump(pw); - pw.decreaseIndent(); - - pw.println("Private address coordinator:"); - pw.increaseIndent(); - mPrivateAddressCoordinator.dump(pw); - pw.decreaseIndent(); - - pw.println("Log:"); - pw.increaseIndent(); - if (argsContain(args, "--short")) { - pw.println(""); - } else { - mLog.dump(fd, pw, args); - } - pw.decreaseIndent(); - - pw.decreaseIndent(); - } - - private static boolean argsContain(String[] args, String target) { - for (String arg : args) { - if (target.equals(arg)) return true; - } - return false; - } - - private void updateConnectedClients(final List wifiClients) { - if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, wifiClients)) { - reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients()); - } - } - - private IpServer.Callback makeControlCallback() { - return new IpServer.Callback() { - @Override - public void updateInterfaceState(IpServer who, int state, int lastError) { - notifyInterfaceStateChange(who, state, lastError); - } - - @Override - public void updateLinkProperties(IpServer who, LinkProperties newLp) { - notifyLinkPropertiesChanged(who, newLp); - } - - @Override - public void dhcpLeasesChanged() { - updateConnectedClients(null /* wifiClients */); - } - - @Override - public void requestEnableTethering(int tetheringType, boolean enabled) { - enableTetheringInternal(tetheringType, enabled, null); - } - }; - } - - // TODO: Move into TetherMainSM. - private void notifyInterfaceStateChange(IpServer who, int state, int error) { - final String iface = who.interfaceName(); - synchronized (mPublicSync) { - final TetherState tetherState = mTetherStates.get(iface); - if (tetherState != null && tetherState.ipServer.equals(who)) { - tetherState.lastState = state; - tetherState.lastError = error; - } else { - if (DBG) Log.d(TAG, "got notification from stale iface " + iface); - } - } - - mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error)); - - // If TetherMainSM is in ErrorState, TetherMainSM stays there. - // Thus we give a chance for TetherMainSM to recover to InitialState - // by sending CMD_CLEAR_ERROR - if (error == TETHER_ERROR_INTERNAL_ERROR) { - mTetherMainSM.sendMessage(TetherMainSM.CMD_CLEAR_ERROR, who); - } - int which; - switch (state) { - case IpServer.STATE_UNAVAILABLE: - case IpServer.STATE_AVAILABLE: - which = TetherMainSM.EVENT_IFACE_SERVING_STATE_INACTIVE; - break; - case IpServer.STATE_TETHERED: - case IpServer.STATE_LOCAL_ONLY: - which = TetherMainSM.EVENT_IFACE_SERVING_STATE_ACTIVE; - break; - default: - Log.wtf(TAG, "Unknown interface state: " + state); - return; - } - mTetherMainSM.sendMessage(which, state, 0, who); - sendTetherStateChangedBroadcast(); - } - - private void notifyLinkPropertiesChanged(IpServer who, LinkProperties newLp) { - final String iface = who.interfaceName(); - final int state; - synchronized (mPublicSync) { - final TetherState tetherState = mTetherStates.get(iface); - if (tetherState != null && tetherState.ipServer.equals(who)) { - state = tetherState.lastState; - } else { - mLog.log("got notification from stale iface " + iface); - return; - } - } - - mLog.log(String.format( - "OBSERVED LinkProperties update iface=%s state=%s lp=%s", - iface, IpServer.getStateString(state), newLp)); - final int which = TetherMainSM.EVENT_IFACE_UPDATE_LINKPROPERTIES; - mTetherMainSM.sendMessage(which, state, 0, newLp); - } - - private void maybeTrackNewInterfaceLocked(final String iface) { - // If we don't care about this type of interface, ignore. - final int interfaceType = ifaceNameToType(iface); - if (interfaceType == TETHERING_INVALID) { - mLog.log(iface + " is not a tetherable iface, ignoring"); - return; - } - maybeTrackNewInterfaceLocked(iface, interfaceType); - } - - private void maybeTrackNewInterfaceLocked(final String iface, int interfaceType) { - // If we have already started a TISM for this interface, skip. - if (mTetherStates.containsKey(iface)) { - mLog.log("active iface (" + iface + ") reported as added, ignoring"); - return; - } - - mLog.log("adding TetheringInterfaceStateMachine for: " + iface); - final TetherState tetherState = new TetherState( - new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator, - makeControlCallback(), mConfig.enableLegacyDhcpServer, - mConfig.isBpfOffloadEnabled(), mPrivateAddressCoordinator, - mDeps.getIpServerDependencies())); - mTetherStates.put(iface, tetherState); - tetherState.ipServer.start(); - } - - private void stopTrackingInterfaceLocked(final String iface) { - final TetherState tetherState = mTetherStates.get(iface); - if (tetherState == null) { - mLog.log("attempting to remove unknown iface (" + iface + "), ignoring"); - return; - } - tetherState.ipServer.stop(); - mLog.log("removing TetheringInterfaceStateMachine for: " + iface); - mTetherStates.remove(iface); - } - - private static String[] copy(String[] strarray) { - return Arrays.copyOf(strarray, strarray.length); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java deleted file mode 100644 index 799637c9b1c5..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright (C) 2017 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.networkstack.tethering; - -import static android.content.Context.TELEPHONY_SERVICE; -import static android.net.ConnectivityManager.TYPE_ETHERNET; -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; -import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; -import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; - -import android.content.Context; -import android.content.res.Resources; -import android.net.TetheringConfigurationParcel; -import android.net.util.SharedLog; -import android.provider.DeviceConfig; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.text.TextUtils; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.StringJoiner; - -/** - * A utility class to encapsulate the various tethering configuration elements. - * - * This configuration data includes elements describing upstream properties - * (preferred and required types of upstream connectivity as well as default - * DNS servers to use if none are available) and downstream properties (such - * as regular expressions use to match suitable downstream interfaces and the - * DHCPv4 ranges to use). - * - * @hide - */ -public class TetheringConfiguration { - private static final String TAG = TetheringConfiguration.class.getSimpleName(); - - private static final String[] EMPTY_STRING_ARRAY = new String[0]; - - // Default ranges used for the legacy DHCP server. - // USB is 192.168.42.1 and 255.255.255.0 - // Wifi is 192.168.43.1 and 255.255.255.0 - // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1 - // with 255.255.255.0 - // P2P is 192.168.49.1 and 255.255.255.0 - private static final String[] LEGACY_DHCP_DEFAULT_RANGE = { - "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254", - "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254", - "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254", - "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254", - }; - - private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"}; - - /** - * Override enabling BPF offload configuration for tethering. - */ - public static final String OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD = - "override_tether_enable_bpf_offload"; - - /** - * Use the old dnsmasq DHCP server for tethering instead of the framework implementation. - */ - public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = - "tether_enable_legacy_dhcp_server"; - - public static final String USE_LEGACY_WIFI_P2P_DEDICATED_IP = - "use_legacy_wifi_p2p_dedicated_ip"; - - /** - * Flag use to enable select all prefix ranges feature. - * TODO: Remove this flag if there are no problems after M-2020-12 rolls out. - */ - public static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES = - "tether_enable_select_all_prefix_ranges"; - - /** - * Default value that used to periodic polls tether offload stats from tethering offload HAL - * to make the data warnings work. - */ - public static final int DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS = 5000; - - public final String[] tetherableUsbRegexs; - public final String[] tetherableWifiRegexs; - public final String[] tetherableWigigRegexs; - public final String[] tetherableWifiP2pRegexs; - public final String[] tetherableBluetoothRegexs; - public final String[] tetherableNcmRegexs; - public final boolean isDunRequired; - public final boolean chooseUpstreamAutomatically; - public final Collection preferredUpstreamIfaceTypes; - public final String[] legacyDhcpRanges; - public final String[] defaultIPv4DNS; - public final boolean enableLegacyDhcpServer; - - public final String[] provisioningApp; - public final String provisioningAppNoUi; - public final int provisioningCheckPeriod; - public final String provisioningResponse; - - public final int activeDataSubId; - - private final int mOffloadPollInterval; - // TODO: Add to TetheringConfigurationParcel if required. - private final boolean mEnableBpfOffload; - private final boolean mEnableWifiP2pDedicatedIp; - - private final boolean mEnableSelectAllPrefixRange; - - public TetheringConfiguration(Context ctx, SharedLog log, int id) { - final SharedLog configLog = log.forSubComponent("config"); - - activeDataSubId = id; - Resources res = getResources(ctx, activeDataSubId); - - tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs); - tetherableNcmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs); - // TODO: Evaluate deleting this altogether now that Wi-Fi always passes - // us an interface name. Careful consideration needs to be given to - // implications for Settings and for provisioning checks. - tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs); - tetherableWigigRegexs = getResourceStringArray(res, R.array.config_tether_wigig_regexs); - tetherableWifiP2pRegexs = getResourceStringArray( - res, R.array.config_tether_wifi_p2p_regexs); - tetherableBluetoothRegexs = getResourceStringArray( - res, R.array.config_tether_bluetooth_regexs); - - isDunRequired = checkDunRequired(ctx); - - chooseUpstreamAutomatically = getResourceBoolean( - res, R.bool.config_tether_upstream_automatic, false /** defaultValue */); - preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired); - - legacyDhcpRanges = getLegacyDhcpRanges(res); - defaultIPv4DNS = copy(DEFAULT_IPV4_DNS); - mEnableBpfOffload = getEnableBpfOffload(res); - enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); - - provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); - provisioningAppNoUi = getResourceString(res, - R.string.config_mobile_hotspot_provision_app_no_ui); - provisioningCheckPeriod = getResourceInteger(res, - R.integer.config_mobile_hotspot_provision_check_period, - 0 /* No periodic re-check */); - provisioningResponse = getResourceString(res, - R.string.config_mobile_hotspot_provision_response); - - mOffloadPollInterval = getResourceInteger(res, - R.integer.config_tether_offload_poll_interval, - DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - - mEnableWifiP2pDedicatedIp = getResourceBoolean(res, - R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip, - false /* defaultValue */); - - // Flags should normally not be booleans, but this is a kill-switch flag that is only used - // to turn off the feature, so binary rollback problems do not apply. - mEnableSelectAllPrefixRange = getDeviceConfigBoolean( - TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true /* defaultValue */); - - configLog.log(toString()); - } - - /** Check whether input interface belong to usb.*/ - public boolean isUsb(String iface) { - return matchesDownstreamRegexs(iface, tetherableUsbRegexs); - } - - /** Check whether input interface belong to wifi.*/ - public boolean isWifi(String iface) { - return matchesDownstreamRegexs(iface, tetherableWifiRegexs); - } - - /** Check whether input interface belong to wigig.*/ - public boolean isWigig(String iface) { - return matchesDownstreamRegexs(iface, tetherableWigigRegexs); - } - - /** Check whether this interface is Wifi P2P interface. */ - public boolean isWifiP2p(String iface) { - return matchesDownstreamRegexs(iface, tetherableWifiP2pRegexs); - } - - /** Check whether using legacy mode for wifi P2P. */ - public boolean isWifiP2pLegacyTetheringMode() { - return (tetherableWifiP2pRegexs == null || tetherableWifiP2pRegexs.length == 0); - } - - /** Check whether input interface belong to bluetooth.*/ - public boolean isBluetooth(String iface) { - return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs); - } - - /** Check if interface is ncm */ - public boolean isNcm(String iface) { - return matchesDownstreamRegexs(iface, tetherableNcmRegexs); - } - - /** Check whether no ui entitlement application is available.*/ - public boolean hasMobileHotspotProvisionApp() { - return !TextUtils.isEmpty(provisioningAppNoUi); - } - - /** Check whether dedicated wifi p2p address is enabled. */ - public boolean shouldEnableWifiP2pDedicatedIp() { - return mEnableWifiP2pDedicatedIp; - } - - /** Does the dumping.*/ - public void dump(PrintWriter pw) { - pw.print("activeDataSubId: "); - pw.println(activeDataSubId); - - dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs); - dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs); - dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs); - dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs); - dumpStringArray(pw, "tetherableNcmRegexs", tetherableNcmRegexs); - - pw.print("isDunRequired: "); - pw.println(isDunRequired); - - pw.print("chooseUpstreamAutomatically: "); - pw.println(chooseUpstreamAutomatically); - pw.print("legacyPreredUpstreamIfaceTypes: "); - pw.println(Arrays.toString(toIntArray(preferredUpstreamIfaceTypes))); - - dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges); - dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS); - - pw.print("offloadPollInterval: "); - pw.println(mOffloadPollInterval); - - dumpStringArray(pw, "provisioningApp", provisioningApp); - pw.print("provisioningAppNoUi: "); - pw.println(provisioningAppNoUi); - - pw.print("enableBpfOffload: "); - pw.println(mEnableBpfOffload); - - pw.print("enableLegacyDhcpServer: "); - pw.println(enableLegacyDhcpServer); - - pw.print("enableWifiP2pDedicatedIp: "); - pw.println(mEnableWifiP2pDedicatedIp); - - pw.print("mEnableSelectAllPrefixRange: "); - pw.println(mEnableSelectAllPrefixRange); - } - - /** Returns the string representation of this object.*/ - public String toString() { - final StringJoiner sj = new StringJoiner(" "); - sj.add(String.format("activeDataSubId:%d", activeDataSubId)); - sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs))); - sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs))); - sj.add(String.format("tetherableWifiP2pRegexs:%s", makeString(tetherableWifiP2pRegexs))); - sj.add(String.format("tetherableBluetoothRegexs:%s", - makeString(tetherableBluetoothRegexs))); - sj.add(String.format("isDunRequired:%s", isDunRequired)); - sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically)); - sj.add(String.format("offloadPollInterval:%d", mOffloadPollInterval)); - sj.add(String.format("preferredUpstreamIfaceTypes:%s", - toIntArray(preferredUpstreamIfaceTypes))); - sj.add(String.format("provisioningApp:%s", makeString(provisioningApp))); - sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi)); - sj.add(String.format("enableBpfOffload:%s", mEnableBpfOffload)); - sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer)); - return String.format("TetheringConfiguration{%s}", sj.toString()); - } - - private static void dumpStringArray(PrintWriter pw, String label, String[] values) { - pw.print(label); - pw.print(": "); - - if (values != null) { - final StringJoiner sj = new StringJoiner(", ", "[", "]"); - for (String value : values) sj.add(value); - pw.print(sj.toString()); - } else { - pw.print("null"); - } - - pw.println(); - } - - private static String makeString(String[] strings) { - if (strings == null) return "null"; - final StringJoiner sj = new StringJoiner(",", "[", "]"); - for (String s : strings) sj.add(s); - return sj.toString(); - } - - /** Check whether dun is required. */ - public static boolean checkDunRequired(Context ctx) { - final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE); - // TelephonyManager would uses the active data subscription, which should be the one used - // by tethering. - return (tm != null) ? tm.isTetheringApnRequired() : false; - } - - public int getOffloadPollInterval() { - return mOffloadPollInterval; - } - - public boolean isBpfOffloadEnabled() { - return mEnableBpfOffload; - } - - public boolean isSelectAllPrefixRangeEnabled() { - return mEnableSelectAllPrefixRange; - } - - private static Collection getUpstreamIfaceTypes(Resources res, boolean dunRequired) { - final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types); - final ArrayList upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); - for (int i : ifaceTypes) { - switch (i) { - case TYPE_MOBILE: - case TYPE_MOBILE_HIPRI: - if (dunRequired) continue; - break; - case TYPE_MOBILE_DUN: - if (!dunRequired) continue; - break; - } - upstreamIfaceTypes.add(i); - } - - // Fix up upstream interface types for DUN or mobile. NOTE: independent - // of the value of |dunRequired|, cell data of one form or another is - // *always* an upstream, regardless of the upstream interface types - // specified by configuration resources. - if (dunRequired) { - appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_DUN); - } else { - // Do not modify if a cellular interface type is already present in the - // upstream interface types. Add TYPE_MOBILE and TYPE_MOBILE_HIPRI if no - // cellular interface types are found in the upstream interface types. - // This preserves backwards compatibility and prevents the DUN and default - // mobile types incorrectly appearing together, which could happen on - // previous releases in the common case where checkDunRequired returned - // DUN_UNSPECIFIED. - if (!containsOneOf(upstreamIfaceTypes, TYPE_MOBILE, TYPE_MOBILE_HIPRI)) { - upstreamIfaceTypes.add(TYPE_MOBILE); - upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI); - } - } - - // Always make sure our good friend Ethernet is present. - // TODO: consider unilaterally forcing this at the front. - prependIfNotPresent(upstreamIfaceTypes, TYPE_ETHERNET); - - return upstreamIfaceTypes; - } - - private static boolean matchesDownstreamRegexs(String iface, String[] regexs) { - for (String regex : regexs) { - if (iface.matches(regex)) return true; - } - return false; - } - - private static String[] getLegacyDhcpRanges(Resources res) { - final String[] fromResource = getResourceStringArray(res, R.array.config_tether_dhcp_range); - if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) { - return fromResource; - } - return copy(LEGACY_DHCP_DEFAULT_RANGE); - } - - private static String getResourceString(Resources res, final int resId) { - try { - return res.getString(resId); - } catch (Resources.NotFoundException e) { - return ""; - } - } - - private static boolean getResourceBoolean(Resources res, int resId, boolean defaultValue) { - try { - return res.getBoolean(resId); - } catch (Resources.NotFoundException e404) { - return defaultValue; - } - } - - private static String[] getResourceStringArray(Resources res, int resId) { - try { - final String[] strArray = res.getStringArray(resId); - return (strArray != null) ? strArray : EMPTY_STRING_ARRAY; - } catch (Resources.NotFoundException e404) { - return EMPTY_STRING_ARRAY; - } - } - - private static int getResourceInteger(Resources res, int resId, int defaultValue) { - try { - return res.getInteger(resId); - } catch (Resources.NotFoundException e404) { - return defaultValue; - } - } - - private boolean getEnableBpfOffload(final Resources res) { - // Get BPF offload config - // Priority 1: Device config - // Priority 2: Resource config - // Priority 3: Default value - final boolean defaultValue = getResourceBoolean( - res, R.bool.config_tether_enable_bpf_offload, true /** default value */); - - return getDeviceConfigBoolean(OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD, defaultValue); - } - - private boolean getEnableLegacyDhcpServer(final Resources res) { - return getResourceBoolean( - res, R.bool.config_tether_enable_legacy_dhcp_server, false /** defaultValue */) - || getDeviceConfigBoolean( - TETHER_ENABLE_LEGACY_DHCP_SERVER, false /** defaultValue */); - } - - private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) { - // Due to the limitation of static mock for testing, using #getDeviceConfigProperty instead - // of DeviceConfig#getBoolean. If using #getBoolean here, the test can't know that the - // returned boolean value comes from device config or default value (because of null - // property string). See the test case testBpfOffload{*} in TetheringConfigurationTest.java. - final String value = getDeviceConfigProperty(name); - return value != null ? Boolean.parseBoolean(value) : defaultValue; - } - - @VisibleForTesting - protected String getDeviceConfigProperty(String name) { - return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name); - } - - private Resources getResources(Context ctx, int subId) { - if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - return getResourcesForSubIdWrapper(ctx, subId); - } else { - return ctx.getResources(); - } - } - - @VisibleForTesting - protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { - return SubscriptionManager.getResourcesForSubId(ctx, subId); - } - - private static String[] copy(String[] strarray) { - return Arrays.copyOf(strarray, strarray.length); - } - - private static void prependIfNotPresent(ArrayList list, int value) { - if (list.contains(value)) return; - list.add(0, value); - } - - private static void appendIfNotPresent(ArrayList list, int value) { - if (list.contains(value)) return; - list.add(value); - } - - private static boolean containsOneOf(ArrayList list, Integer... values) { - for (Integer value : values) { - if (list.contains(value)) return true; - } - return false; - } - - private static int[] toIntArray(Collection values) { - final int[] result = new int[values.size()]; - int index = 0; - for (Integer value : values) { - result[index++] = value; - } - return result; - } - - /** - * Convert this TetheringConfiguration to a TetheringConfigurationParcel. - */ - public TetheringConfigurationParcel toStableParcelable() { - final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel(); - parcel.subId = activeDataSubId; - parcel.tetherableUsbRegexs = tetherableUsbRegexs; - parcel.tetherableWifiRegexs = tetherableWifiRegexs; - parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs; - parcel.isDunRequired = isDunRequired; - parcel.chooseUpstreamAutomatically = chooseUpstreamAutomatically; - - parcel.preferredUpstreamIfaceTypes = toIntArray(preferredUpstreamIfaceTypes); - - parcel.legacyDhcpRanges = legacyDhcpRanges; - parcel.defaultIPv4DNS = defaultIPv4DNS; - parcel.enableLegacyDhcpServer = enableLegacyDhcpServer; - parcel.provisioningApp = provisioningApp; - parcel.provisioningAppNoUi = provisioningAppNoUi; - parcel.provisioningCheckPeriod = provisioningCheckPeriod; - return parcel; - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java deleted file mode 100644 index 45b914178e97..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2017 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.networkstack.tethering; - -import android.app.usage.NetworkStatsManager; -import android.bluetooth.BluetoothAdapter; -import android.content.Context; -import android.net.INetd; -import android.net.NetworkRequest; -import android.net.ip.IpServer; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.SystemProperties; -import android.text.TextUtils; - -import androidx.annotation.NonNull; - -import com.android.internal.util.StateMachine; - -import java.util.ArrayList; - - -/** - * Capture tethering dependencies, for injection. - * - * @hide - */ -public abstract class TetheringDependencies { - /** - * Get a reference to the BpfCoordinator to be used by tethering. - */ - public @NonNull BpfCoordinator getBpfCoordinator( - @NonNull BpfCoordinator.Dependencies deps) { - return new BpfCoordinator(deps); - } - - /** - * Get a reference to the offload hardware interface to be used by tethering. - */ - public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) { - return new OffloadHardwareInterface(h, log); - } - - /** - * Get a reference to the offload controller to be used by tethering. - */ - @NonNull - public OffloadController getOffloadController(@NonNull Handler h, - @NonNull SharedLog log, @NonNull OffloadController.Dependencies deps) { - final NetworkStatsManager statsManager = - (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE); - return new OffloadController(h, getOffloadHardwareInterface(h, log), - getContext().getContentResolver(), statsManager, log, deps); - } - - - /** - * Get a reference to the UpstreamNetworkMonitor to be used by tethering. - */ - public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target, - SharedLog log, int what) { - return new UpstreamNetworkMonitor(ctx, target, log, what); - } - - /** - * Get a reference to the IPv6TetheringCoordinator to be used by tethering. - */ - public IPv6TetheringCoordinator getIPv6TetheringCoordinator( - ArrayList notifyList, SharedLog log) { - return new IPv6TetheringCoordinator(notifyList, log); - } - - /** - * Get dependencies to be used by IpServer. - */ - public abstract IpServer.Dependencies getIpServerDependencies(); - - /** - * Indicates whether tethering is supported on the device. - */ - public boolean isTetheringSupported() { - return true; - } - - /** - * Get the NetworkRequest that should be fulfilled by the default network. - */ - public abstract NetworkRequest getDefaultNetworkRequest(); - - /** - * Get a reference to the EntitlementManager to be used by tethering. - */ - public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log, - Runnable callback) { - return new EntitlementManager(ctx, h, log, callback); - } - - /** - * Generate a new TetheringConfiguration according to input sub Id. - */ - public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log, - int subId) { - return new TetheringConfiguration(ctx, log, subId); - } - - /** - * Get a reference to INetd to be used by tethering. - */ - public INetd getINetd(Context context) { - return INetd.Stub.asInterface( - (IBinder) context.getSystemService(Context.NETD_SERVICE)); - } - - /** - * Get a reference to the TetheringNotificationUpdater to be used by tethering. - */ - public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx, - @NonNull final Looper looper) { - return new TetheringNotificationUpdater(ctx, looper); - } - - /** - * Get tethering thread looper. - */ - public abstract Looper getTetheringLooper(); - - /** - * Get Context of TetheringSerice. - */ - public abstract Context getContext(); - - /** - * Get a reference to BluetoothAdapter to be used by tethering. - */ - public abstract BluetoothAdapter getBluetoothAdapter(); - - /** - * Get SystemProperties which indicate whether tethering is denied. - */ - public boolean isTetheringDenied() { - return TextUtils.equals(SystemProperties.get("ro.tether.denied"), "true"); - } - - /** - * Get a reference to PrivateAddressCoordinator to be used by Tethering. - */ - public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx, - TetheringConfiguration cfg) { - return new PrivateAddressCoordinator(ctx, cfg); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java deleted file mode 100644 index ff38f717a121..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.networkstack.tethering; - -import android.annotation.Nullable; -import android.net.LinkProperties; -import android.net.NetworkCapabilities; -import android.net.RouteInfo; -import android.net.util.InterfaceSet; - -import com.android.net.module.util.NetUtils; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * @hide - */ -public final class TetheringInterfaceUtils { - private static final InetAddress IN6ADDR_ANY = getByAddress( - new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); - private static final InetAddress INADDR_ANY = getByAddress(new byte[] {0, 0, 0, 0}); - - /** - * Get upstream interfaces for tethering based on default routes for IPv4/IPv6. - * @return null if there is no usable interface, or a set of at least one interface otherwise. - */ - public static @Nullable InterfaceSet getTetheringInterfaces(UpstreamNetworkState ns) { - if (ns == null) { - return null; - } - - final LinkProperties lp = ns.linkProperties; - final String if4 = getInterfaceForDestination(lp, INADDR_ANY); - final String if6 = getIPv6Interface(ns); - - return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6); - } - - /** - * Get the upstream interface for IPv6 tethering. - * @return null if there is no usable interface, or the interface name otherwise. - */ - public static @Nullable String getIPv6Interface(UpstreamNetworkState ns) { - // Broadly speaking: - // - // [1] does the upstream have an IPv6 default route? - // - // and - // - // [2] does the upstream have one or more global IPv6 /64s - // dedicated to this device? - // - // In lieu of Prefix Delegation and other evaluation of whether a - // prefix may or may not be dedicated to this device, for now just - // check whether the upstream is TRANSPORT_CELLULAR. This works - // because "[t]he 3GPP network allocates each default bearer a unique - // /64 prefix", per RFC 6459, Section 5.2. - final boolean canTether = - (ns != null) && (ns.network != null) - && (ns.linkProperties != null) && (ns.networkCapabilities != null) - // At least one upstream DNS server: - && ns.linkProperties.hasIpv6DnsServer() - // Minimal amount of IPv6 provisioning: - && ns.linkProperties.hasGlobalIpv6Address() - // Temporary approximation of "dedicated prefix": - && ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); - - return canTether - ? getInterfaceForDestination(ns.linkProperties, IN6ADDR_ANY) - : null; - } - - private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) { - final RouteInfo ri = (lp != null) - ? NetUtils.selectBestRoute(lp.getAllRoutes(), dst) - : null; - return (ri != null) ? ri.getInterface() : null; - } - - private static InetAddress getByAddress(final byte[] addr) { - try { - return InetAddress.getByAddress(null, addr); - } catch (UnknownHostException e) { - throw new AssertionError("illegal address length" + addr.length); - } - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java deleted file mode 100644 index a0198cc9c126..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.text.TextUtils.isEmpty; - -import android.app.Notification; -import android.app.Notification.Action; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.net.NetworkCapabilities; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.UserHandle; -import android.provider.Settings; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.util.SparseArray; - -import androidx.annotation.DrawableRes; -import androidx.annotation.IntDef; -import androidx.annotation.IntRange; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * A class to display tethering-related notifications. - * - *

This class is not thread safe, it is intended to be used only from the tethering handler - * thread. However the constructor is an exception, as it is called on another thread ; - * therefore for thread safety all members of this class MUST either be final or initialized - * to their default value (0, false or null). - * - * @hide - */ -public class TetheringNotificationUpdater { - private static final String TAG = TetheringNotificationUpdater.class.getSimpleName(); - private static final String CHANNEL_ID = "TETHERING_STATUS"; - private static final String WIFI_DOWNSTREAM = "WIFI"; - private static final String USB_DOWNSTREAM = "USB"; - private static final String BLUETOOTH_DOWNSTREAM = "BT"; - @VisibleForTesting - static final String ACTION_DISABLE_TETHERING = - "com.android.server.connectivity.tethering.DISABLE_TETHERING"; - private static final boolean NOTIFY_DONE = true; - private static final boolean NO_NOTIFY = false; - @VisibleForTesting - static final int EVENT_SHOW_NO_UPSTREAM = 1; - // Id to update and cancel restricted notification. Must be unique within the tethering app. - @VisibleForTesting - static final int RESTRICTED_NOTIFICATION_ID = 1001; - // Id to update and cancel no upstream notification. Must be unique within the tethering app. - @VisibleForTesting - static final int NO_UPSTREAM_NOTIFICATION_ID = 1002; - // Id to update and cancel roaming notification. Must be unique within the tethering app. - @VisibleForTesting - static final int ROAMING_NOTIFICATION_ID = 1003; - @VisibleForTesting - static final int NO_ICON_ID = 0; - @VisibleForTesting - static final int DOWNSTREAM_NONE = 0; - // Refer to TelephonyManager#getSimCarrierId for more details about carrier id. - @VisibleForTesting - static final int VERIZON_CARRIER_ID = 1839; - private final Context mContext; - private final NotificationManager mNotificationManager; - private final NotificationChannel mChannel; - private final Handler mHandler; - - // WARNING : the constructor is called on a different thread. Thread safety therefore - // relies on these values being initialized to 0, false or null, and not any other value. If you - // need to change this, you will need to change the thread where the constructor is invoked, or - // to introduce synchronization. - // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. - // This value has to be made 1 2 and 4, and OR'd with the others. - private int mDownstreamTypesMask = DOWNSTREAM_NONE; - private boolean mNoUpstream = false; - private boolean mRoaming = false; - - // WARNING : this value is not able to being initialized to 0 and must have volatile because - // telephony service is not guaranteed that is up before tethering service starts. If telephony - // is up later than tethering, TetheringNotificationUpdater will use incorrect and valid - // subscription id(0) to query resources. Therefore, initialized subscription id must be - // INVALID_SUBSCRIPTION_ID. - private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - RESTRICTED_NOTIFICATION_ID, - NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID - }) - @interface NotificationId {} - - private static final class MccMncOverrideInfo { - public final String visitedMccMnc; - public final int homeMcc; - public final int homeMnc; - MccMncOverrideInfo(String visitedMccMnc, int mcc, int mnc) { - this.visitedMccMnc = visitedMccMnc; - this.homeMcc = mcc; - this.homeMnc = mnc; - } - } - - private static final SparseArray sCarrierIdToMccMnc = new SparseArray<>(); - - static { - sCarrierIdToMccMnc.put(VERIZON_CARRIER_ID, new MccMncOverrideInfo("20404", 311, 480)); - } - - public TetheringNotificationUpdater(@NonNull final Context context, - @NonNull final Looper looper) { - mContext = context; - mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) - .getSystemService(Context.NOTIFICATION_SERVICE); - mChannel = new NotificationChannel( - CHANNEL_ID, - context.getResources().getString(R.string.notification_channel_tethering_status), - NotificationManager.IMPORTANCE_LOW); - mNotificationManager.createNotificationChannel(mChannel); - mHandler = new NotificationHandler(looper); - } - - private class NotificationHandler extends Handler { - NotificationHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch(msg.what) { - case EVENT_SHOW_NO_UPSTREAM: - notifyTetheringNoUpstream(); - break; - } - } - } - - /** Called when downstream has changed */ - public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) { - updateActiveNotifications( - mActiveDataSubId, downstreamTypesMask, mNoUpstream, mRoaming); - } - - /** Called when active data subscription id changed */ - public void onActiveDataSubscriptionIdChanged(final int subId) { - updateActiveNotifications(subId, mDownstreamTypesMask, mNoUpstream, mRoaming); - } - - /** Called when upstream network capabilities changed */ - public void onUpstreamCapabilitiesChanged(@Nullable final NetworkCapabilities capabilities) { - final boolean isNoUpstream = (capabilities == null); - final boolean isRoaming = capabilities != null - && !capabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING); - updateActiveNotifications( - mActiveDataSubId, mDownstreamTypesMask, isNoUpstream, isRoaming); - } - - @NonNull - @VisibleForTesting - final Handler getHandler() { - return mHandler; - } - - @NonNull - @VisibleForTesting - Resources getResourcesForSubId(@NonNull final Context context, final int subId) { - final Resources res = SubscriptionManager.getResourcesForSubId(context, subId); - final TelephonyManager tm = - ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)) - .createForSubscriptionId(mActiveDataSubId); - final int carrierId = tm.getSimCarrierId(); - final String mccmnc = tm.getSimOperator(); - final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId); - if (overrideInfo != null && overrideInfo.visitedMccMnc.equals(mccmnc)) { - // Re-configure MCC/MNC value to specific carrier to get right resources. - final Configuration config = res.getConfiguration(); - config.mcc = overrideInfo.homeMcc; - config.mnc = overrideInfo.homeMnc; - return context.createConfigurationContext(config).getResources(); - } - return res; - } - - private void updateActiveNotifications(final int subId, final int downstreamTypes, - final boolean noUpstream, final boolean isRoaming) { - final boolean tetheringActiveChanged = - (downstreamTypes == DOWNSTREAM_NONE) != (mDownstreamTypesMask == DOWNSTREAM_NONE); - final boolean subIdChanged = subId != mActiveDataSubId; - final boolean upstreamChanged = noUpstream != mNoUpstream; - final boolean roamingChanged = isRoaming != mRoaming; - final boolean updateAll = tetheringActiveChanged || subIdChanged; - mActiveDataSubId = subId; - mDownstreamTypesMask = downstreamTypes; - mNoUpstream = noUpstream; - mRoaming = isRoaming; - - if (updateAll || upstreamChanged) updateNoUpstreamNotification(); - if (updateAll || roamingChanged) updateRoamingNotification(); - } - - private void updateNoUpstreamNotification() { - final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; - - if (tetheringInactive || !mNoUpstream || setupNoUpstreamNotification() == NO_NOTIFY) { - clearNotification(NO_UPSTREAM_NOTIFICATION_ID); - mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM); - } - } - - private void updateRoamingNotification() { - final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; - - if (tetheringInactive || !mRoaming || setupRoamingNotification() == NO_NOTIFY) { - clearNotification(ROAMING_NOTIFICATION_ID); - } - } - - @VisibleForTesting - void tetheringRestrictionLifted() { - clearNotification(RESTRICTED_NOTIFICATION_ID); - } - - private void clearNotification(@NotificationId final int id) { - mNotificationManager.cancel(null /* tag */, id); - } - - @VisibleForTesting - static String getSettingsPackageName(@NonNull final PackageManager pm) { - final Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS); - final ComponentName settingsComponent = settingsIntent.resolveActivity(pm); - return settingsComponent != null - ? settingsComponent.getPackageName() : "com.android.settings"; - } - - @VisibleForTesting - void notifyTetheringDisabledByRestriction() { - final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); - final String title = res.getString(R.string.disable_tether_notification_title); - final String message = res.getString(R.string.disable_tether_notification_message); - if (isEmpty(title) || isEmpty(message)) return; - - final PendingIntent pi = PendingIntent.getActivity( - mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - new Intent(Settings.ACTION_TETHER_SETTINGS) - .setPackage(getSettingsPackageName(mContext.getPackageManager())) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), - PendingIntent.FLAG_IMMUTABLE, - null /* options */); - - showNotification(R.drawable.stat_sys_tether_general, title, message, - RESTRICTED_NOTIFICATION_ID, false /* ongoing */, pi, new Action[0]); - } - - private void notifyTetheringNoUpstream() { - final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); - final String title = res.getString(R.string.no_upstream_notification_title); - final String message = res.getString(R.string.no_upstream_notification_message); - final String disableButton = - res.getString(R.string.no_upstream_notification_disable_button); - if (isEmpty(title) || isEmpty(message) || isEmpty(disableButton)) return; - - final Intent intent = new Intent(ACTION_DISABLE_TETHERING); - intent.setPackage(mContext.getPackageName()); - final PendingIntent pi = PendingIntent.getBroadcast( - mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - intent, - PendingIntent.FLAG_IMMUTABLE); - final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build(); - - showNotification(R.drawable.stat_sys_tether_general, title, message, - NO_UPSTREAM_NOTIFICATION_ID, true /* ongoing */, null /* pendingIntent */, action); - } - - private boolean setupRoamingNotification() { - final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); - final boolean upstreamRoamingNotification = - res.getBoolean(R.bool.config_upstream_roaming_notification); - - if (!upstreamRoamingNotification) return NO_NOTIFY; - - final String title = res.getString(R.string.upstream_roaming_notification_title); - final String message = res.getString(R.string.upstream_roaming_notification_message); - if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY; - - final PendingIntent pi = PendingIntent.getActivity( - mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - new Intent(Settings.ACTION_TETHER_SETTINGS) - .setPackage(getSettingsPackageName(mContext.getPackageManager())) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), - PendingIntent.FLAG_IMMUTABLE, - null /* options */); - - showNotification(R.drawable.stat_sys_tether_general, title, message, - ROAMING_NOTIFICATION_ID, true /* ongoing */, pi, new Action[0]); - return NOTIFY_DONE; - } - - private boolean setupNoUpstreamNotification() { - final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); - final int delayToShowUpstreamNotification = - res.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul); - - if (delayToShowUpstreamNotification < 0) return NO_NOTIFY; - - mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_NO_UPSTREAM), - delayToShowUpstreamNotification); - return NOTIFY_DONE; - } - - private void showNotification(@DrawableRes final int iconId, @NonNull final String title, - @NonNull final String message, @NotificationId final int id, final boolean ongoing, - @Nullable PendingIntent pi, @NonNull final Action... actions) { - final Notification notification = - new Notification.Builder(mContext, mChannel.getId()) - .setSmallIcon(iconId) - .setContentTitle(title) - .setContentText(message) - .setOngoing(ongoing) - .setColor(mContext.getColor( - android.R.color.system_notification_accent_color)) - .setVisibility(Notification.VISIBILITY_PUBLIC) - .setCategory(Notification.CATEGORY_STATUS) - .setContentIntent(pi) - .setActions(actions) - .build(); - - mNotificationManager.notify(null /* tag */, id, notification); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java deleted file mode 100644 index d637ba7557fa..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.Manifest.permission.ACCESS_NETWORK_STATE; -import static android.Manifest.permission.NETWORK_STACK; -import static android.Manifest.permission.TETHER_PRIVILEGED; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; -import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; -import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED; -import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR; - -import android.app.Service; -import android.bluetooth.BluetoothAdapter; -import android.content.Context; -import android.content.Intent; -import android.net.IIntResultListener; -import android.net.INetworkStackConnector; -import android.net.ITetheringConnector; -import android.net.ITetheringEventCallback; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.NetworkStack; -import android.net.TetheringRequestParcel; -import android.net.dhcp.DhcpServerCallbacks; -import android.net.dhcp.DhcpServingParamsParcel; -import android.net.ip.IpServer; -import android.os.Binder; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.provider.Settings; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.FileDescriptor; -import java.io.PrintWriter; - -/** - * Android service used to manage tethering. - * - *

The service returns a binder for the system server to communicate with the tethering. - */ -public class TetheringService extends Service { - private static final String TAG = TetheringService.class.getSimpleName(); - - private TetheringConnector mConnector; - - @Override - public void onCreate() { - final TetheringDependencies deps = makeTetheringDependencies(); - // The Tethering object needs a fully functional context to start, so this can't be done - // in the constructor. - mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this); - } - - /** - * Make a reference to Tethering object. - */ - @VisibleForTesting - public Tethering makeTethering(TetheringDependencies deps) { - System.loadLibrary("tetherutilsjni"); - return new Tethering(deps); - } - - @NonNull - @Override - public IBinder onBind(Intent intent) { - return mConnector; - } - - private static class TetheringConnector extends ITetheringConnector.Stub { - private final TetheringService mService; - private final Tethering mTethering; - - TetheringConnector(Tethering tether, TetheringService service) { - mTethering = tether; - mService = service; - } - - @Override - public void tether(String iface, String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - listener.onResult(mTethering.tether(iface)); - } catch (RemoteException e) { } - } - - @Override - public void untether(String iface, String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - listener.onResult(mTethering.untether(iface)); - } catch (RemoteException e) { } - } - - @Override - public void setUsbTethering(boolean enable, String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - listener.onResult(mTethering.setUsbTethering(enable)); - } catch (RemoteException e) { } - } - - @Override - public void startTethering(TetheringRequestParcel request, String callerPkg, - String callingAttributionTag, IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, - callingAttributionTag, - request.exemptFromEntitlementCheck /* onlyAllowPrivileged */, - listener)) { - return; - } - - mTethering.startTethering(request, listener); - } - - @Override - public void stopTethering(int type, String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - mTethering.stopTethering(type); - listener.onResult(TETHER_ERROR_NO_ERROR); - } catch (RemoteException e) { } - } - - @Override - public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver, - boolean showEntitlementUi, String callerPkg, String callingAttributionTag) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, receiver)) return; - - mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi); - } - - @Override - public void registerTetheringEventCallback(ITetheringEventCallback callback, - String callerPkg) { - try { - if (!hasTetherAccessPermission()) { - callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); - return; - } - mTethering.registerTetheringEventCallback(callback); - } catch (RemoteException e) { } - } - - @Override - public void unregisterTetheringEventCallback(ITetheringEventCallback callback, - String callerPkg) { - try { - if (!hasTetherAccessPermission()) { - callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); - return; - } - mTethering.unregisterTetheringEventCallback(callback); - } catch (RemoteException e) { } - } - - @Override - public void stopAllTethering(String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - mTethering.untetherAll(); - listener.onResult(TETHER_ERROR_NO_ERROR); - } catch (RemoteException e) { } - } - - @Override - public void isTetheringSupported(String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - listener.onResult(TETHER_ERROR_NO_ERROR); - } catch (RemoteException e) { } - } - - @Override - protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, - @Nullable String[] args) { - mTethering.dump(fd, writer, args); - } - - private boolean checkAndNotifyCommonError(final String callerPkg, - final String callingAttributionTag, final IIntResultListener listener) { - return checkAndNotifyCommonError(callerPkg, callingAttributionTag, - false /* onlyAllowPrivileged */, listener); - } - - private boolean checkAndNotifyCommonError(final String callerPkg, - final String callingAttributionTag, final boolean onlyAllowPrivileged, - final IIntResultListener listener) { - try { - if (!hasTetherChangePermission(callerPkg, callingAttributionTag, - onlyAllowPrivileged)) { - listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - return true; - } - if (!mTethering.isTetheringSupported()) { - listener.onResult(TETHER_ERROR_UNSUPPORTED); - return true; - } - } catch (RemoteException e) { - return true; - } - - return false; - } - - private boolean checkAndNotifyCommonError(final String callerPkg, - final String callingAttributionTag, final ResultReceiver receiver) { - if (!hasTetherChangePermission(callerPkg, callingAttributionTag, - false /* onlyAllowPrivileged */)) { - receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null); - return true; - } - if (!mTethering.isTetheringSupported()) { - receiver.send(TETHER_ERROR_UNSUPPORTED, null); - return true; - } - - return false; - } - - private boolean hasNetworkStackPermission() { - return checkCallingOrSelfPermission(NETWORK_STACK) - || checkCallingOrSelfPermission(PERMISSION_MAINLINE_NETWORK_STACK); - } - - private boolean hasTetherPrivilegedPermission() { - return checkCallingOrSelfPermission(TETHER_PRIVILEGED); - } - - private boolean checkCallingOrSelfPermission(final String permission) { - return mService.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED; - } - - private boolean hasTetherChangePermission(final String callerPkg, - final String callingAttributionTag, final boolean onlyAllowPrivileged) { - if (onlyAllowPrivileged && !hasNetworkStackPermission()) return false; - - if (hasTetherPrivilegedPermission()) return true; - - if (mTethering.isTetherProvisioningRequired()) return false; - - int uid = Binder.getCallingUid(); - - // If callerPkg's uid is not same as Binder.getCallingUid(), - // checkAndNoteWriteSettingsOperation will return false and the operation will be - // denied. - return mService.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg, - callingAttributionTag, false /* throwException */); - } - - private boolean hasTetherAccessPermission() { - if (hasTetherPrivilegedPermission()) return true; - - return mService.checkCallingOrSelfPermission( - ACCESS_NETWORK_STATE) == PERMISSION_GRANTED; - } - } - - /** - * Check if the package is a allowed to write settings. This also accounts that such an access - * happened. - * - * @return {@code true} iff the package is allowed to write settings. - */ - @VisibleForTesting - boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid, - @NonNull String callingPackage, @Nullable String callingAttributionTag, - boolean throwException) { - return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage, - callingAttributionTag, throwException); - } - - /** - * An injection method for testing. - */ - @VisibleForTesting - public TetheringDependencies makeTetheringDependencies() { - return new TetheringDependencies() { - @Override - public NetworkRequest getDefaultNetworkRequest() { - // TODO: b/147280869, add a proper system API to replace this. - final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder() - .clearCapabilities() - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .build(); - return trackDefaultRequest; - } - - @Override - public Looper getTetheringLooper() { - final HandlerThread tetherThread = new HandlerThread("android.tethering"); - tetherThread.start(); - return tetherThread.getLooper(); - } - - @Override - public Context getContext() { - return TetheringService.this; - } - - @Override - public IpServer.Dependencies getIpServerDependencies() { - return new IpServer.Dependencies() { - @Override - public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, - DhcpServerCallbacks cb) { - try { - final INetworkStackConnector service = getNetworkStackConnector(); - if (service == null) return; - - service.makeDhcpServer(ifName, params, cb); - } catch (RemoteException e) { - Log.e(TAG, "Fail to make dhcp server"); - try { - cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null); - } catch (RemoteException re) { } - } - } - }; - } - - // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring - // networkStackClient. - static final int NETWORKSTACK_TIMEOUT_MS = 60_000; - private INetworkStackConnector getNetworkStackConnector() { - IBinder connector; - try { - final long before = System.currentTimeMillis(); - while ((connector = NetworkStack.getService()) == null) { - if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { - Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); - return null; - } - Thread.sleep(200); - } - } catch (InterruptedException e) { - Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector"); - return null; - } - return INetworkStackConnector.Stub.asInterface(connector); - } - - @Override - public BluetoothAdapter getBluetoothAdapter() { - return BluetoothAdapter.getDefaultAdapter(); - } - }; - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java deleted file mode 100644 index b17065cb7804..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Copyright (C) 2017 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.networkstack.tethering; - -import static android.net.ConnectivityManager.TYPE_BLUETOOTH; -import static android.net.ConnectivityManager.TYPE_ETHERNET; -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; -import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; -import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.ConnectivityManager.NetworkCallback; -import android.net.IpPrefix; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.util.PrefixUtils; -import android.net.util.SharedLog; -import android.os.Handler; -import android.util.Log; -import android.util.SparseIntArray; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.StateMachine; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - - -/** - * A class to centralize all the network and link properties information - * pertaining to the current and any potential upstream network. - * - * The owner of UNM gets it to register network callbacks by calling the - * following methods : - * Calling #startTrackDefaultNetwork() to track the system default network. - * Calling #startObserveAllNetworks() to observe all networks. Listening all - * networks is necessary while the expression of preferred upstreams remains - * a list of legacy connectivity types. In future, this can be revisited. - * Calling #registerMobileNetworkRequest() to bring up mobile DUN/HIPRI network. - * - * The methods and data members of this class are only to be accessed and - * modified from the tethering main state machine thread. Any other - * access semantics would necessitate the addition of locking. - * - * TODO: Move upstream selection logic here. - * - * All callback methods are run on the same thread as the specified target - * state machine. This class does not require locking when accessed from this - * thread. Access from other threads is not advised. - * - * @hide - */ -public class UpstreamNetworkMonitor { - private static final String TAG = UpstreamNetworkMonitor.class.getSimpleName(); - private static final boolean DBG = false; - private static final boolean VDBG = false; - - public static final int EVENT_ON_CAPABILITIES = 1; - public static final int EVENT_ON_LINKPROPERTIES = 2; - public static final int EVENT_ON_LOST = 3; - public static final int NOTIFY_LOCAL_PREFIXES = 10; - // This value is used by deprecated preferredUpstreamIfaceTypes selection which is default - // disabled. - @VisibleForTesting - public static final int TYPE_NONE = -1; - - private static final int CALLBACK_LISTEN_ALL = 1; - private static final int CALLBACK_DEFAULT_INTERNET = 2; - private static final int CALLBACK_MOBILE_REQUEST = 3; - - private static final SparseIntArray sLegacyTypeToTransport = new SparseIntArray(); - static { - sLegacyTypeToTransport.put(TYPE_MOBILE, NetworkCapabilities.TRANSPORT_CELLULAR); - sLegacyTypeToTransport.put(TYPE_MOBILE_DUN, NetworkCapabilities.TRANSPORT_CELLULAR); - sLegacyTypeToTransport.put(TYPE_MOBILE_HIPRI, NetworkCapabilities.TRANSPORT_CELLULAR); - sLegacyTypeToTransport.put(TYPE_WIFI, NetworkCapabilities.TRANSPORT_WIFI); - sLegacyTypeToTransport.put(TYPE_BLUETOOTH, NetworkCapabilities.TRANSPORT_BLUETOOTH); - sLegacyTypeToTransport.put(TYPE_ETHERNET, NetworkCapabilities.TRANSPORT_ETHERNET); - } - - private final Context mContext; - private final SharedLog mLog; - private final StateMachine mTarget; - private final Handler mHandler; - private final int mWhat; - private final HashMap mNetworkMap = new HashMap<>(); - private HashSet mLocalPrefixes; - private ConnectivityManager mCM; - private EntitlementManager mEntitlementMgr; - private NetworkCallback mListenAllCallback; - private NetworkCallback mDefaultNetworkCallback; - private NetworkCallback mMobileNetworkCallback; - private boolean mDunRequired; - // Whether the current default upstream is mobile or not. - private boolean mIsDefaultCellularUpstream; - // The current system default network (not really used yet). - private Network mDefaultInternetNetwork; - // The current upstream network used for tethering. - private Network mTetheringUpstreamNetwork; - - public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) { - mContext = ctx; - mTarget = tgt; - mHandler = mTarget.getHandler(); - mLog = log.forSubComponent(TAG); - mWhat = what; - mLocalPrefixes = new HashSet<>(); - mIsDefaultCellularUpstream = false; - } - - @VisibleForTesting - public UpstreamNetworkMonitor( - ConnectivityManager cm, StateMachine tgt, SharedLog log, int what) { - this((Context) null, tgt, log, what); - mCM = cm; - } - - /** - * Tracking the system default network. This method should be called when system is ready. - * - * @param defaultNetworkRequest should be the same as ConnectivityService default request - * @param entitle a EntitlementManager object to communicate between EntitlementManager and - * UpstreamNetworkMonitor - */ - public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest, - EntitlementManager entitle) { - - // defaultNetworkRequest is not really a "request", just a way of tracking the system - // default network. It's guaranteed not to actually bring up any networks because it's - // the should be the same request as the ConnectivityService default request, and thus - // shares fate with it. We can't use registerDefaultNetworkCallback because it will not - // track the system default network if there is a VPN that applies to our UID. - if (mDefaultNetworkCallback == null) { - mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET); - cm().requestNetwork(defaultNetworkRequest, mDefaultNetworkCallback, mHandler); - } - if (mEntitlementMgr == null) { - mEntitlementMgr = entitle; - } - } - - /** Listen all networks. */ - public void startObserveAllNetworks() { - stop(); - - final NetworkRequest listenAllRequest = new NetworkRequest.Builder() - .clearCapabilities().build(); - mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL); - cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler); - } - - /** - * Stop tracking candidate tethering upstreams and release mobile network request. - * Note: this function is used when tethering is stopped because tethering do not need to - * choose upstream anymore. But it would not stop default network tracking because - * EntitlementManager may need to know default network to decide whether to request entitlement - * check even tethering is not active yet. - */ - public void stop() { - releaseMobileNetworkRequest(); - - releaseCallback(mListenAllCallback); - mListenAllCallback = null; - - mTetheringUpstreamNetwork = null; - mNetworkMap.clear(); - } - - /** Setup or teardown DUN connection according to |dunRequired|. */ - public void updateMobileRequiresDun(boolean dunRequired) { - final boolean valueChanged = (mDunRequired != dunRequired); - mDunRequired = dunRequired; - if (valueChanged && mobileNetworkRequested()) { - releaseMobileNetworkRequest(); - registerMobileNetworkRequest(); - } - } - - /** Whether mobile network is requested. */ - public boolean mobileNetworkRequested() { - return (mMobileNetworkCallback != null); - } - - /** Request mobile network if mobile upstream is permitted. */ - public void registerMobileNetworkRequest() { - if (!isCellularUpstreamPermitted()) { - mLog.i("registerMobileNetworkRequest() is not permitted"); - releaseMobileNetworkRequest(); - return; - } - if (mMobileNetworkCallback != null) { - mLog.e("registerMobileNetworkRequest() already registered"); - return; - } - - final NetworkRequest mobileUpstreamRequest; - if (mDunRequired) { - mobileUpstreamRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_DUN) - .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) - .addTransportType(TRANSPORT_CELLULAR).build(); - } else { - mobileUpstreamRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_INTERNET) - .addTransportType(TRANSPORT_CELLULAR).build(); - } - - // The existing default network and DUN callbacks will be notified. - // Therefore, to avoid duplicate notifications, we only register a no-op. - mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST); - - // The following use of the legacy type system cannot be removed until - // upstream selection no longer finds networks by legacy type. - // See also http://b/34364553 . - final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI; - - // TODO: Change the timeout from 0 (no onUnavailable callback) to some - // moderate callback timeout. This might be useful for updating some UI. - // Additionally, we log a message to aid in any subsequent debugging. - mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest); - - cm().requestNetwork(mobileUpstreamRequest, 0, legacyType, mHandler, - mMobileNetworkCallback); - } - - /** Release mobile network request. */ - public void releaseMobileNetworkRequest() { - if (mMobileNetworkCallback == null) return; - - cm().unregisterNetworkCallback(mMobileNetworkCallback); - mMobileNetworkCallback = null; - } - - // So many TODOs here, but chief among them is: make this functionality an - // integral part of this class such that whenever a higher priority network - // becomes available and useful we (a) file a request to keep it up as - // necessary and (b) change all upstream tracking state accordingly (by - // passing LinkProperties up to Tethering). - /** - * Select the first available network from |perferredTypes|. - */ - public UpstreamNetworkState selectPreferredUpstreamType(Iterable preferredTypes) { - final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType( - mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted()); - - mLog.log("preferred upstream type: " + typeStatePair.type); - - switch (typeStatePair.type) { - case TYPE_MOBILE_DUN: - case TYPE_MOBILE_HIPRI: - // Tethering just selected mobile upstream in spite of the default network being - // not mobile. This can happen because of the priority list. - // Notify EntitlementManager to check permission for using mobile upstream. - if (!mIsDefaultCellularUpstream) { - mEntitlementMgr.maybeRunProvisioning(); - } - // If we're on DUN, put our own grab on it. - registerMobileNetworkRequest(); - break; - case TYPE_NONE: - // If we found NONE and mobile upstream is permitted we don't want to do this - // as we want any previous requests to keep trying to bring up something we can use. - if (!isCellularUpstreamPermitted()) releaseMobileNetworkRequest(); - break; - default: - // If we've found an active upstream connection that's not DUN/HIPRI - // we should stop any outstanding DUN/HIPRI requests. - releaseMobileNetworkRequest(); - break; - } - - return typeStatePair.ns; - } - - /** - * Get current preferred upstream network. If default network is cellular and DUN is required, - * preferred upstream would be DUN otherwise preferred upstream is the same as default network. - * Returns null if no current upstream is available. - */ - public UpstreamNetworkState getCurrentPreferredUpstream() { - final UpstreamNetworkState dfltState = (mDefaultInternetNetwork != null) - ? mNetworkMap.get(mDefaultInternetNetwork) - : null; - if (isNetworkUsableAndNotCellular(dfltState)) return dfltState; - - if (!isCellularUpstreamPermitted()) return null; - - if (!mDunRequired) return dfltState; - - // Find a DUN network. Note that code in Tethering causes a DUN request - // to be filed, but this might be moved into this class in future. - return findFirstDunNetwork(mNetworkMap.values()); - } - - /** Tell UpstreamNetworkMonitor which network is the current upstream of tethering. */ - public void setCurrentUpstream(Network upstream) { - mTetheringUpstreamNetwork = upstream; - } - - /** Return local prefixes. */ - public Set getLocalPrefixes() { - return (Set) mLocalPrefixes.clone(); - } - - private boolean isCellularUpstreamPermitted() { - if (mEntitlementMgr != null) { - return mEntitlementMgr.isCellularUpstreamPermitted(); - } else { - // This flow should only happens in testing. - return true; - } - } - - private void handleAvailable(Network network) { - if (mNetworkMap.containsKey(network)) return; - - if (VDBG) Log.d(TAG, "onAvailable for " + network); - mNetworkMap.put(network, new UpstreamNetworkState(null, null, network)); - } - - private void handleNetCap(Network network, NetworkCapabilities newNc) { - final UpstreamNetworkState prev = mNetworkMap.get(network); - if (prev == null || newNc.equals(prev.networkCapabilities)) { - // Ignore notifications about networks for which we have not yet - // received onAvailable() (should never happen) and any duplicate - // notifications (e.g. matching more than one of our callbacks). - return; - } - - if (VDBG) { - Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s", - network, newNc)); - } - - mNetworkMap.put(network, new UpstreamNetworkState( - prev.linkProperties, newNc, network)); - // TODO: If sufficient information is available to select a more - // preferable upstream, do so now and notify the target. - notifyTarget(EVENT_ON_CAPABILITIES, network); - } - - private void handleLinkProp(Network network, LinkProperties newLp) { - final UpstreamNetworkState prev = mNetworkMap.get(network); - if (prev == null || newLp.equals(prev.linkProperties)) { - // Ignore notifications about networks for which we have not yet - // received onAvailable() (should never happen) and any duplicate - // notifications (e.g. matching more than one of our callbacks). - return; - } - - if (VDBG) { - Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s", - network, newLp)); - } - - mNetworkMap.put(network, new UpstreamNetworkState( - newLp, prev.networkCapabilities, network)); - // TODO: If sufficient information is available to select a more - // preferable upstream, do so now and notify the target. - notifyTarget(EVENT_ON_LINKPROPERTIES, network); - } - - private void handleLost(Network network) { - // There are few TODOs within ConnectivityService's rematching code - // pertaining to spurious onLost() notifications. - // - // TODO: simplify this, probably if favor of code that: - // - selects a new upstream if mTetheringUpstreamNetwork has - // been lost (by any callback) - // - deletes the entry from the map only when the LISTEN_ALL - // callback gets notified. - - if (!mNetworkMap.containsKey(network)) { - // Ignore loss of networks about which we had not previously - // learned any information or for which we have already processed - // an onLost() notification. - return; - } - - if (VDBG) Log.d(TAG, "EVENT_ON_LOST for " + network); - - // TODO: If sufficient information is available to select a more - // preferable upstream, do so now and notify the target. Likewise, - // if the current upstream network is gone, notify the target of the - // fact that we now have no upstream at all. - notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network)); - } - - private void recomputeLocalPrefixes() { - final HashSet localPrefixes = allLocalPrefixes(mNetworkMap.values()); - if (!mLocalPrefixes.equals(localPrefixes)) { - mLocalPrefixes = localPrefixes; - notifyTarget(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone()); - } - } - - // Fetch (and cache) a ConnectivityManager only if and when we need one. - private ConnectivityManager cm() { - if (mCM == null) { - // MUST call the String variant to be able to write unittests. - mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - } - return mCM; - } - - /** - * A NetworkCallback class that handles information of interest directly - * in the thread on which it is invoked. To avoid locking, this MUST be - * run on the same thread as the target state machine's handler. - */ - private class UpstreamNetworkCallback extends NetworkCallback { - private final int mCallbackType; - - UpstreamNetworkCallback(int callbackType) { - mCallbackType = callbackType; - } - - @Override - public void onAvailable(Network network) { - handleAvailable(network); - } - - @Override - public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) { - if (mCallbackType == CALLBACK_DEFAULT_INTERNET) { - mDefaultInternetNetwork = network; - final boolean newIsCellular = isCellular(newNc); - if (mIsDefaultCellularUpstream != newIsCellular) { - mIsDefaultCellularUpstream = newIsCellular; - mEntitlementMgr.notifyUpstream(newIsCellular); - } - return; - } - - handleNetCap(network, newNc); - } - - @Override - public void onLinkPropertiesChanged(Network network, LinkProperties newLp) { - if (mCallbackType == CALLBACK_DEFAULT_INTERNET) return; - - handleLinkProp(network, newLp); - // Any non-LISTEN_ALL callback will necessarily concern a network that will - // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback. - // So it's not useful to do this work for non-LISTEN_ALL callbacks. - if (mCallbackType == CALLBACK_LISTEN_ALL) { - recomputeLocalPrefixes(); - } - } - - @Override - public void onLost(Network network) { - if (mCallbackType == CALLBACK_DEFAULT_INTERNET) { - mDefaultInternetNetwork = null; - mIsDefaultCellularUpstream = false; - mEntitlementMgr.notifyUpstream(false); - return; - } - - handleLost(network); - // Any non-LISTEN_ALL callback will necessarily concern a network that will - // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback. - // So it's not useful to do this work for non-LISTEN_ALL callbacks. - if (mCallbackType == CALLBACK_LISTEN_ALL) { - recomputeLocalPrefixes(); - } - } - } - - private void releaseCallback(NetworkCallback cb) { - if (cb != null) cm().unregisterNetworkCallback(cb); - } - - private void notifyTarget(int which, Network network) { - notifyTarget(which, mNetworkMap.get(network)); - } - - private void notifyTarget(int which, Object obj) { - mTarget.sendMessage(mWhat, which, 0, obj); - } - - private static class TypeStatePair { - public int type = TYPE_NONE; - public UpstreamNetworkState ns = null; - } - - private static TypeStatePair findFirstAvailableUpstreamByType( - Iterable netStates, Iterable preferredTypes, - boolean isCellularUpstreamPermitted) { - final TypeStatePair result = new TypeStatePair(); - - for (int type : preferredTypes) { - NetworkCapabilities nc; - try { - nc = networkCapabilitiesForType(type); - } catch (IllegalArgumentException iae) { - Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " + type); - continue; - } - if (!isCellularUpstreamPermitted && isCellular(nc)) { - continue; - } - - for (UpstreamNetworkState value : netStates) { - if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) { - continue; - } - - result.type = type; - result.ns = value; - return result; - } - } - - return result; - } - - private static HashSet allLocalPrefixes(Iterable netStates) { - final HashSet prefixSet = new HashSet<>(); - - for (UpstreamNetworkState ns : netStates) { - final LinkProperties lp = ns.linkProperties; - if (lp == null) continue; - prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp)); - } - - return prefixSet; - } - - private static boolean isCellular(UpstreamNetworkState ns) { - return (ns != null) && isCellular(ns.networkCapabilities); - } - - private static boolean isCellular(NetworkCapabilities nc) { - return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR) - && nc.hasCapability(NET_CAPABILITY_NOT_VPN); - } - - private static boolean hasCapability(UpstreamNetworkState ns, int netCap) { - return (ns != null) && (ns.networkCapabilities != null) - && ns.networkCapabilities.hasCapability(netCap); - } - - private static boolean isNetworkUsableAndNotCellular(UpstreamNetworkState ns) { - return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null) - && !isCellular(ns.networkCapabilities); - } - - private static UpstreamNetworkState findFirstDunNetwork( - Iterable netStates) { - for (UpstreamNetworkState ns : netStates) { - if (isCellular(ns) && hasCapability(ns, NET_CAPABILITY_DUN)) return ns; - } - - return null; - } - - /** - * Given a legacy type (TYPE_WIFI, ...) returns the corresponding NetworkCapabilities instance. - * This function is used for deprecated legacy type and be disabled by default. - */ - @VisibleForTesting - public static NetworkCapabilities networkCapabilitiesForType(int type) { - final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); - - // Map from type to transports. - final int notFound = -1; - final int transport = sLegacyTypeToTransport.get(type, notFound); - if (transport == notFound) { - throw new IllegalArgumentException("unknown legacy type: " + type); - } - builder.addTransportType(transport); - - if (type == TYPE_MOBILE_DUN) { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); - // DUN is restricted network, see NetworkCapabilities#FORCE_RESTRICTED_CAPABILITIES. - builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - } else { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - } - return builder.build(); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java deleted file mode 100644 index bab9f84cf762..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.networkstack.tethering; - -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; - -import androidx.annotation.NonNull; - -/** - * Snapshot of tethering upstream network state. - */ -public class UpstreamNetworkState { - /** {@link LinkProperties}. */ - public final LinkProperties linkProperties; - /** {@link NetworkCapabilities}. */ - public final NetworkCapabilities networkCapabilities; - /** {@link Network}. */ - public final Network network; - - /** Constructs a new UpstreamNetworkState. */ - public UpstreamNetworkState(LinkProperties linkProperties, - NetworkCapabilities networkCapabilities, Network network) { - this.linkProperties = linkProperties; - this.networkCapabilities = networkCapabilities; - this.network = network; - } - - @NonNull - @Override - public String toString() { - return String.format("UpstreamNetworkState{%s, %s, %s}", - network == null ? "null" : network, - networkCapabilities == null ? "null" : networkCapabilities, - linkProperties == null ? "null" : linkProperties); - } -} diff --git a/packages/Tethering/tests/Android.bp b/packages/Tethering/tests/Android.bp deleted file mode 100644 index 731144cee0fb..000000000000 --- a/packages/Tethering/tests/Android.bp +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -filegroup { - name: "TetheringTestsJarJarRules", - srcs: ["jarjar-rules.txt"], - visibility: [ - "//frameworks/base/packages/Tethering/tests:__subpackages__", - "//packages/modules/Connectivity/Tethering/tests:__subpackages__", - ] -} diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp deleted file mode 100644 index 5765c01c43f3..000000000000 --- a/packages/Tethering/tests/integration/Android.bp +++ /dev/null @@ -1,85 +0,0 @@ -// -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -java_defaults { - name: "TetheringIntegrationTestsDefaults", - srcs: [ - "src/**/*.java", - "src/**/*.kt", - ], - static_libs: [ - "NetworkStackApiStableLib", - "androidx.test.rules", - "mockito-target-extended-minus-junit4", - "net-tests-utils", - "testables", - ], - libs: [ - "android.test.runner", - "android.test.base", - "android.test.mock", - ], - jni_libs: [ - // For mockito extended - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], - jarjar_rules: ":NetworkStackJarJarRules", -} - -android_library { - name: "TetheringIntegrationTestsLib", - platform_apis: true, - defaults: ["TetheringIntegrationTestsDefaults"], - visibility: ["//cts/tests/tests/tethering"] -} - -android_test { - name: "TetheringIntegrationTests", - platform_apis: true, - defaults: ["TetheringIntegrationTestsDefaults"], - test_suites: [ - "device-tests", - "mts", - ], - compile_multilib: "both", -} - -// Special version of the tethering tests that includes all tests necessary for code coverage -// purposes. This is currently the union of TetheringTests, TetheringIntegrationTests and -// NetworkStackTests. -android_test { - name: "TetheringCoverageTests", - platform_apis: true, - test_suites: ["device-tests", "mts"], - test_config: "AndroidTest_Coverage.xml", - defaults: ["libnetworkstackutilsjni_deps"], - static_libs: [ - "NetworkStaticLibTestsLib", - "NetworkStackTestsLib", - "TetheringTestsLib", - "TetheringIntegrationTestsLib", - ], - jni_libs: [ - // For mockito extended - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - // For NetworkStackUtils included in NetworkStackBase - "libnetworkstackutilsjni", - ], - jarjar_rules: ":TetheringTestsJarJarRules", - compile_multilib: "both", - manifest: "AndroidManifest_coverage.xml", -} diff --git a/packages/Tethering/tests/integration/AndroidManifest.xml b/packages/Tethering/tests/integration/AndroidManifest.xml deleted file mode 100644 index fddfaad29f0f..000000000000 --- a/packages/Tethering/tests/integration/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/Tethering/tests/integration/AndroidManifest_coverage.xml b/packages/Tethering/tests/integration/AndroidManifest_coverage.xml deleted file mode 100644 index 06de00d78558..000000000000 --- a/packages/Tethering/tests/integration/AndroidManifest_coverage.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - diff --git a/packages/Tethering/tests/integration/AndroidTest_Coverage.xml b/packages/Tethering/tests/integration/AndroidTest_Coverage.xml deleted file mode 100644 index 3def2099e45f..000000000000 --- a/packages/Tethering/tests/integration/AndroidTest_Coverage.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java deleted file mode 100644 index d206ea0b4d45..000000000000 --- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ /dev/null @@ -1,566 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import static android.Manifest.permission.MANAGE_TEST_NETWORKS; -import static android.Manifest.permission.NETWORK_SETTINGS; -import static android.Manifest.permission.TETHER_PRIVILEGED; -import static android.net.TetheringManager.TETHERING_ETHERNET; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; -import static org.junit.Assume.assumeTrue; - -import android.app.UiAutomation; -import android.content.Context; -import android.net.EthernetManager.TetheredInterfaceCallback; -import android.net.EthernetManager.TetheredInterfaceRequest; -import android.net.TetheringManager.StartTetheringCallback; -import android.net.TetheringManager.TetheringEventCallback; -import android.net.TetheringManager.TetheringRequest; -import android.net.dhcp.DhcpAckPacket; -import android.net.dhcp.DhcpOfferPacket; -import android.net.dhcp.DhcpPacket; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.system.Os; -import android.util.Log; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.MediumTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.HandlerUtils; -import com.android.testutils.TapPacketReader; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.FileDescriptor; -import java.net.Inet4Address; -import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.util.Collection; -import java.util.List; -import java.util.Random; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -@RunWith(AndroidJUnit4.class) -@MediumTest -public class EthernetTetheringTest { - - private static final String TAG = EthernetTetheringTest.class.getSimpleName(); - private static final int TIMEOUT_MS = 5000; - private static final int PACKET_READ_TIMEOUT_MS = 100; - private static final int DHCP_DISCOVER_ATTEMPTS = 10; - private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] { - DhcpPacket.DHCP_SUBNET_MASK, - DhcpPacket.DHCP_ROUTER, - DhcpPacket.DHCP_DNS_SERVER, - DhcpPacket.DHCP_LEASE_TIME, - }; - private static final String DHCP_HOSTNAME = "testhostname"; - - private final Context mContext = InstrumentationRegistry.getContext(); - private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class); - private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class); - - private TestNetworkInterface mTestIface; - private HandlerThread mHandlerThread; - private Handler mHandler; - private TapPacketReader mTapPacketReader; - - private TetheredInterfaceRequester mTetheredInterfaceRequester; - private MyTetheringEventCallback mTetheringEventCallback; - - private UiAutomation mUiAutomation = - InstrumentationRegistry.getInstrumentation().getUiAutomation(); - private boolean mRunTests; - - @Before - public void setUp() throws Exception { - // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive - // tethered client callbacks. - mUiAutomation.adoptShellPermissionIdentity( - MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED); - mRunTests = mTm.isTetheringSupported() && mEm != null; - assumeTrue(mRunTests); - - mHandlerThread = new HandlerThread(getClass().getSimpleName()); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); - } - - private void cleanUp() throws Exception { - mTm.stopTethering(TETHERING_ETHERNET); - if (mTetheringEventCallback != null) { - mTetheringEventCallback.awaitInterfaceUntethered(); - mTetheringEventCallback.unregister(); - mTetheringEventCallback = null; - } - if (mTapPacketReader != null) { - TapPacketReader reader = mTapPacketReader; - mHandler.post(() -> reader.stop()); - mTapPacketReader = null; - } - mHandlerThread.quitSafely(); - mTetheredInterfaceRequester.release(); - mEm.setIncludeTestInterfaces(false); - maybeDeleteTestInterface(); - } - - @After - public void tearDown() throws Exception { - try { - if (mRunTests) cleanUp(); - } finally { - mUiAutomation.dropShellPermissionIdentity(); - } - } - - @Test - public void testVirtualEthernetAlreadyExists() throws Exception { - // This test requires manipulating packets. Skip if there is a physical Ethernet connected. - assumeFalse(mEm.isAvailable()); - - mTestIface = createTestInterface(); - // This must be done now because as soon as setIncludeTestInterfaces(true) is called, the - // interface will be placed in client mode, which will delete the link-local address. - // At that point NetworkInterface.getByName() will cease to work on the interface, because - // starting in R NetworkInterface can no longer see interfaces without IP addresses. - int mtu = getMTU(mTestIface); - - Log.d(TAG, "Including test interfaces"); - mEm.setIncludeTestInterfaces(true); - - final String iface = mTetheredInterfaceRequester.getInterface(); - assertEquals("TetheredInterfaceCallback for unexpected interface", - mTestIface.getInterfaceName(), iface); - - checkVirtualEthernet(mTestIface, mtu); - } - - @Test - public void testVirtualEthernet() throws Exception { - // This test requires manipulating packets. Skip if there is a physical Ethernet connected. - assumeFalse(mEm.isAvailable()); - - CompletableFuture futureIface = mTetheredInterfaceRequester.requestInterface(); - - mEm.setIncludeTestInterfaces(true); - - mTestIface = createTestInterface(); - - final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - assertEquals("TetheredInterfaceCallback for unexpected interface", - mTestIface.getInterfaceName(), iface); - - checkVirtualEthernet(mTestIface, getMTU(mTestIface)); - } - - @Test - public void testStaticIpv4() throws Exception { - assumeFalse(mEm.isAvailable()); - - mEm.setIncludeTestInterfaces(true); - - mTestIface = createTestInterface(); - - final String iface = mTetheredInterfaceRequester.getInterface(); - assertEquals("TetheredInterfaceCallback for unexpected interface", - mTestIface.getInterfaceName(), iface); - - assertInvalidStaticIpv4Request(iface, null, null); - assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64"); - assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28"); - assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28"); - assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null); - assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28"); - assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28"); - - final String localAddr = "192.0.2.3/28"; - final String clientAddr = "192.0.2.2/28"; - mTetheringEventCallback = enableEthernetTethering(iface, - requestWithStaticIpv4(localAddr, clientAddr)); - - mTetheringEventCallback.awaitInterfaceTethered(); - assertInterfaceHasIpAddress(iface, localAddr); - - byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray(); - byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray(); - - FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor(); - mTapPacketReader = makePacketReader(fd, getMTU(mTestIface)); - DhcpResults dhcpResults = runDhcp(fd, client1); - assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress); - - try { - runDhcp(fd, client2); - fail("Only one client should get an IP address"); - } catch (TimeoutException expected) { } - - } - - private boolean isAdbOverNetwork() { - // If adb TCP port opened, this test may running by adb over network. - return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) - || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1); - } - - @Test - public void testPhysicalEthernet() throws Exception { - assumeTrue(mEm.isAvailable()); - // Do not run this test if adb is over network and ethernet is connected. - // It is likely the adb run over ethernet, the adb would break when ethernet is switching - // from client mode to server mode. See b/160389275. - assumeFalse(isAdbOverNetwork()); - - // Get an interface to use. - final String iface = mTetheredInterfaceRequester.getInterface(); - - // Enable Ethernet tethering and check that it starts. - mTetheringEventCallback = enableEthernetTethering(iface); - - // There is nothing more we can do on a physical interface without connecting an actual - // client, which is not possible in this test. - } - - private static final class MyTetheringEventCallback implements TetheringEventCallback { - private final TetheringManager mTm; - private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1); - private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1); - private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1); - private final String mIface; - - private volatile boolean mInterfaceWasTethered = false; - private volatile boolean mUnregistered = false; - private volatile Collection mClients = null; - - MyTetheringEventCallback(TetheringManager tm, String iface) { - mTm = tm; - mIface = iface; - } - - public void unregister() { - mTm.unregisterTetheringEventCallback(this); - mUnregistered = true; - } - - @Override - public void onTetheredInterfacesChanged(List interfaces) { - // Ignore stale callbacks registered by previous test cases. - if (mUnregistered) return; - - final boolean wasTethered = mTetheringStartedLatch.getCount() == 0; - if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) { - // This interface is being tethered for the first time. - Log.d(TAG, "Tethering started: " + interfaces); - mInterfaceWasTethered = true; - mTetheringStartedLatch.countDown(); - } else if (mInterfaceWasTethered && !interfaces.contains(mIface)) { - Log.d(TAG, "Tethering stopped: " + interfaces); - mTetheringStoppedLatch.countDown(); - } - } - - public void awaitInterfaceTethered() throws Exception { - assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms", - mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - } - - public void awaitInterfaceUntethered() throws Exception { - // Don't block teardown if the interface was never tethered. - // This is racy because the interface might become tethered right after this check, but - // that can only happen in tearDown if startTethering timed out, which likely means - // the test has already failed. - if (!mInterfaceWasTethered) return; - - assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms", - mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - } - - @Override - public void onError(String ifName, int error) { - // Ignore stale callbacks registered by previous test cases. - if (mUnregistered) return; - - fail("TetheringEventCallback got error:" + error + " on iface " + ifName); - } - - @Override - public void onClientsChanged(Collection clients) { - // Ignore stale callbacks registered by previous test cases. - if (mUnregistered) return; - - Log.d(TAG, "Got clients changed: " + clients); - mClients = clients; - if (clients.size() > 0) { - mClientConnectedLatch.countDown(); - } - } - - public Collection awaitClientConnected() throws Exception { - assertTrue("Did not receive client connected callback after " + TIMEOUT_MS + "ms", - mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - return mClients; - } - } - - private MyTetheringEventCallback enableEthernetTethering(String iface, - TetheringRequest request) throws Exception { - MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface); - mTm.registerTetheringEventCallback(mHandler::post, callback); - - StartTetheringCallback startTetheringCallback = new StartTetheringCallback() { - @Override - public void onTetheringFailed(int resultCode) { - fail("Unexpectedly got onTetheringFailed"); - } - }; - Log.d(TAG, "Starting Ethernet tethering"); - mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback); - callback.awaitInterfaceTethered(); - return callback; - } - - private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception { - return enableEthernetTethering(iface, - new TetheringRequest.Builder(TETHERING_ETHERNET) - .setShouldShowEntitlementUi(false).build()); - } - - private int getMTU(TestNetworkInterface iface) throws SocketException { - NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName()); - assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif); - return nif.getMTU(); - } - - private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) { - final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu); - mHandler.post(() -> reader.start()); - HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS); - return reader; - } - - private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception { - FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor(); - mTapPacketReader = makePacketReader(fd, mtu); - mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName()); - checkTetheredClientCallbacks(fd); - } - - private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception { - // We have to retransmit DHCP requests because IpServer declares itself to be ready before - // its DhcpServer is actually started. TODO: fix this race and remove this loop. - DhcpPacket offerPacket = null; - for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) { - Log.d(TAG, "Sending DHCP discover"); - sendDhcpDiscover(fd, clientMacAddr); - offerPacket = getNextDhcpPacket(); - if (offerPacket instanceof DhcpOfferPacket) break; - } - if (!(offerPacket instanceof DhcpOfferPacket)) { - throw new TimeoutException("No DHCPOFFER received on interface within timeout"); - } - - sendDhcpRequest(fd, offerPacket, clientMacAddr); - DhcpPacket ackPacket = getNextDhcpPacket(); - if (!(ackPacket instanceof DhcpAckPacket)) { - throw new TimeoutException("No DHCPACK received on interface within timeout"); - } - - return ackPacket.toDhcpResults(); - } - - private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception { - // Create a fake client. - byte[] clientMacAddr = new byte[6]; - new Random().nextBytes(clientMacAddr); - - DhcpResults dhcpResults = runDhcp(fd, clientMacAddr); - - final Collection clients = mTetheringEventCallback.awaitClientConnected(); - assertEquals(1, clients.size()); - final TetheredClient client = clients.iterator().next(); - - // Check the MAC address. - assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress()); - assertEquals(TETHERING_ETHERNET, client.getTetheringType()); - - // Check the hostname. - assertEquals(1, client.getAddresses().size()); - TetheredClient.AddressInfo info = client.getAddresses().get(0); - assertEquals(DHCP_HOSTNAME, info.getHostname()); - - // Check the address is the one that was handed out in the DHCP ACK. - assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress()); - - // Check that the lifetime is correct +/- 10s. - final long now = SystemClock.elapsedRealtime(); - final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000; - final String msg = String.format("IP address should have lifetime of %d, got %d", - dhcpResults.leaseDuration, actualLeaseDuration); - assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10); - } - - private DhcpPacket getNextDhcpPacket() throws ParseException { - byte[] packet; - while ((packet = mTapPacketReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) { - try { - return DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2); - } catch (DhcpPacket.ParseException e) { - // Not a DHCP packet. Continue. - } - } - return null; - } - - private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback { - private final CountDownLatch mInterfaceAvailableLatch = new CountDownLatch(1); - private final Handler mHandler; - private final EthernetManager mEm; - - private TetheredInterfaceRequest mRequest; - private final CompletableFuture mFuture = new CompletableFuture<>(); - - TetheredInterfaceRequester(Handler handler, EthernetManager em) { - mHandler = handler; - mEm = em; - } - - @Override - public void onAvailable(String iface) { - Log.d(TAG, "Ethernet interface available: " + iface); - mFuture.complete(iface); - } - - @Override - public void onUnavailable() { - mFuture.completeExceptionally(new IllegalStateException("onUnavailable received")); - } - - public CompletableFuture requestInterface() { - assertNull("BUG: more than one tethered interface request", mRequest); - Log.d(TAG, "Requesting tethered interface"); - mRequest = mEm.requestTetheredInterface(mHandler::post, this); - return mFuture; - } - - public String getInterface() throws Exception { - return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - public void release() { - if (mRequest != null) { - mFuture.obtrudeException(new IllegalStateException("Request already released")); - mRequest.release(); - mRequest = null; - } - } - } - - private void sendDhcpDiscover(FileDescriptor fd, byte[] macAddress) throws Exception { - ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2, - new Random().nextInt() /* transactionId */, (short) 0 /* secs */, - macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS, - false /* rapid commit */, DHCP_HOSTNAME); - sendPacket(fd, packet); - } - - private void sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress) - throws Exception { - DhcpResults results = offerPacket.toDhcpResults(); - Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress(); - Inet4Address serverIdentifier = results.serverAddress; - ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2, - 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */, - false /* broadcast */, macAddress, clientIp /* requestedIpAddress */, - serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME); - sendPacket(fd, packet); - } - - private void sendPacket(FileDescriptor fd, ByteBuffer packet) throws Exception { - assertNotNull("Only tests on virtual interfaces can send packets", fd); - Os.write(fd, packet); - } - - public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) { - // Check all fields except the deprecation and expiry times. - String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2); - assertTrue(msg, l1.isSameAddressAs(l2)); - assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags()); - assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope()); - } - - private TetheringRequest requestWithStaticIpv4(String local, String client) { - LinkAddress localAddr = local == null ? null : new LinkAddress(local); - LinkAddress clientAddr = client == null ? null : new LinkAddress(client); - return new TetheringRequest.Builder(TETHERING_ETHERNET) - .setStaticIpv4Addresses(localAddr, clientAddr) - .setShouldShowEntitlementUi(false).build(); - } - - private void assertInvalidStaticIpv4Request(String iface, String local, String client) - throws Exception { - try { - enableEthernetTethering(iface, requestWithStaticIpv4(local, client)); - fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client); - } catch (IllegalArgumentException | NullPointerException expected) { } - } - - private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception { - LinkAddress expectedAddr = new LinkAddress(expected); - NetworkInterface nif = NetworkInterface.getByName(iface); - for (InterfaceAddress ia : nif.getInterfaceAddresses()) { - final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); - if (expectedAddr.equals(addr)) { - return; - } - } - fail("Expected " + iface + " to have IP address " + expected + ", found " - + nif.getInterfaceAddresses()); - } - - private TestNetworkInterface createTestInterface() throws Exception { - TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class); - TestNetworkInterface iface = tnm.createTapInterface(); - Log.d(TAG, "Created test interface " + iface.getInterfaceName()); - return iface; - } - - private void maybeDeleteTestInterface() throws Exception { - if (mTestIface != null) { - mTestIface.getFileDescriptor().close(); - Log.d(TAG, "Deleted test interface " + mTestIface.getInterfaceName()); - mTestIface = null; - } - } -} diff --git a/packages/Tethering/tests/jarjar-rules.txt b/packages/Tethering/tests/jarjar-rules.txt deleted file mode 100644 index c99ff7f81877..000000000000 --- a/packages/Tethering/tests/jarjar-rules.txt +++ /dev/null @@ -1,19 +0,0 @@ -# Don't jar-jar the entire package because this test use some -# internal classes (like ArrayUtils in com.android.internal.util) -rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util.BitUtils@1 -rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1 -rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1 -rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1 -rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1 -rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 -rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1 - -rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1 - -# Classes from net-utils-framework-common -rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1 - -# TODO: either stop using frameworks-base-testutils or remove the unit test classes it contains. -# TestableLooper from "testables" can be used instead of TestLooper from frameworks-base-testutils. -zap android.os.test.TestLooperTest* -zap com.android.test.filters.SelectTestTests* diff --git a/packages/Tethering/tests/mts/Android.bp b/packages/Tethering/tests/mts/Android.bp deleted file mode 100644 index f925b0a53f32..000000000000 --- a/packages/Tethering/tests/mts/Android.bp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -android_test { - // This tests for functionality that is not required for devices that - // don't use Tethering mainline module. - name: "MtsTetheringTest", - - libs: [ - "android.test.base", - ], - - srcs: [ - "src/**/*.java", - ], - - static_libs: [ - "androidx.test.rules", - // mockito-target-extended-minus-junit4 used in this lib have dependency with - // jni_libs libdexmakerjvmtiagent and libstaticjvmtiagent. - "cts-net-utils", - // This is needed for androidx.test.runner.AndroidJUnitRunner. - "ctstestrunner-axt", - "junit", - "junit-params", - ], - - jni_libs: [ - // For mockito extended which is pulled in from -net-utils -> net-tests-utils - // (mockito-target-extended-minus-junit4). - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], - - platform_apis: true, - - // Tag this module as a mts test artifact - test_suites: [ - "general-tests", - "mts", - ], - - // Include both the 32 and 64 bit versions - compile_multilib: "both", -} diff --git a/packages/Tethering/tests/mts/AndroidManifest.xml b/packages/Tethering/tests/mts/AndroidManifest.xml deleted file mode 100644 index 6d2abcad42a3..000000000000 --- a/packages/Tethering/tests/mts/AndroidManifest.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/packages/Tethering/tests/mts/AndroidTest.xml b/packages/Tethering/tests/mts/AndroidTest.xml deleted file mode 100644 index 80788dfa6f40..000000000000 --- a/packages/Tethering/tests/mts/AndroidTest.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - diff --git a/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java deleted file mode 100644 index 7ffe37ad648d..000000000000 --- a/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.tethering.mts; - -import static android.Manifest.permission.MANAGE_TEST_NETWORKS; -import static android.Manifest.permission.NETWORK_SETTINGS; -import static android.Manifest.permission.READ_DEVICE_CONFIG; -import static android.Manifest.permission.TETHER_PRIVILEGED; -import static android.Manifest.permission.WRITE_SETTINGS; -import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported; -import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; - -import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; - -import android.app.UiAutomation; -import android.content.Context; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.TetheringManager; -import android.net.cts.util.CtsTetheringUtils; -import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback; -import android.provider.DeviceConfig; - -import androidx.annotation.NonNull; -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.TestNetworkTracker; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -public class TetheringModuleTest { - private Context mContext; - private TetheringManager mTm; - private CtsTetheringUtils mCtsTetheringUtils; - - private UiAutomation mUiAutomation = - InstrumentationRegistry.getInstrumentation().getUiAutomation(); - - @Before - public void setUp() throws Exception { - mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, - WRITE_SETTINGS, READ_DEVICE_CONFIG, TETHER_PRIVILEGED); - mContext = InstrumentationRegistry.getContext(); - mTm = mContext.getSystemService(TetheringManager.class); - mCtsTetheringUtils = new CtsTetheringUtils(mContext); - } - - @After - public void tearDown() throws Exception { - mUiAutomation.dropShellPermissionIdentity(); - } - - private static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES = - "tether_enable_select_all_prefix_ranges"; - @Test - public void testSwitchBasePrefixRangeWhenConflict() throws Exception { - assumeTrue(isFeatureEnabled(TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true)); - - addressConflictTest(true); - } - - @Test - public void testSwitchPrefixRangeWhenConflict() throws Exception { - addressConflictTest(false); - } - - private void addressConflictTest(final boolean wholeRangeConflict) throws Exception { - final TestTetheringEventCallback tetherEventCallback = - mCtsTetheringUtils.registerTetheringEventCallback(); - - TestNetworkTracker tnt = null; - try { - tetherEventCallback.assumeTetheringSupported(); - assumeTrue(isWifiTetheringSupported(tetherEventCallback)); - - mCtsTetheringUtils.startWifiTethering(tetherEventCallback); - - final List tetheredIfaces = tetherEventCallback.getTetheredInterfaces(); - assertEquals(1, tetheredIfaces.size()); - final String wifiTetheringIface = tetheredIfaces.get(0); - - NetworkInterface nif = NetworkInterface.getByName(wifiTetheringIface); - // Tethering downstream only have one ipv4 address. - final LinkAddress hotspotAddr = getFirstIpv4Address(nif); - assertNotNull(hotspotAddr); - - final IpPrefix testPrefix = getConflictingPrefix(hotspotAddr, wholeRangeConflict); - assertNotNull(testPrefix); - - tnt = setUpTestNetwork( - new LinkAddress(testPrefix.getAddress(), testPrefix.getPrefixLength())); - - tetherEventCallback.expectTetheredInterfacesChanged(null); - final List wifiRegexs = - tetherEventCallback.getTetheringInterfaceRegexps().getTetherableWifiRegexs(); - - tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs); - nif = NetworkInterface.getByName(wifiTetheringIface); - final LinkAddress newHotspotAddr = getFirstIpv4Address(nif); - assertNotNull(newHotspotAddr); - - assertFalse(testPrefix.containsPrefix( - new IpPrefix(newHotspotAddr.getAddress(), newHotspotAddr.getPrefixLength()))); - - mCtsTetheringUtils.stopWifiTethering(tetherEventCallback); - } finally { - if (tnt != null) { - tnt.teardown(); - } - mTm.stopAllTethering(); - mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback); - } - } - - private LinkAddress getFirstIpv4Address(final NetworkInterface nif) { - for (InterfaceAddress ia : nif.getInterfaceAddresses()) { - final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); - if (addr.isIpv4()) return addr; - } - return null; - } - - @NonNull - private IpPrefix getConflictingPrefix(final LinkAddress address, - final boolean wholeRangeConflict) { - if (!wholeRangeConflict) { - return new IpPrefix(address.getAddress(), address.getPrefixLength()); - } - - final ArrayList prefixPool = new ArrayList<>(Arrays.asList( - new IpPrefix("192.168.0.0/16"), - new IpPrefix("172.16.0.0/12"), - new IpPrefix("10.0.0.0/8"))); - - for (IpPrefix prefix : prefixPool) { - if (prefix.contains(address.getAddress())) return prefix; - } - - fail("Could not find sutiable conflict prefix"); - - // Never go here. - return null; - } - - private TestNetworkTracker setUpTestNetwork(final LinkAddress address) throws Exception { - return initTestNetwork(mContext, address, 10_000L /* test timeout ms*/); - - } - - public static boolean isFeatureEnabled(final String name, final boolean defaultValue) { - return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, defaultValue); - } -} diff --git a/packages/Tethering/tests/privileged/Android.bp b/packages/Tethering/tests/privileged/Android.bp deleted file mode 100644 index 9217345dc2f5..000000000000 --- a/packages/Tethering/tests/privileged/Android.bp +++ /dev/null @@ -1,49 +0,0 @@ -// -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -java_defaults { - name: "TetheringPrivilegedTestsJniDefaults", - jni_libs: [ - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - "libtetherutilsjni", - ], - jni_uses_sdk_apis: true, - visibility: ["//visibility:private"], -} - -android_test { - name: "TetheringPrivilegedTests", - defaults: [ - "TetheringPrivilegedTestsJniDefaults", - ], - srcs: [ - "src/**/*.java", - "src/**/*.kt", - ], - certificate: "networkstack", - platform_apis: true, - test_suites: [ - "device-tests", - "mts", - ], - static_libs: [ - "androidx.test.rules", - "net-tests-utils", - "TetheringApiCurrentLib", - ], - compile_multilib: "both", -} diff --git a/packages/Tethering/tests/privileged/AndroidManifest.xml b/packages/Tethering/tests/privileged/AndroidManifest.xml deleted file mode 100644 index 49eba15d13d4..000000000000 --- a/packages/Tethering/tests/privileged/AndroidManifest.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java deleted file mode 100644 index 42a91aa9acd5..000000000000 --- a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import static android.system.OsConstants.IPPROTO_ICMPV6; - -import static com.android.net.module.util.IpUtils.icmpv6Checksum; -import static com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import android.app.Instrumentation; -import android.content.Context; -import android.net.INetd; -import android.net.InetAddresses; -import android.net.MacAddress; -import android.net.util.InterfaceParams; -import android.net.util.TetheringUtils; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.TapPacketReader; -import com.android.testutils.TapPacketReaderRule; - -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; - -import java.nio.ByteBuffer; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DadProxyTest { - private static final int DATA_BUFFER_LEN = 4096; - private static final int PACKET_TIMEOUT_MS = 5_000; - - // Start the readers manually on a common handler shared with DadProxy, for simplicity - @Rule - public final TapPacketReaderRule mUpstreamReader = new TapPacketReaderRule( - DATA_BUFFER_LEN, false /* autoStart */); - @Rule - public final TapPacketReaderRule mTetheredReader = new TapPacketReaderRule( - DATA_BUFFER_LEN, false /* autoStart */); - - private InterfaceParams mUpstreamParams, mTetheredParams; - private HandlerThread mHandlerThread; - private Handler mHandler; - private TapPacketReader mUpstreamPacketReader, mTetheredPacketReader; - - private static INetd sNetd; - - @BeforeClass - public static void setupOnce() { - System.loadLibrary("tetherutilsjni"); - - final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); - final IBinder netdIBinder = - (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE); - sNetd = INetd.Stub.asInterface(netdIBinder); - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mHandlerThread = new HandlerThread(getClass().getSimpleName()); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - - setupTapInterfaces(); - - // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads. - if (Looper.myLooper() == null) Looper.prepare(); - - DadProxy mProxy = setupProxy(); - } - - @After - public void tearDown() throws Exception { - mUpstreamReader.stop(); - mTetheredReader.stop(); - - if (mHandlerThread != null) { - mHandlerThread.quitSafely(); - mHandlerThread.join(PACKET_TIMEOUT_MS); - } - - if (mTetheredParams != null) { - sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mTetheredParams.name); - } - if (mUpstreamParams != null) { - sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name); - } - } - - private void setupTapInterfaces() { - // Create upstream test iface. - mUpstreamReader.start(mHandler); - mUpstreamParams = InterfaceParams.getByName(mUpstreamReader.iface.getInterfaceName()); - assertNotNull(mUpstreamParams); - mUpstreamPacketReader = mUpstreamReader.getReader(); - - // Create tethered test iface. - mTetheredReader.start(mHandler); - mTetheredParams = InterfaceParams.getByName(mTetheredReader.getIface().getInterfaceName()); - assertNotNull(mTetheredParams); - mTetheredPacketReader = mTetheredReader.getReader(); - } - - private static final int IPV6_HEADER_LEN = 40; - private static final int ETH_HEADER_LEN = 14; - private static final int ICMPV6_NA_NS_LEN = 24; - private static final int LL_TARGET_OPTION_LEN = 8; - private static final int ICMPV6_CHECKSUM_OFFSET = 2; - private static final int ETHER_TYPE_IPV6 = 0x86dd; - - private static ByteBuffer createDadPacket(int type) { - // Refer to buildArpPacket() - int icmpLen = ICMPV6_NA_NS_LEN - + (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT - ? LL_TARGET_OPTION_LEN : 0); - final ByteBuffer buf = ByteBuffer.allocate(icmpLen + IPV6_HEADER_LEN + ETH_HEADER_LEN); - - // Ethernet header. - final MacAddress srcMac = MacAddress.fromString("33:33:ff:66:77:88"); - buf.put(srcMac.toByteArray()); - final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06"); - buf.put(dstMac.toByteArray()); - buf.putShort((short) ETHER_TYPE_IPV6); - - // IPv6 header - byte[] version = {(byte) 0x60, 0x00, 0x00, 0x00}; - buf.put(version); // Version - buf.putShort((byte) icmpLen); // Length - buf.put((byte) IPPROTO_ICMPV6); // Next header - buf.put((byte) 0xff); // Hop limit - - final byte[] target = - InetAddresses.parseNumericAddress("fe80::1122:3344:5566:7788").getAddress(); - final byte[] src; - final byte[] dst; - if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION) { - src = InetAddresses.parseNumericAddress("::").getAddress(); - dst = InetAddresses.parseNumericAddress("ff02::1:ff66:7788").getAddress(); - } else { - src = target; - dst = TetheringUtils.ALL_NODES; - } - buf.put(src); - buf.put(dst); - - // ICMPv6 Header - buf.put((byte) type); // Type - buf.put((byte) 0x00); // Code - buf.putShort((short) 0); // Checksum - buf.putInt(0); // Reserved - buf.put(target); - - if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT) { - //NA packet has LL target address - //ICMPv6 Option - buf.put((byte) 0x02); // Type - buf.put((byte) 0x01); // Length - byte[] ll_target = MacAddress.fromString("01:02:03:04:05:06").toByteArray(); - buf.put(ll_target); - } - - // Populate checksum field - final int transportOffset = ETH_HEADER_LEN + IPV6_HEADER_LEN; - final short checksum = icmpv6Checksum(buf, ETH_HEADER_LEN, transportOffset, icmpLen); - buf.putShort(transportOffset + ICMPV6_CHECKSUM_OFFSET, checksum); - - buf.flip(); - return buf; - } - - private DadProxy setupProxy() throws Exception { - DadProxy proxy = new DadProxy(mHandler, mTetheredParams); - mHandler.post(() -> proxy.setUpstreamIface(mUpstreamParams)); - - // Upstream iface is added to local network to simplify test case. - // Otherwise the test needs to create and destroy a network for the upstream iface. - sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name); - sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mTetheredParams.name); - - return proxy; - } - - // TODO: change to assert. - private boolean waitForPacket(ByteBuffer packet, TapPacketReader reader) { - byte[] p; - - while ((p = reader.popPacket(PACKET_TIMEOUT_MS)) != null) { - final ByteBuffer buffer = ByteBuffer.wrap(p); - - if (buffer.compareTo(packet) == 0) return true; - } - return false; - } - - private void updateDstMac(ByteBuffer buf, MacAddress mac) { - buf.put(mac.toByteArray()); - buf.rewind(); - } - private void updateSrcMac(ByteBuffer buf, InterfaceParams ifaceParams) { - buf.position(ETHER_SRC_ADDR_OFFSET); - buf.put(ifaceParams.macAddr.toByteArray()); - buf.rewind(); - } - - @Test - public void testNaForwardingFromUpstreamToTether() throws Exception { - ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); - - mUpstreamPacketReader.sendResponse(na); - updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01")); - updateSrcMac(na, mTetheredParams); - assertTrue(waitForPacket(na, mTetheredPacketReader)); - } - - @Test - // TODO: remove test once DAD works in both directions. - public void testNaForwardingFromTetherToUpstream() throws Exception { - ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); - - mTetheredPacketReader.sendResponse(na); - updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01")); - updateSrcMac(na, mTetheredParams); - assertFalse(waitForPacket(na, mUpstreamPacketReader)); - } - - @Test - public void testNsForwardingFromTetherToUpstream() throws Exception { - ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); - - mTetheredPacketReader.sendResponse(ns); - updateSrcMac(ns, mUpstreamParams); - assertTrue(waitForPacket(ns, mUpstreamPacketReader)); - } - - @Test - // TODO: remove test once DAD works in both directions. - public void testNsForwardingFromUpstreamToTether() throws Exception { - ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); - - mUpstreamPacketReader.sendResponse(ns); - updateSrcMac(ns, mUpstreamParams); - assertFalse(waitForPacket(ns, mTetheredPacketReader)); - } -} diff --git a/packages/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java b/packages/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java deleted file mode 100644 index 57c28fc67cc3..000000000000 --- a/packages/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE; -import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; -import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; - -import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_GET; -import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_NEW; -import static com.android.networkstack.tethering.OffloadHardwareInterface.NFNL_SUBSYS_CTNETLINK; -import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_DESTROY; -import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_NEW; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import android.net.netlink.StructNlMsgHdr; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.NativeHandle; -import android.system.Os; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ConntrackSocketTest { - private static final long TIMEOUT = 500; - - private HandlerThread mHandlerThread; - private Handler mHandler; - private final SharedLog mLog = new SharedLog("privileged-test"); - - private OffloadHardwareInterface mOffloadHw; - private OffloadHardwareInterface.Dependencies mDeps; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mHandlerThread = new HandlerThread(getClass().getSimpleName()); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - - // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads. - if (Looper.myLooper() == null) Looper.prepare(); - - mDeps = new OffloadHardwareInterface.Dependencies(mLog); - mOffloadHw = new OffloadHardwareInterface(mHandler, mLog, mDeps); - } - - @Test - public void testIpv4ConntrackSocket() throws Exception { - // Set up server and connect. - final InetSocketAddress anyAddress = new InetSocketAddress( - InetAddress.getByName("127.0.0.1"), 0); - final ServerSocket serverSocket = new ServerSocket(); - serverSocket.bind(anyAddress); - final SocketAddress theAddress = serverSocket.getLocalSocketAddress(); - - // Make a connection to the server. - final Socket socket = new Socket(); - socket.connect(theAddress); - final Socket acceptedSocket = serverSocket.accept(); - - final NativeHandle handle = mDeps.createConntrackSocket( - NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); - mOffloadHw.sendIpv4NfGenMsg(handle, - (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET), - (short) (NLM_F_REQUEST | NLM_F_DUMP)); - - boolean foundConntrackEntry = false; - ByteBuffer buffer = ByteBuffer.allocate(DEFAULT_RECV_BUFSIZE); - buffer.order(ByteOrder.nativeOrder()); - - try { - while (Os.read(handle.getFileDescriptor(), buffer) > 0) { - buffer.flip(); - - // TODO: ConntrackMessage should get a parse API like StructNlMsgHdr - // so we can confirm that the conntrack added is for the TCP connection above. - final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(buffer); - assertNotNull(nlmsghdr); - - // As long as 1 conntrack entry is found test case will pass, even if it's not - // the from the TCP connection above. - if (nlmsghdr.nlmsg_type == ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW)) { - foundConntrackEntry = true; - break; - } - } - } finally { - socket.close(); - serverSocket.close(); - } - assertTrue("Did not receive any NFNL_SUBSYS_CTNETLINK/IPCTNL_MSG_CT_NEW message", - foundConntrackEntry); - } -} diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp deleted file mode 100644 index 956e2e832589..000000000000 --- a/packages/Tethering/tests/unit/Android.bp +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -// Tests in this folder are included both in unit tests and CTS. -java_library { - name: "TetheringCommonTests", - srcs: [ - "common/**/*.java", - "common/**/*.kt" - ], - static_libs: [ - "androidx.test.rules", - "net-tests-utils", - ], - // TODO(b/147200698) change sdk_version to module-current and remove framework-minus-apex - sdk_version: "core_platform", - libs: [ - "framework-minus-apex", - "framework-tethering.impl", - ], - visibility: ["//cts/tests/tests/tethering"], -} - -java_defaults { - name: "TetheringTestsDefaults", - srcs: [ - "src/**/*.java", - "src/**/*.kt", - ], - static_libs: [ - "TetheringApiCurrentLib", - "TetheringCommonTests", - "androidx.test.rules", - "frameworks-base-testutils", - "mockito-target-extended-minus-junit4", - "net-tests-utils", - "testables", - ], - // TODO(b/147200698) change sdk_version to module-current and - // remove framework-minus-apex, ext, and framework-res - sdk_version: "core_platform", - libs: [ - "android.test.runner", - "android.test.base", - "android.test.mock", - "ext", - "framework-minus-apex", - "framework-res", - "framework-tethering.impl", - "framework-wifi.stubs.module_lib", - ], - jni_libs: [ - // For mockito extended - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], -} - -// Library containing the unit tests. This is used by the coverage test target to pull in the -// unit test code. It is not currently used by the tests themselves because all the build -// configuration needed by the tests is in the TetheringTestsDefaults rule. -android_library { - name: "TetheringTestsLib", - defaults: ["TetheringTestsDefaults"], - visibility: [ - "//frameworks/base/packages/Tethering/tests/integration", - "//packages/modules/Connectivity/Tethering/tests/integration", - ] -} - -android_test { - name: "TetheringTests", - platform_apis: true, - test_suites: [ - "device-tests", - "mts", - ], - jarjar_rules: ":TetheringTestsJarJarRules", - defaults: ["TetheringTestsDefaults"], - compile_multilib: "both", -} diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml deleted file mode 100644 index 355342f64371..000000000000 --- a/packages/Tethering/tests/unit/AndroidManifest.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt b/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt deleted file mode 100644 index 55c59dd08f41..000000000000 --- a/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net - -import android.net.InetAddresses.parseNumericAddress -import android.net.TetheredClient.AddressInfo -import android.net.TetheringManager.TETHERING_BLUETOOTH -import android.net.TetheringManager.TETHERING_USB -import android.system.OsConstants.RT_SCOPE_UNIVERSE -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.assertParcelSane -import org.junit.Test -import org.junit.runner.RunWith -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals - -private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, 67)) -private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78)) -private val TEST_ADDR1 = makeLinkAddress("192.168.113.3", prefixLength = 24, expTime = 123L) -private val TEST_ADDR2 = makeLinkAddress("fe80::1:2:3", prefixLength = 64, expTime = 456L) -private val TEST_HOSTNAME = "test_hostname" -private val TEST_OTHER_HOSTNAME = "test_other_hostname" -private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, TEST_HOSTNAME) -private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null) - -private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = LinkAddress( - parseNumericAddress(addr), - prefixLength, - 0 /* flags */, - RT_SCOPE_UNIVERSE, - expTime /* deprecationTime */, - expTime /* expirationTime */) - -@RunWith(AndroidJUnit4::class) -@SmallTest -class TetheredClientTest { - @Test - fun testParceling() { - assertParcelSane(TEST_ADDRINFO1, fieldCount = 2) - assertParcelSane(makeTestClient(), fieldCount = 3) - } - - @Test - fun testEquals() { - assertEquals(makeTestClient(), makeTestClient()) - - // Different mac address - assertNotEquals(makeTestClient(), TetheredClient( - TEST_OTHER_MACADDR, - listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), - TETHERING_BLUETOOTH)) - - // Different hostname - assertNotEquals(makeTestClient(), TetheredClient( - TEST_MACADDR, - listOf(AddressInfo(TEST_ADDR1, TEST_OTHER_HOSTNAME), TEST_ADDRINFO2), - TETHERING_BLUETOOTH)) - - // Null hostname - assertNotEquals(makeTestClient(), TetheredClient( - TEST_MACADDR, - listOf(AddressInfo(TEST_ADDR1, null), TEST_ADDRINFO2), - TETHERING_BLUETOOTH)) - - // Missing address - assertNotEquals(makeTestClient(), TetheredClient( - TEST_MACADDR, - listOf(TEST_ADDRINFO2), - TETHERING_BLUETOOTH)) - - // Different type - assertNotEquals(makeTestClient(), TetheredClient( - TEST_MACADDR, - listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), - TETHERING_USB)) - } - - @Test - fun testAddAddresses() { - val client1 = TetheredClient(TEST_MACADDR, listOf(TEST_ADDRINFO1), TETHERING_USB) - val client2 = TetheredClient(TEST_OTHER_MACADDR, listOf(TEST_ADDRINFO2), TETHERING_USB) - assertEquals(TetheredClient( - TEST_MACADDR, - listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), - TETHERING_USB), client1.addAddresses(client2)) - } - - @Test - fun testGetters() { - assertEquals(TEST_MACADDR, makeTestClient().macAddress) - assertEquals(listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), makeTestClient().addresses) - assertEquals(TETHERING_BLUETOOTH, makeTestClient().tetheringType) - } - - @Test - fun testAddressInfo_Getters() { - assertEquals(TEST_ADDR1, TEST_ADDRINFO1.address) - assertEquals(TEST_ADDR2, TEST_ADDRINFO2.address) - assertEquals(TEST_HOSTNAME, TEST_ADDRINFO1.hostname) - assertEquals(null, TEST_ADDRINFO2.hostname) - } - - private fun makeTestClient() = TetheredClient( - TEST_MACADDR, - listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), - TETHERING_BLUETOOTH) -} \ No newline at end of file diff --git a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java deleted file mode 100644 index a8857b2e5cb0..000000000000 --- a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 android.net.dhcp; - -import static android.net.InetAddresses.parseNumericAddress; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.net.LinkAddress; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.Inet4Address; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DhcpServingParamsParcelExtTest { - private static final Inet4Address TEST_ADDRESS = inet4Addr("192.168.0.123"); - private static final Inet4Address TEST_CLIENT_ADDRESS = inet4Addr("192.168.0.42"); - private static final int TEST_ADDRESS_PARCELED = 0xc0a8007b; - private static final int TEST_CLIENT_ADDRESS_PARCELED = 0xc0a8002a; - private static final int TEST_PREFIX_LENGTH = 17; - private static final int TEST_LEASE_TIME_SECS = 120; - private static final int TEST_MTU = 1000; - private static final Set TEST_ADDRESS_SET = - new HashSet(Arrays.asList( - new Inet4Address[] {inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124")})); - private static final Set TEST_ADDRESS_SET_PARCELED = - new HashSet(Arrays.asList(new Integer[] {0xc0a8017b, 0xc0a8017c})); - - private DhcpServingParamsParcelExt mParcel; - - @Before - public void setUp() { - mParcel = new DhcpServingParamsParcelExt(); - } - - @Test - public void testSetServerAddr() { - mParcel.setServerAddr(new LinkAddress(TEST_ADDRESS, TEST_PREFIX_LENGTH)); - - assertEquals(TEST_ADDRESS_PARCELED, mParcel.serverAddr); - assertEquals(TEST_PREFIX_LENGTH, mParcel.serverAddrPrefixLength); - } - - @Test - public void testSetDefaultRouters() { - mParcel.setDefaultRouters(TEST_ADDRESS_SET); - assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.defaultRouters)); - } - - @Test - public void testSetDnsServers() { - mParcel.setDnsServers(TEST_ADDRESS_SET); - assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.dnsServers)); - } - - @Test - public void testSetExcludedAddrs() { - mParcel.setExcludedAddrs(TEST_ADDRESS_SET); - assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.excludedAddrs)); - } - - @Test - public void testSetDhcpLeaseTimeSecs() { - mParcel.setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS); - assertEquals(TEST_LEASE_TIME_SECS, mParcel.dhcpLeaseTimeSecs); - } - - @Test - public void testSetLinkMtu() { - mParcel.setLinkMtu(TEST_MTU); - assertEquals(TEST_MTU, mParcel.linkMtu); - } - - @Test - public void testSetMetered() { - mParcel.setMetered(true); - assertTrue(mParcel.metered); - mParcel.setMetered(false); - assertFalse(mParcel.metered); - } - - @Test - public void testSetClientAddr() { - mParcel.setSingleClientAddr(TEST_CLIENT_ADDRESS); - assertEquals(TEST_CLIENT_ADDRESS_PARCELED, mParcel.singleClientAddr); - } - - private static Inet4Address inet4Addr(String addr) { - return (Inet4Address) parseNumericAddress(addr); - } - - private static Set asSet(int[] ints) { - return IntStream.of(ints).boxed().collect(Collectors.toSet()); - } -} diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java deleted file mode 100644 index 2eb75895ac3e..000000000000 --- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ /dev/null @@ -1,1201 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import static android.net.INetd.IF_STATE_UP; -import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_NCM; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; -import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; -import static android.net.ip.IpServer.STATE_AVAILABLE; -import static android.net.ip.IpServer.STATE_LOCAL_ONLY; -import static android.net.ip.IpServer.STATE_TETHERED; -import static android.net.ip.IpServer.STATE_UNAVAILABLE; -import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH; -import static android.net.netlink.NetlinkConstants.RTM_NEWNEIGH; -import static android.net.netlink.StructNdMsg.NUD_FAILED; -import static android.net.netlink.StructNdMsg.NUD_REACHABLE; -import static android.net.netlink.StructNdMsg.NUD_STALE; - -import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.app.usage.NetworkStatsManager; -import android.net.INetd; -import android.net.InetAddresses; -import android.net.InterfaceConfigurationParcel; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.MacAddress; -import android.net.RouteInfo; -import android.net.TetherOffloadRuleParcel; -import android.net.TetherStatsParcel; -import android.net.dhcp.DhcpServerCallbacks; -import android.net.dhcp.DhcpServingParamsParcel; -import android.net.dhcp.IDhcpEventCallbacks; -import android.net.dhcp.IDhcpServer; -import android.net.dhcp.IDhcpServerCallbacks; -import android.net.ip.IpNeighborMonitor.NeighborEvent; -import android.net.ip.IpNeighborMonitor.NeighborEventConsumer; -import android.net.ip.RouterAdvertisementDaemon.RaParams; -import android.net.util.InterfaceParams; -import android.net.util.InterfaceSet; -import android.net.util.PrefixUtils; -import android.net.util.SharedLog; -import android.os.Build; -import android.os.Handler; -import android.os.RemoteException; -import android.os.test.TestLooper; -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.networkstack.tethering.BpfCoordinator; -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; -import com.android.networkstack.tethering.PrivateAddressCoordinator; -import com.android.networkstack.tethering.TetheringConfiguration; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; -import org.mockito.Captor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.Arrays; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class IpServerTest { - @Rule - public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); - - private static final String IFACE_NAME = "testnet1"; - private static final String UPSTREAM_IFACE = "upstream0"; - private static final String UPSTREAM_IFACE2 = "upstream1"; - private static final int UPSTREAM_IFINDEX = 101; - private static final int UPSTREAM_IFINDEX2 = 102; - private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1"; - private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; - private static final int DHCP_LEASE_TIME_SECS = 3600; - private static final boolean DEFAULT_USING_BPF_OFFLOAD = true; - - private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams( - IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */); - private static final InterfaceParams UPSTREAM_IFACE_PARAMS = new InterfaceParams( - UPSTREAM_IFACE, UPSTREAM_IFINDEX, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */); - private static final InterfaceParams UPSTREAM_IFACE_PARAMS2 = new InterfaceParams( - UPSTREAM_IFACE2, UPSTREAM_IFINDEX2, MacAddress.ALL_ZEROS_ADDRESS, - 1500 /* defaultMtu */); - - private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000; - - private final LinkAddress mTestAddress = new LinkAddress("192.168.42.5/24"); - private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24"); - - @Mock private INetd mNetd; - @Mock private IpServer.Callback mCallback; - @Mock private SharedLog mSharedLog; - @Mock private IDhcpServer mDhcpServer; - @Mock private DadProxy mDadProxy; - @Mock private RouterAdvertisementDaemon mRaDaemon; - @Mock private IpNeighborMonitor mIpNeighborMonitor; - @Mock private IpServer.Dependencies mDependencies; - @Mock private PrivateAddressCoordinator mAddressCoordinator; - @Mock private NetworkStatsManager mStatsManager; - @Mock private TetheringConfiguration mTetherConfig; - - @Captor private ArgumentCaptor mDhcpParamsCaptor; - - private final TestLooper mLooper = new TestLooper(); - private final ArgumentCaptor mLinkPropertiesCaptor = - ArgumentCaptor.forClass(LinkProperties.class); - private IpServer mIpServer; - private InterfaceConfigurationParcel mInterfaceConfiguration; - private NeighborEventConsumer mNeighborEventConsumer; - private BpfCoordinator mBpfCoordinator; - - private void initStateMachine(int interfaceType) throws Exception { - initStateMachine(interfaceType, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD); - } - - private void initStateMachine(int interfaceType, boolean usingLegacyDhcp, - boolean usingBpfOffload) throws Exception { - when(mDependencies.getDadProxy(any(), any())).thenReturn(mDadProxy); - when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon); - when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS); - when(mDependencies.getInterfaceParams(UPSTREAM_IFACE)).thenReturn(UPSTREAM_IFACE_PARAMS); - when(mDependencies.getInterfaceParams(UPSTREAM_IFACE2)).thenReturn(UPSTREAM_IFACE_PARAMS2); - - when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX); - when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2); - - mInterfaceConfiguration = new InterfaceConfigurationParcel(); - mInterfaceConfiguration.flags = new String[0]; - if (interfaceType == TETHERING_BLUETOOTH) { - mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR; - mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH; - } - - ArgumentCaptor neighborCaptor = - ArgumentCaptor.forClass(NeighborEventConsumer.class); - doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(), - neighborCaptor.capture()); - - mIpServer = new IpServer( - IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mBpfCoordinator, - mCallback, usingLegacyDhcp, usingBpfOffload, mAddressCoordinator, mDependencies); - mIpServer.start(); - mNeighborEventConsumer = neighborCaptor.getValue(); - - // Starting the state machine always puts us in a consistent state and notifies - // the rest of the world that we've changed from an unknown to available state. - mLooper.dispatchAll(); - reset(mNetd, mCallback); - - when(mRaDaemon.start()).thenReturn(true); - } - - private void initTetheredStateMachine(int interfaceType, String upstreamIface) - throws Exception { - initTetheredStateMachine(interfaceType, upstreamIface, false, - DEFAULT_USING_BPF_OFFLOAD); - } - - private void initTetheredStateMachine(int interfaceType, String upstreamIface, - boolean usingLegacyDhcp, boolean usingBpfOffload) throws Exception { - initStateMachine(interfaceType, usingLegacyDhcp, usingBpfOffload); - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - if (upstreamIface != null) { - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(upstreamIface); - dispatchTetherConnectionChanged(upstreamIface, lp, 0); - } - reset(mNetd, mCallback, mAddressCoordinator); - when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn( - mTestAddress); - } - - private void setUpDhcpServer() throws Exception { - doAnswer(inv -> { - final IDhcpServerCallbacks cb = inv.getArgument(2); - new Thread(() -> { - try { - cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer); - } catch (RemoteException e) { - fail(e.getMessage()); - } - }).run(); - return null; - }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any()); - } - - @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); - when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn( - mTestAddress); - when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */); - - mBpfCoordinator = spy(new BpfCoordinator( - new BpfCoordinator.Dependencies() { - @NonNull - public Handler getHandler() { - return new Handler(mLooper.getLooper()); - } - - @NonNull - public INetd getNetd() { - return mNetd; - } - - @NonNull - public NetworkStatsManager getNetworkStatsManager() { - return mStatsManager; - } - - @NonNull - public SharedLog getSharedLog() { - return mSharedLog; - } - - @Nullable - public TetheringConfiguration getTetherConfig() { - return mTetherConfig; - } - })); - - setUpDhcpServer(); - } - - @Test - public void startsOutAvailable() { - when(mDependencies.getIpNeighborMonitor(any(), any(), any())) - .thenReturn(mIpNeighborMonitor); - mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, - mNetd, mBpfCoordinator, mCallback, false /* usingLegacyDhcp */, - DEFAULT_USING_BPF_OFFLOAD, mAddressCoordinator, mDependencies); - mIpServer.start(); - mLooper.dispatchAll(); - verify(mCallback).updateInterfaceState( - mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); - verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mCallback, mNetd); - } - - @Test - public void shouldDoNothingUntilRequested() throws Exception { - initStateMachine(TETHERING_BLUETOOTH); - final int [] noOp_commands = { - IpServer.CMD_TETHER_UNREQUESTED, - IpServer.CMD_IP_FORWARDING_ENABLE_ERROR, - IpServer.CMD_IP_FORWARDING_DISABLE_ERROR, - IpServer.CMD_START_TETHERING_ERROR, - IpServer.CMD_STOP_TETHERING_ERROR, - IpServer.CMD_SET_DNS_FORWARDERS_ERROR, - IpServer.CMD_TETHER_CONNECTION_CHANGED - }; - for (int command : noOp_commands) { - // None of these commands should trigger us to request action from - // the rest of the system. - dispatchCommand(command); - verifyNoMoreInteractions(mNetd, mCallback); - } - } - - @Test - public void handlesImmediateInterfaceDown() throws Exception { - initStateMachine(TETHERING_BLUETOOTH); - - dispatchCommand(IpServer.CMD_INTERFACE_DOWN); - verify(mCallback).updateInterfaceState( - mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); - verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mCallback); - } - - @Test - public void canBeTethered() throws Exception { - initStateMachine(TETHERING_BLUETOOTH); - - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mCallback, mNetd); - inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); - inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - // One for ipv4 route, one for ipv6 link local route. - inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), - any(), any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mCallback); - } - - @Test - public void canUnrequestTethering() throws Exception { - initTetheredStateMachine(TETHERING_BLUETOOTH, null); - - dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); - inOrder.verify(mNetd).tetherApplyDnsInterfaces(); - inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); - inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - inOrder.verify(mAddressCoordinator).releaseDownstream(any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); - } - - @Test - public void canBeTetheredAsUsb() throws Exception { - initStateMachine(TETHERING_USB); - - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true)); - inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> - IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP))); - inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); - inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), - any(), any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), mLinkPropertiesCaptor.capture()); - assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); - } - - @Test - public void canBeTetheredAsWifiP2p() throws Exception { - initStateMachine(TETHERING_WIFI_P2P); - - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); - InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true)); - inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> - IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP))); - inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); - inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), - any(), any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), mLinkPropertiesCaptor.capture()); - assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); - } - - @Test - public void handlesFirstUpstreamChange() throws Exception { - initTetheredStateMachine(TETHERING_BLUETOOTH, null); - - // Telling the state machine about its upstream interface triggers - // a little more configuration. - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - InOrder inOrder = inOrder(mNetd); - inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNetd, mCallback); - } - - @Test - public void handlesChangingUpstream() throws Exception { - initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); - - dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - verifyNoMoreInteractions(mNetd, mCallback); - } - - @Test - public void handlesChangingUpstreamNatFailure() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - - doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); - - dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); - } - - @Test - public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - - doThrow(RemoteException.class).when(mNetd).ipfwdAddInterfaceForward( - IFACE_NAME, UPSTREAM_IFACE2); - - dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); - } - - @Test - public void canUnrequestTetheringWithUpstream() throws Exception { - initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); - - dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherApplyDnsInterfaces(); - inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); - inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - inOrder.verify(mAddressCoordinator).releaseDownstream(any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); - } - - @Test - public void interfaceDownLeadsToUnavailable() throws Exception { - for (boolean shouldThrow : new boolean[]{true, false}) { - initTetheredStateMachine(TETHERING_USB, null); - - if (shouldThrow) { - doThrow(RemoteException.class).when(mNetd).tetherInterfaceRemove(IFACE_NAME); - } - dispatchCommand(IpServer.CMD_INTERFACE_DOWN); - InOrder usbTeardownOrder = inOrder(mNetd, mCallback); - // Currently IpServer interfaceSetCfg twice to stop IPv4. One just set interface down - // Another one is set IPv4 to 0.0.0.0/0 as clearng ipv4 address. - usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( - argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - usbTeardownOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); - usbTeardownOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), mLinkPropertiesCaptor.capture()); - assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); - } - } - - @Test - public void usbShouldBeTornDownOnTetherError() throws Exception { - initStateMachine(TETHERING_USB); - - doThrow(RemoteException.class).when(mNetd).tetherInterfaceAdd(IFACE_NAME); - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder usbTeardownOrder = inOrder(mNetd, mCallback); - usbTeardownOrder.verify(mNetd).interfaceSetCfg( - argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - usbTeardownOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); - - usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( - argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - usbTeardownOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR); - usbTeardownOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), mLinkPropertiesCaptor.capture()); - assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); - } - - @Test - public void shouldTearDownUsbOnUpstreamError() throws Exception { - initTetheredStateMachine(TETHERING_USB, null); - - doThrow(RemoteException.class).when(mNetd).tetherAddForward(anyString(), anyString()); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - InOrder usbTeardownOrder = inOrder(mNetd, mCallback); - usbTeardownOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE); - - usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( - argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - usbTeardownOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_FORWARDING_ERROR); - usbTeardownOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), mLinkPropertiesCaptor.capture()); - assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); - } - - @Test - public void ignoresDuplicateUpstreamNotifications() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - - verifyNoMoreInteractions(mNetd, mCallback); - - for (int i = 0; i < 5; i++) { - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - verifyNoMoreInteractions(mNetd, mCallback); - } - } - - @Test - public void startsDhcpServer() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - - assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress)); - } - - @Test - public void startsDhcpServerOnBluetooth() throws Exception { - initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - - assertDhcpStarted(mBluetoothPrefix); - } - - @Test - public void startsDhcpServerOnWifiP2p() throws Exception { - initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - - assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress)); - } - - @Test - public void startsDhcpServerOnNcm() throws Exception { - initStateMachine(TETHERING_NCM); - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - - assertDhcpStarted(new IpPrefix("192.168.42.0/24")); - } - - @Test - public void testOnNewPrefixRequest() throws Exception { - initStateMachine(TETHERING_NCM); - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); - - final IDhcpEventCallbacks eventCallbacks; - final ArgumentCaptor dhcpEventCbsCaptor = - ArgumentCaptor.forClass(IDhcpEventCallbacks.class); - verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), dhcpEventCbsCaptor.capture()); - eventCallbacks = dhcpEventCbsCaptor.getValue(); - assertDhcpStarted(new IpPrefix("192.168.42.0/24")); - - final ArgumentCaptor lpCaptor = - ArgumentCaptor.forClass(LinkProperties.class); - InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator); - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true)); - inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - // One for ipv4 route, one for ipv6 link local route. - inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), - any(), any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture()); - verifyNoMoreInteractions(mCallback, mAddressCoordinator); - - // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals - // onNewPrefixRequest callback. - final LinkAddress newAddress = new LinkAddress("192.168.100.125/24"); - when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn( - newAddress); - eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24")); - mLooper.dispatchAll(); - - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(false)); - inOrder.verify(mNetd).tetherApplyDnsInterfaces(); - inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture()); - verifyNoMoreInteractions(mCallback); - - final LinkProperties linkProperties = lpCaptor.getValue(); - final List linkAddresses = linkProperties.getLinkAddresses(); - assertEquals(1, linkProperties.getLinkAddresses().size()); - assertEquals(1, linkProperties.getRoutes().size()); - final IpPrefix prefix = new IpPrefix(linkAddresses.get(0).getAddress(), - linkAddresses.get(0).getPrefixLength()); - assertNotEquals(prefix, new IpPrefix("192.168.42.0/24")); - - verify(mDhcpServer).updateParams(mDhcpParamsCaptor.capture(), any()); - assertDhcpServingParams(mDhcpParamsCaptor.getValue(), prefix); - } - - @Test - public void doesNotStartDhcpServerIfDisabled() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */, - DEFAULT_USING_BPF_OFFLOAD); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - - verify(mDependencies, never()).makeDhcpServer(any(), any(), any()); - } - - private InetAddress addr(String addr) throws Exception { - return InetAddresses.parseNumericAddress(addr); - } - - private void recvNewNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) { - mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_NEWNEIGH, ifindex, addr, - nudState, mac)); - mLooper.dispatchAll(); - } - - private void recvDelNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) { - mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_DELNEIGH, ifindex, addr, - nudState, mac)); - mLooper.dispatchAll(); - } - - /** - * Custom ArgumentMatcher for TetherOffloadRuleParcel. This is needed because generated stable - * AIDL classes don't have equals(), so we cannot just use eq(). A custom assert, such as: - * - * private void checkFooCalled(StableParcelable p, ...) { - * ArgumentCaptor captor = ArgumentCaptor.forClass(FooParam.class); - * verify(mMock).foo(captor.capture()); - * Foo foo = captor.getValue(); - * assertFooMatchesExpectations(foo); - * } - * - * almost works, but not quite. This is because if the code under test calls foo() twice, the - * first call to checkFooCalled() matches both the calls, putting both calls into the captor, - * and then fails with TooManyActualInvocations. It also makes it harder to use other mockito - * features such as never(), inOrder(), etc. - * - * This approach isn't great because if the match fails, the error message is unhelpful - * (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does - * work. - * - * TODO: consider making the error message more readable by adding a method that catching the - * AssertionFailedError and throwing a new assertion with more details. See - * NetworkMonitorTest#verifyNetworkTested. - * - * See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the - * TooManyActualInvocations problem described above by forcing the caller of the custom assert - * method to specify all expected invocations in one call. This is useful when the stable - * parcelable class being asserted on has a corresponding Java object (eg., RouteInfo and - * RouteInfoParcelable), and the caller can just pass in a list of them. It not useful here - * because there is no such object. - */ - private static class TetherOffloadRuleParcelMatcher implements - ArgumentMatcher { - public final int upstreamIfindex; - public final InetAddress dst; - public final MacAddress dstMac; - - TetherOffloadRuleParcelMatcher(int upstreamIfindex, InetAddress dst, MacAddress dstMac) { - this.upstreamIfindex = upstreamIfindex; - this.dst = dst; - this.dstMac = dstMac; - } - - public boolean matches(TetherOffloadRuleParcel parcel) { - return upstreamIfindex == parcel.inputInterfaceIndex - && (TEST_IFACE_PARAMS.index == parcel.outputInterfaceIndex) - && Arrays.equals(dst.getAddress(), parcel.destination) - && (128 == parcel.prefixLength) - && Arrays.equals(TEST_IFACE_PARAMS.macAddr.toByteArray(), parcel.srcL2Address) - && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address); - } - - public String toString() { - return String.format("TetherOffloadRuleParcelMatcher(%d, %s, %s", - upstreamIfindex, dst.getHostAddress(), dstMac); - } - } - - @NonNull - private static TetherOffloadRuleParcel matches( - int upstreamIfindex, InetAddress dst, MacAddress dstMac) { - return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac)); - } - - @NonNull - private static Ipv6ForwardingRule makeForwardingRule( - int upstreamIfindex, @NonNull InetAddress dst, @NonNull MacAddress dstMac) { - return new Ipv6ForwardingRule(upstreamIfindex, TEST_IFACE_PARAMS.index, - (Inet6Address) dst, TEST_IFACE_PARAMS.macAddr, dstMac); - } - - @NonNull - private static TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) { - TetherStatsParcel parcel = new TetherStatsParcel(); - parcel.ifIndex = ifIndex; - return parcel; - } - - private void resetNetdAndBpfCoordinator() throws Exception { - reset(mNetd, mBpfCoordinator); - when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]); - when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX)) - .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX)); - when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX2)) - .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX2)); - } - - @Test - public void addRemoveipv6ForwardingRules() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - DEFAULT_USING_BPF_OFFLOAD); - - final int myIfindex = TEST_IFACE_PARAMS.index; - final int notMyIfindex = myIfindex - 1; - - final MacAddress myMac = TEST_IFACE_PARAMS.macAddr; - final InetAddress neighA = InetAddresses.parseNumericAddress("2001:db8::1"); - final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2"); - final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1"); - final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234"); - final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00"); - final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a"); - final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b"); - - resetNetdAndBpfCoordinator(); - verifyNoMoreInteractions(mBpfCoordinator, mNetd); - - // TODO: Perhaps verify the interaction of tetherOffloadSetInterfaceQuota and - // tetherOffloadGetAndClearStats in netd while the rules are changed. - - // Events on other interfaces are ignored. - recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA); - verifyNoMoreInteractions(mBpfCoordinator, mNetd); - - // Events on this interface are received and sent to netd. - recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); - resetNetdAndBpfCoordinator(); - - recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); - resetNetdAndBpfCoordinator(); - - // Link-local and multicast neighbors are ignored. - recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA); - verifyNoMoreInteractions(mBpfCoordinator, mNetd); - recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA); - verifyNoMoreInteractions(mBpfCoordinator, mNetd); - - // A neighbor that is no longer valid causes the rule to be removed. - // NUD_FAILED events do not have a MAC address. - recvNewNeigh(myIfindex, neighA, NUD_FAILED, null); - verify(mBpfCoordinator).tetherOffloadRuleRemove( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macNull)); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macNull)); - resetNetdAndBpfCoordinator(); - - // A neighbor that is deleted causes the rule to be removed. - recvDelNeigh(myIfindex, neighB, NUD_STALE, macB); - verify(mBpfCoordinator).tetherOffloadRuleRemove( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macNull)); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macNull)); - resetNetdAndBpfCoordinator(); - - // Upstream changes result in updating the rules. - recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - resetNetdAndBpfCoordinator(); - - InOrder inOrder = inOrder(mNetd); - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(UPSTREAM_IFACE2); - dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1); - verify(mBpfCoordinator).tetherOffloadRuleUpdate(mIpServer, UPSTREAM_IFINDEX2); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA)); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB)); - resetNetdAndBpfCoordinator(); - - // When the upstream is lost, rules are removed. - dispatchTetherConnectionChanged(null, null, 0); - // Clear function is called two times by: - // - processMessage CMD_TETHER_CONNECTION_CHANGED for the upstream is lost. - // - processMessage CMD_IPV6_TETHER_UPDATE for the IPv6 upstream is lost. - // See dispatchTetherConnectionChanged. - verify(mBpfCoordinator, times(2)).tetherOffloadRuleClear(mIpServer); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA)); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB)); - resetNetdAndBpfCoordinator(); - - // If the upstream is IPv4-only, no rules are added. - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - resetNetdAndBpfCoordinator(); - recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - // Clear function is called by #updateIpv6ForwardingRules for the IPv6 upstream is lost. - verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer); - verifyNoMoreInteractions(mBpfCoordinator, mNetd); - - // Rules can be added again once upstream IPv6 connectivity is available. - lp.setInterfaceName(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); - recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); - verify(mBpfCoordinator, never()).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); - verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); - - // If upstream IPv6 connectivity is lost, rules are removed. - resetNetdAndBpfCoordinator(); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); - verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); - - // When the interface goes down, rules are removed. - lp.setInterfaceName(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); - recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); - resetNetdAndBpfCoordinator(); - - mIpServer.stop(); - mLooper.dispatchAll(); - verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); - verify(mIpNeighborMonitor).stop(); - resetNetdAndBpfCoordinator(); - } - - @Test - public void enableDisableUsingBpfOffload() throws Exception { - final int myIfindex = TEST_IFACE_PARAMS.index; - final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1"); - final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a"); - final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00"); - - // Expect that rules can be only added/removed when the BPF offload config is enabled. - // Note that the BPF offload disabled case is not a realistic test case. Because IP - // neighbor monitor doesn't start if BPF offload is disabled, there should have no - // neighbor event listening. This is used for testing the protection check just in case. - // TODO: Perhaps remove the BPF offload disabled case test once this check isn't needed - // anymore. - - // [1] Enable BPF offload. - // A neighbor that is added or deleted causes the rule to be added or removed. - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - true /* usingBpfOffload */); - resetNetdAndBpfCoordinator(); - - recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macA)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA)); - resetNetdAndBpfCoordinator(); - - recvDelNeigh(myIfindex, neigh, NUD_STALE, macA); - verify(mBpfCoordinator).tetherOffloadRuleRemove( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macNull)); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull)); - resetNetdAndBpfCoordinator(); - - // [2] Disable BPF offload. - // A neighbor that is added or deleted doesn’t cause the rule to be added or removed. - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - false /* usingBpfOffload */); - resetNetdAndBpfCoordinator(); - - recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); - verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(any(), any()); - verify(mNetd, never()).tetherOffloadRuleAdd(any()); - resetNetdAndBpfCoordinator(); - - recvDelNeigh(myIfindex, neigh, NUD_STALE, macA); - verify(mBpfCoordinator, never()).tetherOffloadRuleRemove(any(), any()); - verify(mNetd, never()).tetherOffloadRuleRemove(any()); - resetNetdAndBpfCoordinator(); - } - - @Test - public void doesNotStartIpNeighborMonitorIfBpfOffloadDisabled() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - false /* usingBpfOffload */); - - // IP neighbor monitor doesn't start if BPF offload is disabled. - verify(mIpNeighborMonitor, never()).start(); - } - - private LinkProperties buildIpv6OnlyLinkProperties(final String iface) { - final LinkProperties linkProp = new LinkProperties(); - linkProp.setInterfaceName(iface); - linkProp.addLinkAddress(new LinkAddress("2001:db8::1/64")); - linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, iface, RTN_UNICAST)); - final InetAddress dns = InetAddresses.parseNumericAddress("2001:4860:4860::8888"); - linkProp.addDnsServer(dns); - - return linkProp; - } - - @Test - public void testAdjustTtlValue() throws Exception { - final ArgumentCaptor raParamsCaptor = - ArgumentCaptor.forClass(RaParams.class); - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); - final RaParams noV6Params = raParamsCaptor.getValue(); - assertEquals(65, noV6Params.hopLimit); - reset(mRaDaemon); - - when(mNetd.getProcSysNet( - INetd.IPV6, INetd.CONF, UPSTREAM_IFACE, "hop_limit")).thenReturn("64"); - final LinkProperties lp = buildIpv6OnlyLinkProperties(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 1); - verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); - final RaParams nonCellularParams = raParamsCaptor.getValue(); - assertEquals(65, nonCellularParams.hopLimit); - reset(mRaDaemon); - - dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); - verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); - final RaParams noUpstream = raParamsCaptor.getValue(); - assertEquals(65, nonCellularParams.hopLimit); - reset(mRaDaemon); - - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); - verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); - final RaParams cellularParams = raParamsCaptor.getValue(); - assertEquals(63, cellularParams.hopLimit); - reset(mRaDaemon); - } - - @Test - public void testStopObsoleteDhcpServer() throws Exception { - final ArgumentCaptor cbCaptor = - ArgumentCaptor.forClass(DhcpServerCallbacks.class); - doNothing().when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), - cbCaptor.capture()); - initStateMachine(TETHERING_WIFI); - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - verify(mDhcpServer, never()).startWithCallbacks(any(), any()); - - // No stop dhcp server because dhcp server is not created yet. - dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - verify(mDhcpServer, never()).stop(any()); - - // Stop obsolete dhcp server. - try { - final DhcpServerCallbacks cb = cbCaptor.getValue(); - cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer); - mLooper.dispatchAll(); - } catch (RemoteException e) { - fail(e.getMessage()); - } - verify(mDhcpServer).stop(any()); - } - - private void assertDhcpServingParams(final DhcpServingParamsParcel params, - final IpPrefix prefix) { - // Last address byte is random - assertTrue(prefix.contains(intToInet4AddressHTH(params.serverAddr))); - assertEquals(prefix.getPrefixLength(), params.serverAddrPrefixLength); - assertEquals(1, params.defaultRouters.length); - assertEquals(params.serverAddr, params.defaultRouters[0]); - assertEquals(1, params.dnsServers.length); - assertEquals(params.serverAddr, params.dnsServers[0]); - assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs); - if (mIpServer.interfaceType() == TETHERING_NCM) { - assertTrue(params.changePrefixOnDecline); - } - } - - private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception { - verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any()); - verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - assertDhcpServingParams(mDhcpParamsCaptor.getValue(), expectedPrefix); - } - - /** - * Send a command to the state machine under test, and run the event loop to idle. - * - * @param command One of the IpServer.CMD_* constants. - * @param arg1 An additional argument to pass. - */ - private void dispatchCommand(int command, int arg1) { - mIpServer.sendMessage(command, arg1); - mLooper.dispatchAll(); - } - - /** - * Send a command to the state machine under test, and run the event loop to idle. - * - * @param command One of the IpServer.CMD_* constants. - */ - private void dispatchCommand(int command) { - mIpServer.sendMessage(command); - mLooper.dispatchAll(); - } - - /** - * Special override to tell the state machine that the upstream interface has changed. - * - * @see #dispatchCommand(int) - * @param upstreamIface String name of upstream interface (or null) - * @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream. - */ - private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp, - int ttlAdjustment) { - dispatchTetherConnectionChanged(upstreamIface); - mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, ttlAdjustment, 0, v6lp); - mLooper.dispatchAll(); - } - - private void dispatchTetherConnectionChanged(String upstreamIface) { - final InterfaceSet ifs = (upstreamIface != null) ? new InterfaceSet(upstreamIface) : null; - mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifs); - mLooper.dispatchAll(); - } - - private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) { - // Find the first IPv4 LinkAddress. - LinkAddress addr4 = null; - for (LinkAddress addr : lp.getLinkAddresses()) { - if (!(addr.getAddress() instanceof Inet4Address)) continue; - addr4 = addr; - break; - } - assertNotNull("missing IPv4 address", addr4); - - final IpPrefix destination = new IpPrefix(addr4.getAddress(), addr4.getPrefixLength()); - // Assert the presence of the associated directly connected route. - final RouteInfo directlyConnected = new RouteInfo(destination, null, lp.getInterfaceName(), - RouteInfo.RTN_UNICAST); - assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'", - lp.getRoutes().contains(directlyConnected)); - } - - private void assertNoAddressesNorRoutes(LinkProperties lp) { - assertTrue(lp.getLinkAddresses().isEmpty()); - assertTrue(lp.getRoutes().isEmpty()); - // We also check that interface name is non-empty, because we should - // never see an empty interface name in any LinkProperties update. - assertFalse(TextUtils.isEmpty(lp.getInterfaceName())); - } - - private boolean assertContainsFlag(String[] flags, String match) { - for (String flag : flags) { - if (flag.equals(match)) return true; - } - fail("Missing flag: " + match); - return false; - } - - private boolean assertNotContainsFlag(String[] flags, String match) { - for (String flag : flags) { - if (flag.equals(match)) { - fail("Unexpected flag: " + match); - return false; - } - } - return true; - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void dadProxyUpdates() throws Exception { - InOrder inOrder = inOrder(mDadProxy); - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); - - // Add an upstream without IPv6. - dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); - inOrder.verify(mDadProxy).setUpstreamIface(null); - - // Add IPv6 to the upstream. - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0); - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); - - // Change upstream. - // New linkproperties is needed, otherwise changing the iface has no impact. - LinkProperties lp2 = new LinkProperties(); - lp2.setInterfaceName(UPSTREAM_IFACE2); - dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp2, 0); - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS2); - - // Lose IPv6 on the upstream... - dispatchTetherConnectionChanged(UPSTREAM_IFACE2, null, 0); - inOrder.verify(mDadProxy).setUpstreamIface(null); - - // ... and regain it on a different upstream. - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0); - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); - - // Lose upstream. - dispatchTetherConnectionChanged(null, null, 0); - inOrder.verify(mDadProxy).setUpstreamIface(null); - - // Regain upstream. - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0); - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); - - // Stop tethering. - mIpServer.stop(); - mLooper.dispatchAll(); - } - - private void checkDadProxyEnabled(boolean expectEnabled) throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - InOrder inOrder = inOrder(mDadProxy); - // Add IPv6 to the upstream. - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(UPSTREAM_IFACE); - if (expectEnabled) { - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); - } else { - inOrder.verifyNoMoreInteractions(); - } - // Stop tethering. - mIpServer.stop(); - mLooper.dispatchAll(); - if (expectEnabled) { - inOrder.verify(mDadProxy).stop(); - } - else { - verify(mDependencies, never()).getDadProxy(any(), any()); - } - } - @Test @IgnoreAfter(Build.VERSION_CODES.R) - public void testDadProxyUpdates_DisabledUpToR() throws Exception { - checkDadProxyEnabled(false); - } - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void testDadProxyUpdates_EnabledAfterR() throws Exception { - checkDadProxyEnabled(true); - } -} diff --git a/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java b/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java deleted file mode 100644 index ea084b607868..000000000000 --- a/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 android.net.util; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class InterfaceSetTest { - @Test - public void testNullNamesIgnored() { - final InterfaceSet set = new InterfaceSet(null, "if1", null, "if2", null); - assertEquals(2, set.ifnames.size()); - assertTrue(set.ifnames.contains("if1")); - assertTrue(set.ifnames.contains("if2")); - } - - @Test - public void testToString() { - final InterfaceSet set = new InterfaceSet("if1", "if2"); - final String setString = set.toString(); - assertTrue(setString.equals("[if1,if2]") || setString.equals("[if2,if1]")); - } - - @Test - public void testToString_Empty() { - final InterfaceSet set = new InterfaceSet(null, null); - assertEquals("[]", set.toString()); - } - - @Test - public void testEquals() { - assertEquals(new InterfaceSet(null, "if1", "if2"), new InterfaceSet("if2", "if1")); - assertEquals(new InterfaceSet(null, null), new InterfaceSet()); - assertFalse(new InterfaceSet("if1", "if3").equals(new InterfaceSet("if1", "if2"))); - assertFalse(new InterfaceSet("if1", "if2").equals(new InterfaceSet("if1"))); - assertFalse(new InterfaceSet().equals(null)); - } -} diff --git a/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java deleted file mode 100644 index 91c7771cc7fe..000000000000 --- a/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net.util; - -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import android.net.LinkAddress; -import android.net.TetheringRequestParcel; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.MiscAsserts; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class TetheringUtilsTest { - private static final LinkAddress TEST_SERVER_ADDR = new LinkAddress("192.168.43.1/24"); - private static final LinkAddress TEST_CLIENT_ADDR = new LinkAddress("192.168.43.5/24"); - private TetheringRequestParcel mTetheringRequest; - - @Before - public void setUp() { - mTetheringRequest = makeTetheringRequestParcel(); - } - - public TetheringRequestParcel makeTetheringRequestParcel() { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = TETHERING_WIFI; - request.localIPv4Address = TEST_SERVER_ADDR; - request.staticClientAddress = TEST_CLIENT_ADDR; - request.exemptFromEntitlementCheck = false; - request.showProvisioningUi = true; - return request; - } - - @Test - public void testIsTetheringRequestEquals() throws Exception { - TetheringRequestParcel request = makeTetheringRequestParcel(); - - assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, mTetheringRequest)); - assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - assertTrue(TetheringUtils.isTetheringRequestEquals(null, null)); - assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, null)); - assertFalse(TetheringUtils.isTetheringRequestEquals(null, mTetheringRequest)); - - request = makeTetheringRequestParcel(); - request.tetheringType = TETHERING_USB; - assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - - request = makeTetheringRequestParcel(); - request.localIPv4Address = null; - request.staticClientAddress = null; - assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - - request = makeTetheringRequestParcel(); - request.exemptFromEntitlementCheck = true; - assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - - request = makeTetheringRequestParcel(); - request.showProvisioningUi = false; - assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - - MiscAsserts.assertFieldCountEquals(5, TetheringRequestParcel.class); - } -} diff --git a/packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java b/packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java deleted file mode 100644 index 5a9b6e380ea9..000000000000 --- a/packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2017 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 android.net.util; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.reset; - -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Handler; -import android.os.Looper; -import android.os.UserHandle; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.test.BroadcastInterceptingContext; - -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class VersionedBroadcastListenerTest { - private static final String TAG = VersionedBroadcastListenerTest.class.getSimpleName(); - private static final String ACTION_TEST = "action.test.happy.broadcasts"; - - @Mock private Context mContext; - private BroadcastInterceptingContext mServiceContext; - private Handler mHandler; - private VersionedBroadcastListener mListener; - private int mCallbackCount; - - private void doCallback() { - mCallbackCount++; - } - - private class MockContext extends BroadcastInterceptingContext { - MockContext(Context base) { - super(base); - } - } - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - } - - @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - reset(mContext); - mServiceContext = new MockContext(mContext); - mHandler = new Handler(Looper.myLooper()); - mCallbackCount = 0; - final IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_TEST); - mListener = new VersionedBroadcastListener( - TAG, mServiceContext, mHandler, filter, (Intent intent) -> doCallback()); - } - - @After public void tearDown() throws Exception { - if (mListener != null) { - mListener.stopListening(); - mListener = null; - } - } - - private void sendBroadcast() { - final Intent intent = new Intent(ACTION_TEST); - mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - @Test - public void testBasicListening() { - assertEquals(0, mCallbackCount); - mListener.startListening(); - for (int i = 0; i < 5; i++) { - sendBroadcast(); - assertEquals(i + 1, mCallbackCount); - } - mListener.stopListening(); - } - - @Test - public void testBroadcastsBeforeStartAreIgnored() { - assertEquals(0, mCallbackCount); - for (int i = 0; i < 5; i++) { - sendBroadcast(); - assertEquals(0, mCallbackCount); - } - - mListener.startListening(); - sendBroadcast(); - assertEquals(1, mCallbackCount); - } - - @Test - public void testBroadcastsAfterStopAreIgnored() { - mListener.startListening(); - sendBroadcast(); - assertEquals(1, mCallbackCount); - mListener.stopListening(); - - for (int i = 0; i < 5; i++) { - sendBroadcast(); - assertEquals(1, mCallbackCount); - } - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java deleted file mode 100644 index 64242ae8255f..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStats.UID_TETHERING; -import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; - -import static com.android.networkstack.tethering.BpfCoordinator.StatsType; -import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE; -import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID; -import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.argThat; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.usage.NetworkStatsManager; -import android.net.INetd; -import android.net.InetAddresses; -import android.net.MacAddress; -import android.net.NetworkStats; -import android.net.TetherOffloadRuleParcel; -import android.net.TetherStatsParcel; -import android.net.ip.IpServer; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.test.TestLooper; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; -import com.android.testutils.TestableNetworkStatsProviderCbBinder; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class BpfCoordinatorTest { - private static final int DOWNSTREAM_IFINDEX = 10; - private static final MacAddress DOWNSTREAM_MAC = MacAddress.ALL_ZEROS_ADDRESS; - private static final InetAddress NEIGH_A = InetAddresses.parseNumericAddress("2001:db8::1"); - private static final InetAddress NEIGH_B = InetAddresses.parseNumericAddress("2001:db8::2"); - private static final MacAddress MAC_A = MacAddress.fromString("00:00:00:00:00:0a"); - private static final MacAddress MAC_B = MacAddress.fromString("11:22:33:00:00:0b"); - - @Mock private NetworkStatsManager mStatsManager; - @Mock private INetd mNetd; - @Mock private IpServer mIpServer; - @Mock private TetheringConfiguration mTetherConfig; - - // Late init since methods must be called by the thread that created this object. - private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; - private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider; - private final ArgumentCaptor mStringArrayCaptor = - ArgumentCaptor.forClass(ArrayList.class); - private final TestLooper mTestLooper = new TestLooper(); - private BpfCoordinator.Dependencies mDeps = - new BpfCoordinator.Dependencies() { - @NonNull - public Handler getHandler() { - return new Handler(mTestLooper.getLooper()); - } - - @NonNull - public INetd getNetd() { - return mNetd; - } - - @NonNull - public NetworkStatsManager getNetworkStatsManager() { - return mStatsManager; - } - - @NonNull - public SharedLog getSharedLog() { - return new SharedLog("test"); - } - - @Nullable - public TetheringConfiguration getTetherConfig() { - return mTetherConfig; - } - }; - - @Before public void setUp() { - MockitoAnnotations.initMocks(this); - when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */); - } - - private void waitForIdle() { - mTestLooper.dispatchAll(); - } - - private void setupFunctioningNetdInterface() throws Exception { - when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]); - } - - @NonNull - private BpfCoordinator makeBpfCoordinator() throws Exception { - final BpfCoordinator coordinator = new BpfCoordinator(mDeps); - final ArgumentCaptor - tetherStatsProviderCaptor = - ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class); - verify(mStatsManager).registerNetworkStatsProvider(anyString(), - tetherStatsProviderCaptor.capture()); - mTetherStatsProvider = tetherStatsProviderCaptor.getValue(); - assertNotNull(mTetherStatsProvider); - mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder(); - mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb); - return coordinator; - } - - @NonNull - private static NetworkStats.Entry buildTestEntry(@NonNull StatsType how, - @NonNull String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) { - return new NetworkStats.Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, - SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, - rxPackets, txBytes, txPackets, 0L); - } - - @NonNull - private static TetherStatsParcel buildTestTetherStatsParcel(@NonNull Integer ifIndex, - long rxBytes, long rxPackets, long txBytes, long txPackets) { - final TetherStatsParcel parcel = new TetherStatsParcel(); - parcel.ifIndex = ifIndex; - parcel.rxBytes = rxBytes; - parcel.rxPackets = rxPackets; - parcel.txBytes = txBytes; - parcel.txPackets = txPackets; - return parcel; - } - - // Set up specific tether stats list and wait for the stats cache is updated by polling thread - // in the coordinator. Beware of that it is only used for the default polling interval. - private void setTetherOffloadStatsList(TetherStatsParcel[] tetherStatsList) throws Exception { - when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - } - - @Test - public void testGetForwardedStats() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - coordinator.startPolling(); - - final String wlanIface = "wlan0"; - final Integer wlanIfIndex = 100; - final String mobileIface = "rmnet_data0"; - final Integer mobileIfIndex = 101; - - // Add interface name to lookup table. In realistic case, the upstream interface name will - // be added by IpServer when IpServer has received with a new IPv6 upstream update event. - coordinator.addUpstreamNameToLookupTable(wlanIfIndex, wlanIface); - coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); - - // [1] Both interface stats are changed. - // Setup the tether stats of wlan and mobile interface. Note that move forward the time of - // the looper to make sure the new tether stats has been updated by polling update thread. - setTetherOffloadStatsList(new TetherStatsParcel[] { - buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200), - buildTestTetherStatsParcel(mobileIfIndex, 3000, 300, 4000, 400)}); - - final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 1000, 100, 2000, 200)) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 3000, 300, 4000, 400)); - - final NetworkStats expectedUidStats = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 1000, 100, 2000, 200)) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 3000, 300, 4000, 400)); - - // Force pushing stats update to verify the stats reported. - // TODO: Perhaps make #expectNotifyStatsUpdated to use test TetherStatsParcel object for - // verifying the notification. - mTetherStatsProvider.pushTetherStats(); - mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats); - - // [2] Only one interface stats is changed. - // The tether stats of mobile interface is accumulated and The tether stats of wlan - // interface is the same. - setTetherOffloadStatsList(new TetherStatsParcel[] { - buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200), - buildTestTetherStatsParcel(mobileIfIndex, 3010, 320, 4030, 440)}); - - final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 0, 0, 0, 0)) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 10, 20, 30, 40)); - - final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 0, 0, 0, 0)) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 10, 20, 30, 40)); - - // Force pushing stats update to verify that only diff of stats is reported. - mTetherStatsProvider.pushTetherStats(); - mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff, - expectedUidStatsDiff); - - // [3] Stop coordinator. - // Shutdown the coordinator and clear the invocation history, especially the - // tetherOffloadGetStats() calls. - coordinator.stopPolling(); - clearInvocations(mNetd); - - // Verify the polling update thread stopped. - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - verify(mNetd, never()).tetherOffloadGetStats(); - } - - @Test - public void testOnSetAlert() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - coordinator.startPolling(); - - final String mobileIface = "rmnet_data0"; - final Integer mobileIfIndex = 100; - coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); - - // Verify that set quota to 0 will immediately triggers a callback. - mTetherStatsProvider.onSetAlert(0); - waitForIdle(); - mTetherStatsProviderCb.expectNotifyAlertReached(); - - // Verify that notifyAlertReached never fired if quota is not yet reached. - when(mNetd.tetherOffloadGetStats()).thenReturn( - new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)}); - mTetherStatsProvider.onSetAlert(100); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.assertNoCallback(); - - // Verify that notifyAlertReached fired when quota is reached. - when(mNetd.tetherOffloadGetStats()).thenReturn( - new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0)}); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.expectNotifyAlertReached(); - - // Verify that set quota with UNLIMITED won't trigger any callback. - mTetherStatsProvider.onSetAlert(QUOTA_UNLIMITED); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.assertNoCallback(); - } - - // The custom ArgumentMatcher simply comes from IpServerTest. - // TODO: move both of them into a common utility class for reusing the code. - private static class TetherOffloadRuleParcelMatcher implements - ArgumentMatcher { - public final int upstreamIfindex; - public final int downstreamIfindex; - public final Inet6Address address; - public final MacAddress srcMac; - public final MacAddress dstMac; - - TetherOffloadRuleParcelMatcher(@NonNull Ipv6ForwardingRule rule) { - upstreamIfindex = rule.upstreamIfindex; - downstreamIfindex = rule.downstreamIfindex; - address = rule.address; - srcMac = rule.srcMac; - dstMac = rule.dstMac; - } - - public boolean matches(@NonNull TetherOffloadRuleParcel parcel) { - return upstreamIfindex == parcel.inputInterfaceIndex - && (downstreamIfindex == parcel.outputInterfaceIndex) - && Arrays.equals(address.getAddress(), parcel.destination) - && (128 == parcel.prefixLength) - && Arrays.equals(srcMac.toByteArray(), parcel.srcL2Address) - && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address); - } - - public String toString() { - return String.format("TetherOffloadRuleParcelMatcher(%d, %d, %s, %s, %s", - upstreamIfindex, downstreamIfindex, address.getHostAddress(), srcMac, dstMac); - } - } - - @NonNull - private TetherOffloadRuleParcel matches(@NonNull Ipv6ForwardingRule rule) { - return argThat(new TetherOffloadRuleParcelMatcher(rule)); - } - - @NonNull - private static Ipv6ForwardingRule buildTestForwardingRule( - int upstreamIfindex, @NonNull InetAddress address, @NonNull MacAddress dstMac) { - return new Ipv6ForwardingRule(upstreamIfindex, DOWNSTREAM_IFINDEX, (Inet6Address) address, - DOWNSTREAM_MAC, dstMac); - } - - @Test - public void testSetDataLimit() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - - final String mobileIface = "rmnet_data0"; - final Integer mobileIfIndex = 100; - coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); - - // [1] Default limit. - // Set the unlimited quota as default if the service has never applied a data limit for a - // given upstream. Note that the data limit only be applied on an upstream which has rules. - final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); - final InOrder inOrder = inOrder(mNetd); - coordinator.tetherOffloadRuleAdd(mIpServer, rule); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(rule)); - inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED); - inOrder.verifyNoMoreInteractions(); - - // [2] Specific limit. - // Applying the data limit boundary {min, 1gb, max, infinity} on current upstream. - for (final long quota : new long[] {0, 1048576000, Long.MAX_VALUE, QUOTA_UNLIMITED}) { - mTetherStatsProvider.onSetLimit(mobileIface, quota); - waitForIdle(); - inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, quota); - inOrder.verifyNoMoreInteractions(); - } - - // [3] Invalid limit. - // The valid range of quota is 0..max_int64 or -1 (unlimited). - final long invalidLimit = Long.MIN_VALUE; - try { - mTetherStatsProvider.onSetLimit(mobileIface, invalidLimit); - waitForIdle(); - fail("No exception thrown for invalid limit " + invalidLimit + "."); - } catch (IllegalArgumentException expected) { - assertEquals(expected.getMessage(), "invalid quota value " + invalidLimit); - } - } - - // TODO: Test the case in which the rules are changed from different IpServer objects. - @Test - public void testSetDataLimitOnRuleChange() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - - final String mobileIface = "rmnet_data0"; - final Integer mobileIfIndex = 100; - coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); - - // Applying a data limit to the current upstream does not take any immediate action. - // The data limit could be only set on an upstream which has rules. - final long limit = 12345; - final InOrder inOrder = inOrder(mNetd); - mTetherStatsProvider.onSetLimit(mobileIface, limit); - waitForIdle(); - inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong()); - - // Adding the first rule on current upstream immediately sends the quota to netd. - final Ipv6ForwardingRule ruleA = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); - coordinator.tetherOffloadRuleAdd(mIpServer, ruleA); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ruleA)); - inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, limit); - inOrder.verifyNoMoreInteractions(); - - // Adding the second rule on current upstream does not send the quota to netd. - final Ipv6ForwardingRule ruleB = buildTestForwardingRule(mobileIfIndex, NEIGH_B, MAC_B); - coordinator.tetherOffloadRuleAdd(mIpServer, ruleB); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ruleB)); - inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong()); - - // Removing the second rule on current upstream does not send the quota to netd. - coordinator.tetherOffloadRuleRemove(mIpServer, ruleB); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleB)); - inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong()); - - // Removing the last rule on current upstream immediately sends the cleanup stuff to netd. - when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex)) - .thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)); - coordinator.tetherOffloadRuleRemove(mIpServer, ruleA); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleA)); - inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testTetherOffloadRuleUpdateAndClear() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - - final String ethIface = "eth1"; - final String mobileIface = "rmnet_data0"; - final Integer ethIfIndex = 100; - final Integer mobileIfIndex = 101; - coordinator.addUpstreamNameToLookupTable(ethIfIndex, ethIface); - coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); - - final InOrder inOrder = inOrder(mNetd); - - // Before the rule test, here are the additional actions while the rules are changed. - // - After adding the first rule on a given upstream, the coordinator adds a data limit. - // If the service has never applied the data limit, set an unlimited quota as default. - // - After removing the last rule on a given upstream, the coordinator gets the last stats. - // Then, it clears the stats and the limit entry from BPF maps. - // See tetherOffloadRule{Add, Remove, Clear, Clean}. - - // [1] Adding rules on the upstream Ethernet. - // Note that the default data limit is applied after the first rule is added. - final Ipv6ForwardingRule ethernetRuleA = buildTestForwardingRule( - ethIfIndex, NEIGH_A, MAC_A); - final Ipv6ForwardingRule ethernetRuleB = buildTestForwardingRule( - ethIfIndex, NEIGH_B, MAC_B); - - coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleA); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ethernetRuleA)); - inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(ethIfIndex, QUOTA_UNLIMITED); - - coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleB); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ethernetRuleB)); - - // [2] Update the existing rules from Ethernet to cellular. - final Ipv6ForwardingRule mobileRuleA = buildTestForwardingRule( - mobileIfIndex, NEIGH_A, MAC_A); - final Ipv6ForwardingRule mobileRuleB = buildTestForwardingRule( - mobileIfIndex, NEIGH_B, MAC_B); - when(mNetd.tetherOffloadGetAndClearStats(ethIfIndex)) - .thenReturn(buildTestTetherStatsParcel(ethIfIndex, 10, 20, 30, 40)); - - // Update the existing rules for upstream changes. The rules are removed and re-added one - // by one for updating upstream interface index by #tetherOffloadRuleUpdate. - coordinator.tetherOffloadRuleUpdate(mIpServer, mobileIfIndex); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleA)); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(mobileRuleA)); - inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleB)); - inOrder.verify(mNetd).tetherOffloadGetAndClearStats(ethIfIndex); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(mobileRuleB)); - - // [3] Clear all rules for a given IpServer. - when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex)) - .thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 50, 60, 70, 80)); - coordinator.tetherOffloadRuleClear(mIpServer); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleA)); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleB)); - inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex); - - // [4] Force pushing stats update to verify that the last diff of stats is reported on all - // upstreams. - mTetherStatsProvider.pushTetherStats(); - mTetherStatsProviderCb.expectNotifyStatsUpdated( - new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, ethIface, 10, 20, 30, 40)) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 50, 60, 70, 80)), - new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, ethIface, 10, 20, 30, 40)) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 50, 60, 70, 80))); - } - - @Test - public void testTetheringConfigDisable() throws Exception { - setupFunctioningNetdInterface(); - when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(false); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - coordinator.startPolling(); - - // The tether stats polling task should not be scheduled. - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - verify(mNetd, never()).tetherOffloadGetStats(); - - // The interface name lookup table can't be added. - final String iface = "rmnet_data0"; - final Integer ifIndex = 100; - coordinator.addUpstreamNameToLookupTable(ifIndex, iface); - assertEquals(0, coordinator.getInterfaceNamesForTesting().size()); - - // The rule can't be added. - final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1"); - final MacAddress mac = MacAddress.fromString("00:00:00:00:00:0a"); - final Ipv6ForwardingRule rule = buildTestForwardingRule(ifIndex, neigh, mac); - coordinator.tetherOffloadRuleAdd(mIpServer, rule); - verify(mNetd, never()).tetherOffloadRuleAdd(any()); - LinkedHashMap rules = - coordinator.getForwardingRulesForTesting().get(mIpServer); - assertNull(rules); - - // The rule can't be removed. This is not a realistic case because adding rule is not - // allowed. That implies no rule could be removed, cleared or updated. Verify these - // cases just in case. - rules = new LinkedHashMap(); - rules.put(rule.address, rule); - coordinator.getForwardingRulesForTesting().put(mIpServer, rules); - coordinator.tetherOffloadRuleRemove(mIpServer, rule); - verify(mNetd, never()).tetherOffloadRuleRemove(any()); - rules = coordinator.getForwardingRulesForTesting().get(mIpServer); - assertNotNull(rules); - assertEquals(1, rules.size()); - - // The rule can't be cleared. - coordinator.tetherOffloadRuleClear(mIpServer); - verify(mNetd, never()).tetherOffloadRuleRemove(any()); - rules = coordinator.getForwardingRulesForTesting().get(mIpServer); - assertNotNull(rules); - assertEquals(1, rules.size()); - - // The rule can't be updated. - coordinator.tetherOffloadRuleUpdate(mIpServer, rule.upstreamIfindex + 1 /* new */); - verify(mNetd, never()).tetherOffloadRuleRemove(any()); - verify(mNetd, never()).tetherOffloadRuleAdd(any()); - rules = coordinator.getForwardingRulesForTesting().get(mIpServer); - assertNotNull(rules); - assertEquals(1, rules.size()); - } - - @Test - public void testTetheringConfigSetPollingInterval() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - - // [1] The default polling interval. - coordinator.startPolling(); - assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval()); - coordinator.stopPolling(); - - // [2] Expect the invalid polling interval isn't applied. The valid range of interval is - // DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long. - for (final int interval - : new int[] {0, 100, DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS - 1}) { - when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval); - coordinator.startPolling(); - assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval()); - coordinator.stopPolling(); - } - - // [3] Set a specific polling interval which is larger than default value. - // Use a large polling interval to avoid flaky test because the time forwarding - // approximation is used to verify the scheduled time of the polling thread. - final int pollingInterval = 100_000; - when(mTetherConfig.getOffloadPollInterval()).thenReturn(pollingInterval); - coordinator.startPolling(); - - // Expect the specific polling interval to be applied. - assertEquals(pollingInterval, coordinator.getPollingInterval()); - - // Start on a new polling time slot. - mTestLooper.moveTimeForward(pollingInterval); - waitForIdle(); - clearInvocations(mNetd); - - // Move time forward to 90% polling interval time. Expect that the polling thread has not - // scheduled yet. - mTestLooper.moveTimeForward((long) (pollingInterval * 0.9)); - waitForIdle(); - verify(mNetd, never()).tetherOffloadGetStats(); - - // Move time forward to the remaining 10% polling interval time. Expect that the polling - // thread has scheduled. - mTestLooper.moveTimeForward((long) (pollingInterval * 0.1)); - waitForIdle(); - verify(mNetd).tetherOffloadGetStats(); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt deleted file mode 100644 index d915354b0c37..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering - -import android.net.LinkAddress -import android.net.MacAddress -import android.net.TetheredClient -import android.net.TetheredClient.AddressInfo -import android.net.TetheringManager.TETHERING_USB -import android.net.TetheringManager.TETHERING_WIFI -import android.net.ip.IpServer -import android.net.wifi.WifiClient -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -@RunWith(AndroidJUnit4::class) -@SmallTest -class ConnectedClientsTrackerTest { - - private val server1 = mock(IpServer::class.java) - private val server2 = mock(IpServer::class.java) - private val servers = listOf(server1, server2) - - private val clock = TestClock(1324L) - - private val client1Addr = MacAddress.fromString("01:23:45:67:89:0A") - private val client1 = TetheredClient(client1Addr, listOf( - makeAddrInfo("192.168.43.44/32", null /* hostname */, clock.time + 20)), - TETHERING_WIFI) - private val wifiClient1 = makeWifiClient(client1Addr) - private val client2Addr = MacAddress.fromString("02:34:56:78:90:AB") - private val client2Exp30AddrInfo = makeAddrInfo( - "192.168.43.45/32", "my_hostname", clock.time + 30) - private val client2 = TetheredClient(client2Addr, listOf( - client2Exp30AddrInfo, - makeAddrInfo("2001:db8:12::34/72", "other_hostname", clock.time + 10)), - TETHERING_WIFI) - private val wifiClient2 = makeWifiClient(client2Addr) - private val client3Addr = MacAddress.fromString("03:45:67:89:0A:BC") - private val client3 = TetheredClient(client3Addr, - listOf(makeAddrInfo("2001:db8:34::34/72", "other_other_hostname", clock.time + 10)), - TETHERING_USB) - - private fun makeAddrInfo(addr: String, hostname: String?, expTime: Long) = - LinkAddress(addr).let { - AddressInfo(LinkAddress(it.address, it.prefixLength, it.flags, it.scope, - expTime /* deprecationTime */, expTime /* expirationTime */), hostname) - } - - @Test - fun testUpdateConnectedClients() { - doReturn(emptyList()).`when`(server1).allLeases - doReturn(emptyList()).`when`(server2).allLeases - - val tracker = ConnectedClientsTracker(clock) - assertFalse(tracker.updateConnectedClients(servers, null)) - - // Obtain a lease for client 1 - doReturn(listOf(client1)).`when`(server1).allLeases - assertSameClients(listOf(client1), assertNewClients(tracker, servers, listOf(wifiClient1))) - - // Client 2 L2-connected, no lease yet - val client2WithoutAddr = TetheredClient(client2Addr, emptyList(), TETHERING_WIFI) - assertSameClients(listOf(client1, client2WithoutAddr), - assertNewClients(tracker, servers, listOf(wifiClient1, wifiClient2))) - - // Client 2 lease obtained - doReturn(listOf(client1, client2)).`when`(server1).allLeases - assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers, null)) - - // Client 3 lease obtained - doReturn(listOf(client3)).`when`(server2).allLeases - assertSameClients(listOf(client1, client2, client3), - assertNewClients(tracker, servers, null)) - - // Client 2 L2-disconnected - assertSameClients(listOf(client1, client3), - assertNewClients(tracker, servers, listOf(wifiClient1))) - - // Client 1 L2-disconnected - assertSameClients(listOf(client3), assertNewClients(tracker, servers, emptyList())) - - // Client 1 comes back - assertSameClients(listOf(client1, client3), - assertNewClients(tracker, servers, listOf(wifiClient1))) - - // Leases lost, client 1 still L2-connected - doReturn(emptyList()).`when`(server1).allLeases - doReturn(emptyList()).`when`(server2).allLeases - assertSameClients(listOf(TetheredClient(client1Addr, emptyList(), TETHERING_WIFI)), - assertNewClients(tracker, servers, null)) - } - - @Test - fun testUpdateConnectedClients_LeaseExpiration() { - val tracker = ConnectedClientsTracker(clock) - doReturn(listOf(client1, client2)).`when`(server1).allLeases - doReturn(listOf(client3)).`when`(server2).allLeases - assertSameClients(listOf(client1, client2, client3), assertNewClients( - tracker, servers, listOf(wifiClient1, wifiClient2))) - - clock.time += 20 - // Client 3 has no remaining lease: removed - val expectedClients = listOf( - // Client 1 has no remaining lease but is L2-connected - TetheredClient(client1Addr, emptyList(), TETHERING_WIFI), - // Client 2 has some expired leases - TetheredClient( - client2Addr, - // Only the "t + 30" address is left, the "t + 10" address expired - listOf(client2Exp30AddrInfo), - TETHERING_WIFI)) - assertSameClients(expectedClients, assertNewClients(tracker, servers, null)) - } - - private fun assertNewClients( - tracker: ConnectedClientsTracker, - ipServers: Iterable, - wifiClients: List? - ): List { - assertTrue(tracker.updateConnectedClients(ipServers, wifiClients)) - return tracker.lastTetheredClients - } - - private fun assertSameClients(expected: List, actual: List) { - val expectedSet = HashSet(expected) - assertEquals(expected.size, expectedSet.size) - assertEquals(expectedSet, HashSet(actual)) - } - - private fun makeWifiClient(macAddr: MacAddress): WifiClient { - // Use a mock WifiClient as the constructor is not part of the WiFi module exported API. - return mock(WifiClient::class.java).apply { doReturn(macAddr).`when`(this).macAddress } - } - - private class TestClock(var time: Long) : ConnectedClientsTracker.Clock() { - override fun elapsedRealtime(): Long { - return time - } - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java deleted file mode 100644 index 354e75356e9f..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ /dev/null @@ -1,623 +0,0 @@ -/* - * 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.networkstack.tethering; - -import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; -import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; -import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; -import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; -import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; -import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; -import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_INVALID; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED; -import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; -import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.net.util.SharedLog; -import android.os.Bundle; -import android.os.Handler; -import android.os.PersistableBundle; -import android.os.ResultReceiver; -import android.os.SystemProperties; -import android.os.test.TestLooper; -import android.provider.DeviceConfig; -import android.provider.Settings; -import android.telephony.CarrierConfigManager; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.test.BroadcastInterceptingContext; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public final class EntitlementManagerTest { - - private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; - private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - private static final String PROVISIONING_APP_RESPONSE = "app_response"; - - @Mock private CarrierConfigManager mCarrierConfigManager; - @Mock private Context mContext; - @Mock private Resources mResources; - @Mock private SharedLog mLog; - @Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener; - - // Like so many Android system APIs, these cannot be mocked because it is marked final. - // We have to use the real versions. - private final PersistableBundle mCarrierConfig = new PersistableBundle(); - private final TestLooper mLooper = new TestLooper(); - private Context mMockContext; - private Runnable mPermissionChangeCallback; - - private WrappedEntitlementManager mEnMgr; - private TetheringConfiguration mConfig; - private MockitoSession mMockingSession; - - private class MockContext extends BroadcastInterceptingContext { - MockContext(Context base) { - super(base); - } - - @Override - public Resources getResources() { - return mResources; - } - } - - public class WrappedEntitlementManager extends EntitlementManager { - public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN; - public int uiProvisionCount = 0; - public int silentProvisionCount = 0; - - public WrappedEntitlementManager(Context ctx, Handler h, SharedLog log, - Runnable callback) { - super(ctx, h, log, callback); - } - - public void reset() { - fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN; - uiProvisionCount = 0; - silentProvisionCount = 0; - } - - @Override - protected Intent runUiTetherProvisioning(int type, - final TetheringConfiguration config, final ResultReceiver receiver) { - Intent intent = super.runUiTetherProvisioning(type, config, receiver); - assertUiTetherProvisioningIntent(type, config, receiver, intent); - uiProvisionCount++; - receiver.send(fakeEntitlementResult, null); - return intent; - } - - private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config, - final ResultReceiver receiver, final Intent intent) { - assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction()); - assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); - final String[] appName = intent.getStringArrayExtra( - EXTRA_TETHER_UI_PROVISIONING_APP_NAME); - assertEquals(PROVISIONING_APP_NAME.length, appName.length); - for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) { - assertEquals(PROVISIONING_APP_NAME[i], appName[i]); - } - assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK)); - assertEquals(config.activeDataSubId, - intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); - } - - @Override - protected Intent runSilentTetherProvisioning(int type, - final TetheringConfiguration config) { - Intent intent = super.runSilentTetherProvisioning(type, config); - assertSilentTetherProvisioning(type, config, intent); - silentProvisionCount++; - addDownstreamMapping(type, fakeEntitlementResult); - return intent; - } - - private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config, - final Intent intent) { - assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); - assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false)); - assertEquals(PROVISIONING_NO_UI_APP_NAME, - intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION)); - assertEquals(PROVISIONING_APP_RESPONSE, - intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE)); - assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK)); - assertEquals(config.activeDataSubId, - intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); - } - } - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mMockingSession = mockitoSession() - .initMocks(this) - .mockStatic(SystemProperties.class) - .mockStatic(DeviceConfig.class) - .strictness(Strictness.WARN) - .startMocking(); - // Don't disable tethering provisioning unless requested. - doReturn(false).when( - () -> SystemProperties.getBoolean( - eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean())); - doReturn(null).when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), anyString())); - - when(mResources.getStringArray(R.array.config_tether_dhcp_range)) - .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_usb_regexs)) - .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) - .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) - .thenReturn(new String[0]); - when(mResources.getIntArray(R.array.config_tether_upstream_types)) - .thenReturn(new int[0]); - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - false); - when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); - when(mLog.forSubComponent(anyString())).thenReturn(mLog); - - mMockContext = new MockContext(mContext); - mPermissionChangeCallback = spy(() -> { }); - mEnMgr = new WrappedEntitlementManager(mMockContext, new Handler(mLooper.getLooper()), mLog, - mPermissionChangeCallback); - mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - mEnMgr.setTetheringConfigurationFetcher(() -> { - return mConfig; - }); - } - - @After - public void tearDown() throws Exception { - mMockingSession.finishMocking(); - } - - private void setupForRequiredProvisioning() { - // Produce some acceptable looking provision app setting if requested. - when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) - .thenReturn(PROVISIONING_APP_NAME); - when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) - .thenReturn(PROVISIONING_NO_UI_APP_NAME); - when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn( - PROVISIONING_APP_RESPONSE); - // Act like the CarrierConfigManager is present and ready unless told otherwise. - when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) - .thenReturn(mCarrierConfigManager); - when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - } - - @Test - public void canRequireProvisioning() { - setupForRequiredProvisioning(); - assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); - } - - @Test - public void toleratesCarrierConfigManagerMissing() { - setupForRequiredProvisioning(); - when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) - .thenReturn(null); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - // Couldn't get the CarrierConfigManager, but still had a declared provisioning app. - // Therefore provisioning still be required. - assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); - } - - @Test - public void toleratesCarrierConfigMissing() { - setupForRequiredProvisioning(); - when(mCarrierConfigManager.getConfig()).thenReturn(null); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - // We still have a provisioning app configured, so still require provisioning. - assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); - } - - @Test - public void toleratesCarrierConfigNotLoaded() { - setupForRequiredProvisioning(); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, false); - // We still have a provisioning app configured, so still require provisioning. - assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); - } - - @Test - public void provisioningNotRequiredWhenAppNotFound() { - setupForRequiredProvisioning(); - when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) - .thenReturn(null); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig)); - when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) - .thenReturn(new String[] {"malformedApp"}); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig)); - } - - @Test - public void testRequestLastEntitlementCacheValue() throws Exception { - // 1. Entitlement check is not required. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - ResultReceiver receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - - setupForRequiredProvisioning(); - // 2. No cache value and don't need to run entitlement check. - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 3. No cache value and ui entitlement check is needed. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement - // check. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 6. Cache value is TETHER_ERROR_NO_ERROR. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 7. Test get value for other downstream type. - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 8. Test get value for invalid downstream type. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - } - - private void assertPermissionChangeCallback(InOrder inOrder) { - inOrder.verify(mPermissionChangeCallback, times(1)).run(); - } - - private void assertNoPermissionChange(InOrder inOrder) { - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void verifyPermissionResult() { - final InOrder inOrder = inOrder(mPermissionChangeCallback); - setupForRequiredProvisioning(); - mEnMgr.notifyUpstream(true); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); - mLooper.dispatchAll(); - // Permitted: true -> false - assertPermissionChangeCallback(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); - mLooper.dispatchAll(); - // Permitted: false -> false - assertNoPermissionChange(inOrder); - - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); - mLooper.dispatchAll(); - // Permitted: false -> true - assertPermissionChangeCallback(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - } - - @Test - public void verifyPermissionIfAllNotApproved() { - final InOrder inOrder = inOrder(mPermissionChangeCallback); - setupForRequiredProvisioning(); - mEnMgr.notifyUpstream(true); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); - mLooper.dispatchAll(); - // Permitted: true -> false - assertPermissionChangeCallback(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); - mLooper.dispatchAll(); - // Permitted: false -> false - assertNoPermissionChange(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true); - mLooper.dispatchAll(); - // Permitted: false -> false - assertNoPermissionChange(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - } - - @Test - public void verifyPermissionIfAnyApproved() { - final InOrder inOrder = inOrder(mPermissionChangeCallback); - setupForRequiredProvisioning(); - mEnMgr.notifyUpstream(true); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); - mLooper.dispatchAll(); - // Permitted: true -> true - assertNoPermissionChange(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); - mLooper.dispatchAll(); - // Permitted: true -> true - assertNoPermissionChange(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); - mLooper.dispatchAll(); - // Permitted: true -> false - assertPermissionChangeCallback(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - } - - @Test - public void verifyPermissionWhenProvisioningNotStarted() { - final InOrder inOrder = inOrder(mPermissionChangeCallback); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - assertNoPermissionChange(inOrder); - setupForRequiredProvisioning(); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - assertNoPermissionChange(inOrder); - } - - @Test - public void testRunTetherProvisioning() { - final InOrder inOrder = inOrder(mPermissionChangeCallback); - setupForRequiredProvisioning(); - // 1. start ui provisioning, upstream is mobile - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.notifyUpstream(true); - mLooper.dispatchAll(); - mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - // Permitted: true -> true - assertNoPermissionChange(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.reset(); - - // 2. start no-ui provisioning - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, false); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - assertEquals(1, mEnMgr.silentProvisionCount); - // Permitted: true -> true - assertNoPermissionChange(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.reset(); - - // 3. tear down mobile, then start ui provisioning - mEnMgr.notifyUpstream(false); - mLooper.dispatchAll(); - mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - assertNoPermissionChange(inOrder); - mEnMgr.reset(); - - // 4. switch upstream back to mobile - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.notifyUpstream(true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - // Permitted: true -> true - assertNoPermissionChange(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.reset(); - - // 5. tear down mobile, then switch SIM - mEnMgr.notifyUpstream(false); - mLooper.dispatchAll(); - mEnMgr.reevaluateSimCardProvisioning(mConfig); - assertEquals(0, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - assertNoPermissionChange(inOrder); - mEnMgr.reset(); - - // 6. switch upstream back to mobile again - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.notifyUpstream(true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - assertEquals(3, mEnMgr.silentProvisionCount); - // Permitted: true -> false - assertPermissionChangeCallback(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.reset(); - - // 7. start ui provisioning, upstream is mobile, downstream is ethernet - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.startProvisioningIfNeeded(TETHERING_ETHERNET, true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - // Permitted: false -> true - assertPermissionChangeCallback(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.reset(); - - // 8. downstream is invalid - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI_P2P, true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - assertNoPermissionChange(inOrder); - mEnMgr.reset(); - } - - @Test - public void testCallStopTetheringWhenUiProvisioningFail() { - setupForRequiredProvisioning(); - verify(mEntitlementFailedListener, times(0)).onUiEntitlementFailed(TETHERING_WIFI); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.notifyUpstream(true); - mLooper.dispatchAll(); - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - verify(mEntitlementFailedListener, times(1)).onUiEntitlementFailed(TETHERING_WIFI); - } - - @Test - public void testsetExemptedDownstreamType() throws Exception { - setupForRequiredProvisioning(); - // Cellular upstream is not permitted when no entitlement result. - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - - // If there is exempted downstream and no other non-exempted downstreams, cellular is - // permitted. - mEnMgr.setExemptedDownstreamType(TETHERING_WIFI); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - - // If second downstream run entitlement check fail, cellular upstream is not permitted. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.notifyUpstream(true); - mLooper.dispatchAll(); - mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); - mLooper.dispatchAll(); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - - // When second downstream is down, exempted downstream can use cellular upstream. - assertEquals(1, mEnMgr.uiProvisionCount); - verify(mEntitlementFailedListener).onUiEntitlementFailed(TETHERING_USB); - mEnMgr.stopProvisioningIfNeeded(TETHERING_USB); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java deleted file mode 100644 index f2b5314e5a17..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.ip.IpServer.STATE_LOCAL_ONLY; -import static android.net.ip.IpServer.STATE_TETHERED; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.net.InetAddresses; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.RouteInfo; -import android.net.ip.IpServer; -import android.net.util.SharedLog; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class IPv6TetheringCoordinatorTest { - private static final String TEST_DNS_SERVER = "2001:4860:4860::8888"; - private static final String TEST_INTERFACE = "test_rmnet0"; - private static final String TEST_IPV6_ADDRESS = "2001:db8::1/64"; - private static final String TEST_IPV4_ADDRESS = "192.168.100.1/24"; - - private IPv6TetheringCoordinator mIPv6TetheringCoordinator; - private ArrayList mNotifyList; - - @Mock private SharedLog mSharedLog; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); - mNotifyList = new ArrayList(); - mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mSharedLog); - } - - private UpstreamNetworkState createDualStackUpstream(final int transportType) { - final Network network = mock(Network.class); - final NetworkCapabilities netCap = - new NetworkCapabilities.Builder().addTransportType(transportType).build(); - final InetAddress dns = InetAddresses.parseNumericAddress(TEST_DNS_SERVER); - final LinkProperties linkProp = new LinkProperties(); - linkProp.setInterfaceName(TEST_INTERFACE); - linkProp.addLinkAddress(new LinkAddress(TEST_IPV6_ADDRESS)); - linkProp.addLinkAddress(new LinkAddress(TEST_IPV4_ADDRESS)); - linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, TEST_INTERFACE, RTN_UNICAST)); - linkProp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, TEST_INTERFACE, - RTN_UNICAST)); - linkProp.addDnsServer(dns); - return new UpstreamNetworkState(linkProp, netCap, network); - } - - private void assertOnlyOneV6AddressAndNoV4(LinkProperties lp) { - assertEquals(lp.getInterfaceName(), TEST_INTERFACE); - assertFalse(lp.hasIpv4Address()); - final List addresses = lp.getLinkAddresses(); - assertEquals(addresses.size(), 1); - final LinkAddress v6Address = addresses.get(0); - assertEquals(v6Address, new LinkAddress(TEST_IPV6_ADDRESS)); - } - - @Test - public void testUpdateIpv6Upstream() throws Exception { - // 1. Add first IpServer. - final IpServer firstServer = mock(IpServer.class); - mNotifyList.add(firstServer); - mIPv6TetheringCoordinator.addActiveDownstream(firstServer, STATE_TETHERED); - verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - verifyNoMoreInteractions(firstServer); - - // 2. Add second IpServer and it would not have ipv6 tethering. - final IpServer secondServer = mock(IpServer.class); - mNotifyList.add(secondServer); - mIPv6TetheringCoordinator.addActiveDownstream(secondServer, STATE_LOCAL_ONLY); - verifyNoMoreInteractions(secondServer); - reset(firstServer, secondServer); - - // 3. No upstream. - mIPv6TetheringCoordinator.updateUpstreamNetworkState(null); - verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - reset(firstServer, secondServer); - - // 4. Update ipv6 mobile upstream. - final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR); - final ArgumentCaptor lp = ArgumentCaptor.forClass(LinkProperties.class); - mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream); - verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0), - lp.capture()); - final LinkProperties v6OnlyLink = lp.getValue(); - assertOnlyOneV6AddressAndNoV4(v6OnlyLink); - verifyNoMoreInteractions(firstServer); - verifyNoMoreInteractions(secondServer); - reset(firstServer, secondServer); - - // 5. Remove first IpServer. - mNotifyList.remove(firstServer); - mIPv6TetheringCoordinator.removeActiveDownstream(firstServer); - verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0), - lp.capture()); - final LinkProperties localOnlyLink = lp.getValue(); - assertNotNull(localOnlyLink); - assertNotEquals(localOnlyLink, v6OnlyLink); - reset(firstServer, secondServer); - - // 6. Remove second IpServer. - mNotifyList.remove(secondServer); - mIPv6TetheringCoordinator.removeActiveDownstream(secondServer); - verifyNoMoreInteractions(firstServer); - verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java deleted file mode 100644 index 071a290e657b..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.networkstack.tethering; - -import static android.Manifest.permission.WRITE_SETTINGS; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - -import static org.mockito.Mockito.mock; - -import android.content.Context; -import android.content.Intent; -import android.net.ITetheringConnector; -import android.os.Binder; -import android.os.IBinder; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public class MockTetheringService extends TetheringService { - private final Tethering mTethering = mock(Tethering.class); - - @Override - public IBinder onBind(Intent intent) { - return new MockTetheringConnector(super.onBind(intent)); - } - - @Override - public Tethering makeTethering(TetheringDependencies deps) { - return mTethering; - } - - @Override - boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid, - @NonNull String callingPackage, @Nullable String callingAttributionTag, - boolean throwException) { - // Test this does not verify the calling package / UID, as calling package could be shell - // and not match the UID. - return context.checkCallingOrSelfPermission(WRITE_SETTINGS) == PERMISSION_GRANTED; - } - - public Tethering getTethering() { - return mTethering; - } - - public class MockTetheringConnector extends Binder { - final IBinder mBase; - MockTetheringConnector(IBinder base) { - mBase = base; - } - - public ITetheringConnector getTetheringConnector() { - return ITetheringConnector.Stub.asInterface(mBase); - } - - public MockTetheringService getService() { - return MockTetheringService.this; - } - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java deleted file mode 100644 index ce52ae22ece8..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java +++ /dev/null @@ -1,827 +0,0 @@ -/* - * Copyright (C) 2017 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.networkstack.tethering; - -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStats.UID_TETHERING; -import static android.net.RouteInfo.RTN_UNICAST; -import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; - -import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE; -import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID; -import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; -import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; -import static com.android.testutils.MiscAsserts.assertContainsAll; -import static com.android.testutils.MiscAsserts.assertThrows; -import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals; - -import static junit.framework.Assert.assertNotNull; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.app.usage.NetworkStatsManager; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.net.ITetheringStatsProvider; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.NetworkStats; -import android.net.NetworkStats.Entry; -import android.net.RouteInfo; -import android.net.netstats.provider.NetworkStatsProvider; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.test.TestLooper; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; -import android.test.mock.MockContentResolver; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.test.FakeSettingsProvider; -import com.android.testutils.TestableNetworkStatsProviderCbBinder; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class OffloadControllerTest { - private static final String RNDIS0 = "test_rndis0"; - private static final String RMNET0 = "test_rmnet_data0"; - private static final String WLAN0 = "test_wlan0"; - - private static final String IPV6_LINKLOCAL = "fe80::/64"; - private static final String IPV6_DOC_PREFIX = "2001:db8::/64"; - private static final String IPV6_DISCARD_PREFIX = "100::/64"; - private static final String USB_PREFIX = "192.168.42.0/24"; - private static final String WIFI_PREFIX = "192.168.43.0/24"; - private static final long WAIT_FOR_IDLE_TIMEOUT = 2 * 1000; - - @Mock private OffloadHardwareInterface mHardware; - @Mock private ApplicationInfo mApplicationInfo; - @Mock private Context mContext; - @Mock private NetworkStatsManager mStatsManager; - @Mock private TetheringConfiguration mTetherConfig; - // Late init since methods must be called by the thread that created this object. - private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; - private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider; - private final ArgumentCaptor mStringArrayCaptor = - ArgumentCaptor.forClass(ArrayList.class); - private final ArgumentCaptor mControlCallbackCaptor = - ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); - private MockContentResolver mContentResolver; - private final TestLooper mTestLooper = new TestLooper(); - private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() { - @Override - public TetheringConfiguration getTetherConfig() { - return mTetherConfig; - } - }; - - @Before public void setUp() { - MockitoAnnotations.initMocks(this); - when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); - when(mContext.getPackageName()).thenReturn("OffloadControllerTest"); - mContentResolver = new MockContentResolver(mContext); - mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); - when(mContext.getContentResolver()).thenReturn(mContentResolver); - FakeSettingsProvider.clearSettingsProvider(); - when(mTetherConfig.getOffloadPollInterval()).thenReturn(-1); // Disabled. - } - - @After public void tearDown() throws Exception { - FakeSettingsProvider.clearSettingsProvider(); - } - - private void setupFunctioningHardwareInterface() { - when(mHardware.initOffloadConfig()).thenReturn(true); - when(mHardware.initOffloadControl(mControlCallbackCaptor.capture())) - .thenReturn(true); - when(mHardware.setUpstreamParameters(anyString(), any(), any(), any())).thenReturn(true); - when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats()); - when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); - } - - private void enableOffload() { - Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0); - } - - private void setOffloadPollInterval(int interval) { - when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval); - } - - private void waitForIdle() { - mTestLooper.dispatchAll(); - } - - private OffloadController makeOffloadController() throws Exception { - OffloadController offload = new OffloadController(new Handler(mTestLooper.getLooper()), - mHardware, mContentResolver, mStatsManager, new SharedLog("test"), mDeps); - final ArgumentCaptor - tetherStatsProviderCaptor = - ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class); - verify(mStatsManager).registerNetworkStatsProvider(anyString(), - tetherStatsProviderCaptor.capture()); - mTetherStatsProvider = tetherStatsProviderCaptor.getValue(); - assertNotNull(mTetherStatsProvider); - mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder(); - mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb); - return offload; - } - - @Test - public void testNoSettingsValueDefaultDisabledDoesNotStart() throws Exception { - setupFunctioningHardwareInterface(); - when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(1); - assertThrows(SettingNotFoundException.class, () -> - Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED)); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); - inOrder.verify(mHardware, never()).initOffloadConfig(); - inOrder.verify(mHardware, never()).initOffloadControl( - any(OffloadHardwareInterface.ControlCallback.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testNoSettingsValueDefaultEnabledDoesStart() throws Exception { - setupFunctioningHardwareInterface(); - when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(0); - assertThrows(SettingNotFoundException.class, () -> - Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED)); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); - inOrder.verify(mHardware, times(1)).initOffloadConfig(); - inOrder.verify(mHardware, times(1)).initOffloadControl( - any(OffloadHardwareInterface.ControlCallback.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testSettingsAllowsStart() throws Exception { - setupFunctioningHardwareInterface(); - Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); - inOrder.verify(mHardware, times(1)).initOffloadConfig(); - inOrder.verify(mHardware, times(1)).initOffloadControl( - any(OffloadHardwareInterface.ControlCallback.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testSettingsDisablesStart() throws Exception { - setupFunctioningHardwareInterface(); - Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 1); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); - inOrder.verify(mHardware, never()).initOffloadConfig(); - inOrder.verify(mHardware, never()).initOffloadControl(anyObject()); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testSetUpstreamLinkPropertiesWorking() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); - inOrder.verify(mHardware, times(1)).initOffloadConfig(); - inOrder.verify(mHardware, times(1)).initOffloadControl( - any(OffloadHardwareInterface.ControlCallback.class)); - inOrder.verifyNoMoreInteractions(); - - // In reality, the UpstreamNetworkMonitor would have passed down to us - // a covering set of local prefixes representing a minimum essential - // set plus all the prefixes on networks with network agents. - // - // We simulate that there, and then add upstream elements one by one - // and watch what happens. - final Set minimumLocalPrefixes = new HashSet<>(); - for (String s : new String[]{ - "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) { - minimumLocalPrefixes.add(new IpPrefix(s)); - } - offload.setLocalPrefixes(minimumLocalPrefixes); - inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); - ArrayList localPrefixes = mStringArrayCaptor.getValue(); - assertEquals(4, localPrefixes.size()); - assertContainsAll(localPrefixes, - "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"); - inOrder.verifyNoMoreInteractions(); - - offload.setUpstreamLinkProperties(null); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - // This LinkProperties value does not differ from the default upstream. - // There should be no extraneous call to setUpstreamParameters(). - inOrder.verify(mHardware, never()).setUpstreamParameters( - anyObject(), anyObject(), anyObject(), anyObject()); - inOrder.verifyNoMoreInteractions(); - - final LinkProperties lp = new LinkProperties(); - - final String testIfName = "rmnet_data17"; - lp.setInterfaceName(testIfName); - offload.setUpstreamLinkProperties(lp); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(null), eq(null), eq(null)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - final String ipv4Addr = "192.0.2.5"; - final String linkAddr = ipv4Addr + "/24"; - lp.addLinkAddress(new LinkAddress(linkAddr)); - lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, null, RTN_UNICAST)); - offload.setUpstreamLinkProperties(lp); - // IPv4 prefixes and addresses on the upstream are simply left as whole - // prefixes (already passed in from UpstreamNetworkMonitor code). If a - // tethering client sends traffic to the IPv4 default router or other - // clients on the upstream this will not be hardware-forwarded, and that - // should be fine for now. Ergo: no change in local addresses, no call - // to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(null), eq(null)); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - final String ipv4Gateway = "192.0.2.1"; - lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv4Gateway), null, RTN_UNICAST)); - offload.setUpstreamLinkProperties(lp); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null)); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - final String ipv6Gw1 = "fe80::cafe"; - lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw1), null, RTN_UNICAST)); - offload.setUpstreamLinkProperties(lp); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - ArrayList v6gws = mStringArrayCaptor.getValue(); - assertEquals(1, v6gws.size()); - assertTrue(v6gws.contains(ipv6Gw1)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - final String ipv6Gw2 = "fe80::d00d"; - lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw2), null, RTN_UNICAST)); - offload.setUpstreamLinkProperties(lp); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - v6gws = mStringArrayCaptor.getValue(); - assertEquals(2, v6gws.size()); - assertTrue(v6gws.contains(ipv6Gw1)); - assertTrue(v6gws.contains(ipv6Gw2)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - final LinkProperties stacked = new LinkProperties(); - stacked.setInterfaceName("stacked"); - stacked.addLinkAddress(new LinkAddress("192.0.2.129/25")); - stacked.addRoute(new RouteInfo(null, InetAddress.getByName("192.0.2.254"), null, - RTN_UNICAST)); - stacked.addRoute(new RouteInfo(null, InetAddress.getByName("fe80::bad:f00"), null, - RTN_UNICAST)); - assertTrue(lp.addStackedLink(stacked)); - offload.setUpstreamLinkProperties(lp); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - v6gws = mStringArrayCaptor.getValue(); - assertEquals(2, v6gws.size()); - assertTrue(v6gws.contains(ipv6Gw1)); - assertTrue(v6gws.contains(ipv6Gw2)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - // Add in some IPv6 upstream info. When there is a tethered downstream - // making use of the IPv6 prefix we would expect to see the /64 route - // removed from "local prefixes" and /128s added for the upstream IPv6 - // addresses. This is not yet implemented, and for now we simply - // expect to see these /128s. - lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"), null, null, RTN_UNICAST)); - // "2001:db8::/64" plus "assigned" ASCII in hex - lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64")); - // "2001:db8::/64" plus "random" ASCII in hex - lp.addLinkAddress(new LinkAddress("2001:db8::7261:6e64:6f6d/64")); - offload.setUpstreamLinkProperties(lp); - inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); - localPrefixes = mStringArrayCaptor.getValue(); - assertEquals(6, localPrefixes.size()); - assertContainsAll(localPrefixes, - "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64", - "2001:db8::6173:7369:676e:6564/128", "2001:db8::7261:6e64:6f6d/128"); - // The relevant parts of the LinkProperties have not changed, but at the - // moment we do not de-dup upstream LinkProperties this carefully. - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); - v6gws = mStringArrayCaptor.getValue(); - assertEquals(2, v6gws.size()); - assertTrue(v6gws.contains(ipv6Gw1)); - assertTrue(v6gws.contains(ipv6Gw2)); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - // Completely identical LinkProperties updates are de-duped. - offload.setUpstreamLinkProperties(lp); - // This LinkProperties value does not differ from the default upstream. - // There should be no extraneous call to setUpstreamParameters(). - inOrder.verify(mHardware, never()).setUpstreamParameters( - anyObject(), anyObject(), anyObject(), anyObject()); - inOrder.verifyNoMoreInteractions(); - } - - private static @NonNull Entry buildTestEntry(@NonNull OffloadController.StatsType how, - @NonNull String iface, long rxBytes, long txBytes) { - return new Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, SET_DEFAULT, - TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, 0L, - txBytes, 0L, 0L); - } - - @Test - public void testGetForwardedStats() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final String ethernetIface = "eth1"; - final String mobileIface = "rmnet_data0"; - - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( - new ForwardedStats(12345, 54321)); - when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn( - new ForwardedStats(999, 99999)); - - InOrder inOrder = inOrder(mHardware); - - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(ethernetIface); - offload.setUpstreamLinkProperties(lp); - // Previous upstream was null, so no stats are fetched. - inOrder.verify(mHardware, never()).getForwardedStats(any()); - - lp.setInterfaceName(mobileIface); - offload.setUpstreamLinkProperties(lp); - // Expect that we fetch stats from the previous upstream. - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface)); - - lp.setInterfaceName(ethernetIface); - offload.setUpstreamLinkProperties(lp); - // Expect that we fetch stats from the previous upstream. - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface)); - - // Verify that the fetched stats are stored. - final NetworkStats ifaceStats = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE); - final NetworkStats uidStats = mTetherStatsProvider.getTetherStats(STATS_PER_UID); - final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) - .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321)); - - final NetworkStats expectedUidStats = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) - .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321)); - - assertNetworkStatsEquals(expectedIfaceStats, ifaceStats); - assertNetworkStatsEquals(expectedUidStats, uidStats); - - // Force pushing stats update to verify the stats reported. - mTetherStatsProvider.pushTetherStats(); - mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats); - - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( - new ForwardedStats(100000, 100000)); - offload.setUpstreamLinkProperties(null); - // Expect that we first clear the HAL's upstream parameters. - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(""), eq("0.0.0.0"), eq("0.0.0.0"), eq(null)); - // Expect that we fetch stats from the previous upstream. - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface)); - - // There is no current upstream, so no stats are fetched. - inOrder.verify(mHardware, never()).getForwardedStats(any()); - inOrder.verifyNoMoreInteractions(); - - // Verify that the stored stats is accumulated. - final NetworkStats ifaceStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE); - final NetworkStats uidStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_UID); - final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) - .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321)); - - final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) - .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321)); - - assertNetworkStatsEquals(expectedIfaceStatsAccu, ifaceStatsAccu); - assertNetworkStatsEquals(expectedUidStatsAccu, uidStatsAccu); - - // Verify that only diff of stats is reported. - mTetherStatsProvider.pushTetherStats(); - final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0)) - .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000)); - - final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0)) - .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000)); - mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff, - expectedUidStatsDiff); - } - - @Test - public void testSetInterfaceQuota() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final String ethernetIface = "eth1"; - final String mobileIface = "rmnet_data0"; - final long ethernetLimit = 12345; - final long mobileLimit = 12345678; - - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(ethernetIface); - offload.setUpstreamLinkProperties(lp); - - final InOrder inOrder = inOrder(mHardware); - when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true); - when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); - - // Applying an interface quota to the current upstream immediately sends it to the hardware. - mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit); - waitForIdle(); - inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit); - inOrder.verifyNoMoreInteractions(); - - // Applying an interface quota to another upstream does not take any immediate action. - mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); - waitForIdle(); - inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); - - // Switching to that upstream causes the quota to be applied if the parameters were applied - // correctly. - lp.setInterfaceName(mobileIface); - offload.setUpstreamLinkProperties(lp); - waitForIdle(); - inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit); - - // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set - // to Long.MAX_VALUE. - mTetherStatsProvider.onSetLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED); - waitForIdle(); - inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE); - - // If setting upstream parameters fails, then the data limit is not set. - when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false); - lp.setInterfaceName(ethernetIface); - offload.setUpstreamLinkProperties(lp); - mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); - waitForIdle(); - inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); - - // If setting the data limit fails while changing upstreams, offload is stopped. - when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true); - when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false); - lp.setInterfaceName(mobileIface); - offload.setUpstreamLinkProperties(lp); - mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); - waitForIdle(); - inOrder.verify(mHardware).getForwardedStats(ethernetIface); - inOrder.verify(mHardware).stopOffloadControl(); - } - - @Test - public void testDataLimitCallback() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); - callback.onStoppedLimitReached(); - mTetherStatsProviderCb.expectNotifyStatsUpdated(); - } - - @Test - public void testAddRemoveDownstreams() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).initOffloadConfig(); - inOrder.verify(mHardware, times(1)).initOffloadControl( - any(OffloadHardwareInterface.ControlCallback.class)); - inOrder.verifyNoMoreInteractions(); - - // Tethering makes several calls to setLocalPrefixes() before add/remove - // downstream calls are made. This is not tested here; only the behavior - // of notifyDownstreamLinkProperties() and removeDownstreamInterface() - // are tested. - - // [1] USB tethering is started. - final LinkProperties usbLinkProperties = new LinkProperties(); - usbLinkProperties.setInterfaceName(RNDIS0); - usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24")); - usbLinkProperties.addRoute( - new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, USB_PREFIX); - inOrder.verifyNoMoreInteractions(); - - // [2] Routes for IPv6 link-local prefixes should never be added. - usbLinkProperties.addRoute( - new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString()); - inOrder.verifyNoMoreInteractions(); - - // [3] Add an IPv6 prefix for good measure. Only new offload-able - // prefixes should be passed to the HAL. - usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64")); - usbLinkProperties.addRoute( - new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX); - inOrder.verifyNoMoreInteractions(); - - // [4] Adding addresses doesn't affect notifyDownstreamLinkProperties(). - // The address is passed in by a separate setLocalPrefixes() invocation. - usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::2/64")); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString()); - - // [5] Differences in local routes are converted into addDownstream() - // and removeDownstream() invocations accordingly. - usbLinkProperties.removeRoute( - new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, RNDIS0, RTN_UNICAST)); - usbLinkProperties.addRoute( - new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX); - inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX); - inOrder.verifyNoMoreInteractions(); - - // [6] Removing a downstream interface which was never added causes no - // interactions with the HAL. - offload.removeDownstreamInterface(WLAN0); - inOrder.verifyNoMoreInteractions(); - - // [7] Removing an active downstream removes all remaining prefixes. - offload.removeDownstreamInterface(RNDIS0); - inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, USB_PREFIX); - inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testControlCallbackOnStoppedUnsupportedFetchesAllStats() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - // Pretend to set a few different upstreams (only the interface name - // matters for this test; we're ignoring IP and route information). - final LinkProperties upstreamLp = new LinkProperties(); - for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) { - upstreamLp.setInterfaceName(ifname); - offload.setUpstreamLinkProperties(upstreamLp); - } - - // Clear invocation history, especially the getForwardedStats() calls - // that happen with setUpstreamParameters(). - clearInvocations(mHardware); - - OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); - callback.onStoppedUnsupported(); - - // Verify forwarded stats behaviour. - verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); - verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); - // TODO: verify the exact stats reported. - mTetherStatsProviderCb.expectNotifyStatsUpdated(); - mTetherStatsProviderCb.assertNoCallback(); - verifyNoMoreInteractions(mHardware); - } - - @Test - public void testControlCallbackOnSupportAvailableFetchesAllStatsAndPushesAllParameters() - throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - // Pretend to set a few different upstreams (only the interface name - // matters for this test; we're ignoring IP and route information). - final LinkProperties upstreamLp = new LinkProperties(); - for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) { - upstreamLp.setInterfaceName(ifname); - offload.setUpstreamLinkProperties(upstreamLp); - } - - // Pretend that some local prefixes and downstreams have been added - // (and removed, for good measure). - final Set minimumLocalPrefixes = new HashSet<>(); - for (String s : new String[]{ - "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) { - minimumLocalPrefixes.add(new IpPrefix(s)); - } - offload.setLocalPrefixes(minimumLocalPrefixes); - - final LinkProperties usbLinkProperties = new LinkProperties(); - usbLinkProperties.setInterfaceName(RNDIS0); - usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24")); - usbLinkProperties.addRoute( - new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - - final LinkProperties wifiLinkProperties = new LinkProperties(); - wifiLinkProperties.setInterfaceName(WLAN0); - wifiLinkProperties.addLinkAddress(new LinkAddress("192.168.43.1/24")); - wifiLinkProperties.addRoute( - new RouteInfo(new IpPrefix(WIFI_PREFIX), null, null, RTN_UNICAST)); - wifiLinkProperties.addRoute( - new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST)); - // Use a benchmark prefix (RFC 5180 + erratum), since the documentation - // prefix is included in the excluded prefix list. - wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::1/64")); - wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::2/64")); - wifiLinkProperties.addRoute( - new RouteInfo(new IpPrefix("2001:2::/64"), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(wifiLinkProperties); - - offload.removeDownstreamInterface(RNDIS0); - - // Clear invocation history, especially the getForwardedStats() calls - // that happen with setUpstreamParameters(). - clearInvocations(mHardware); - - OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); - callback.onSupportAvailable(); - - // Verify forwarded stats behaviour. - verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); - verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); - mTetherStatsProviderCb.expectNotifyStatsUpdated(); - mTetherStatsProviderCb.assertNoCallback(); - - // TODO: verify local prefixes and downstreams are also pushed to the HAL. - verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); - ArrayList localPrefixes = mStringArrayCaptor.getValue(); - assertEquals(4, localPrefixes.size()); - assertContainsAll(localPrefixes, - // TODO: The logic to find and exclude downstream IP prefixes - // is currently in Tethering's OffloadWrapper but must be moved - // into OffloadController proper. After this, also check for: - // "192.168.43.1/32", "2001:2::1/128", "2001:2::2/128" - "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"); - verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "192.168.43.0/24"); - verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "2001:2::/64"); - verify(mHardware, times(1)).setUpstreamParameters(eq(RMNET0), any(), any(), any()); - verify(mHardware, times(1)).setDataLimit(eq(RMNET0), anyLong()); - verifyNoMoreInteractions(mHardware); - } - - @Test - public void testOnSetAlert() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - final OffloadController offload = makeOffloadController(); - offload.start(); - - // Initialize with fake eth upstream. - final String ethernetIface = "eth1"; - InOrder inOrder = inOrder(mHardware); - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(ethernetIface); - offload.setUpstreamLinkProperties(lp); - // Previous upstream was null, so no stats are fetched. - inOrder.verify(mHardware, never()).getForwardedStats(any()); - - // Verify that set quota to 0 will immediately triggers an callback. - mTetherStatsProvider.onSetAlert(0); - waitForIdle(); - mTetherStatsProviderCb.expectNotifyAlertReached(); - - // Verify that notifyAlertReached never fired if quota is not yet reached. - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( - new ForwardedStats(0, 0)); - mTetherStatsProvider.onSetAlert(100); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.assertNoCallback(); - - // Verify that notifyAlertReached fired when quota is reached. - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( - new ForwardedStats(50, 50)); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.expectNotifyAlertReached(); - - // Verify that set quota with UNLIMITED won't trigger any callback, and won't fetch - // any stats since the polling is stopped. - reset(mHardware); - mTetherStatsProvider.onSetAlert(NetworkStatsProvider.QUOTA_UNLIMITED); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.assertNoCallback(); - verify(mHardware, never()).getForwardedStats(any()); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java deleted file mode 100644 index 38b19dd3da5c..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.util.TetheringUtils.uint16; -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.AF_UNIX; -import static android.system.OsConstants.SOCK_STREAM; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; -import android.hardware.tetheroffload.control.V1_0.IOffloadControl; -import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; -import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; -import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; -import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; -import android.net.netlink.StructNfGenMsg; -import android.net.netlink.StructNlMsgHdr; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.NativeHandle; -import android.os.test.TestLooper; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.FileDescriptor; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public final class OffloadHardwareInterfaceTest { - private static final String RMNET0 = "test_rmnet_data0"; - - private final TestLooper mTestLooper = new TestLooper(); - - private OffloadHardwareInterface mOffloadHw; - private ITetheringOffloadCallback mTetheringOffloadCallback; - private OffloadHardwareInterface.ControlCallback mControlCallback; - - @Mock private IOffloadConfig mIOffloadConfig; - @Mock private IOffloadControl mIOffloadControl; - @Mock private NativeHandle mNativeHandle; - - // Random values to test Netlink message. - private static final short TEST_TYPE = 184; - private static final short TEST_FLAGS = 263; - - class MyDependencies extends OffloadHardwareInterface.Dependencies { - MyDependencies(SharedLog log) { - super(log); - } - - @Override - public IOffloadConfig getOffloadConfig() { - return mIOffloadConfig; - } - - @Override - public IOffloadControl getOffloadControl() { - return mIOffloadControl; - } - - @Override - public NativeHandle createConntrackSocket(final int groups) { - return mNativeHandle; - } - } - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - final SharedLog log = new SharedLog("test"); - mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log, - new MyDependencies(log)); - mControlCallback = spy(new OffloadHardwareInterface.ControlCallback()); - } - - private void startOffloadHardwareInterface() throws Exception { - mOffloadHw.initOffloadConfig(); - mOffloadHw.initOffloadControl(mControlCallback); - final ArgumentCaptor mOffloadCallbackCaptor = - ArgumentCaptor.forClass(ITetheringOffloadCallback.class); - verify(mIOffloadControl).initOffload(mOffloadCallbackCaptor.capture(), any()); - mTetheringOffloadCallback = mOffloadCallbackCaptor.getValue(); - } - - @Test - public void testGetForwardedStats() throws Exception { - startOffloadHardwareInterface(); - final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0); - verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any()); - assertNotNull(stats); - } - - @Test - public void testSetLocalPrefixes() throws Exception { - startOffloadHardwareInterface(); - final ArrayList localPrefixes = new ArrayList<>(); - localPrefixes.add("127.0.0.0/8"); - localPrefixes.add("fe80::/64"); - mOffloadHw.setLocalPrefixes(localPrefixes); - verify(mIOffloadControl).setLocalPrefixes(eq(localPrefixes), any()); - } - - @Test - public void testSetDataLimit() throws Exception { - startOffloadHardwareInterface(); - final long limit = 12345; - mOffloadHw.setDataLimit(RMNET0, limit); - verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any()); - } - - @Test - public void testSetUpstreamParameters() throws Exception { - startOffloadHardwareInterface(); - final String v4addr = "192.168.10.1"; - final String v4gateway = "192.168.10.255"; - final ArrayList v6gws = new ArrayList<>(0); - v6gws.add("2001:db8::1"); - mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws); - verify(mIOffloadControl).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway), - eq(v6gws), any()); - - final ArgumentCaptor> mArrayListCaptor = - ArgumentCaptor.forClass(ArrayList.class); - mOffloadHw.setUpstreamParameters(null, null, null, null); - verify(mIOffloadControl).setUpstreamParameters(eq(""), eq(""), eq(""), - mArrayListCaptor.capture(), any()); - assertEquals(mArrayListCaptor.getValue().size(), 0); - } - - @Test - public void testUpdateDownstreamPrefix() throws Exception { - startOffloadHardwareInterface(); - final String ifName = "wlan1"; - final String prefix = "192.168.43.0/24"; - mOffloadHw.addDownstreamPrefix(ifName, prefix); - verify(mIOffloadControl).addDownstream(eq(ifName), eq(prefix), any()); - - mOffloadHw.removeDownstreamPrefix(ifName, prefix); - verify(mIOffloadControl).removeDownstream(eq(ifName), eq(prefix), any()); - } - - @Test - public void testTetheringOffloadCallback() throws Exception { - startOffloadHardwareInterface(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStarted(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStoppedError(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStoppedUnsupported(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE); - mTestLooper.dispatchAll(); - verify(mControlCallback).onSupportAvailable(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStoppedLimitReached(); - - final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP); - mTetheringOffloadCallback.updateTimeout(tcpParams); - mTestLooper.dispatchAll(); - verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP), - eq(tcpParams.src.addr), - eq(uint16(tcpParams.src.port)), - eq(tcpParams.dst.addr), - eq(uint16(tcpParams.dst.port))); - - final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP); - mTetheringOffloadCallback.updateTimeout(udpParams); - mTestLooper.dispatchAll(); - verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP), - eq(udpParams.src.addr), - eq(uint16(udpParams.src.port)), - eq(udpParams.dst.addr), - eq(uint16(udpParams.dst.port))); - } - - @Test - public void testSendIpv4NfGenMsg() throws Exception { - FileDescriptor writeSocket = new FileDescriptor(); - FileDescriptor readSocket = new FileDescriptor(); - try { - Os.socketpair(AF_UNIX, SOCK_STREAM, 0, writeSocket, readSocket); - } catch (ErrnoException e) { - fail(); - return; - } - when(mNativeHandle.getFileDescriptor()).thenReturn(writeSocket); - - mOffloadHw.sendIpv4NfGenMsg(mNativeHandle, TEST_TYPE, TEST_FLAGS); - - ByteBuffer buffer = ByteBuffer.allocate(9823); // Arbitrary value > expectedLen. - buffer.order(ByteOrder.nativeOrder()); - - int read = Os.read(readSocket, buffer); - final int expectedLen = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE; - assertEquals(expectedLen, read); - - buffer.flip(); - assertEquals(expectedLen, buffer.getInt()); - assertEquals(TEST_TYPE, buffer.getShort()); - assertEquals(TEST_FLAGS, buffer.getShort()); - assertEquals(0 /* seq */, buffer.getInt()); - assertEquals(0 /* pid */, buffer.getInt()); - assertEquals(AF_INET, buffer.get()); // nfgen_family - assertEquals(0 /* error */, buffer.get()); // version - assertEquals(0 /* error */, buffer.getShort()); // res_id - assertEquals(expectedLen, buffer.position()); - } - - private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) { - final NatTimeoutUpdate params = new NatTimeoutUpdate(); - params.proto = proto; - params.src.addr = "192.168.43.200"; - params.src.port = 100; - params.dst.addr = "172.50.46.169"; - params.dst.port = 150; - return params; - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java deleted file mode 100644 index 41d46e522ca4..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.networkstack.tethering; - -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.util.PrefixUtils.asIpPrefix; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.ip.IpServer; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Arrays; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public final class PrivateAddressCoordinatorTest { - private static final String TEST_IFNAME = "test0"; - - @Mock private IpServer mHotspotIpServer; - @Mock private IpServer mUsbIpServer; - @Mock private IpServer mEthernetIpServer; - @Mock private IpServer mWifiP2pIpServer; - @Mock private Context mContext; - @Mock private ConnectivityManager mConnectivityMgr; - @Mock private TetheringConfiguration mConfig; - - private PrivateAddressCoordinator mPrivateAddressCoordinator; - private final LinkAddress mBluetoothAddress = new LinkAddress("192.168.44.1/24"); - private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24"); - private final Network mWifiNetwork = new Network(1); - private final Network mMobileNetwork = new Network(2); - private final Network mVpnNetwork = new Network(3); - private final Network mMobileNetwork2 = new Network(4); - private final Network mMobileNetwork3 = new Network(5); - private final Network mMobileNetwork4 = new Network(6); - private final Network mMobileNetwork5 = new Network(7); - private final Network mMobileNetwork6 = new Network(8); - private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork, mVpnNetwork, - mMobileNetwork2, mMobileNetwork3, mMobileNetwork4, mMobileNetwork5, mMobileNetwork6}; - private final ArrayList mTetheringPrefixes = new ArrayList<>(Arrays.asList( - new IpPrefix("192.168.0.0/16"), - new IpPrefix("172.16.0.0/12"), - new IpPrefix("10.0.0.0/8"))); - - private void setUpIpServers() throws Exception { - when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB); - when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET); - when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI); - when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P); - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr); - when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks); - when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false); - when(mConfig.isSelectAllPrefixRangeEnabled()).thenReturn(true); - setUpIpServers(); - mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig)); - } - - private LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) { - final LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( - ipServer, useLastAddress); - when(ipServer.getAddress()).thenReturn(address); - return address; - } - - @Test - public void testRequestDownstreamAddressWithoutUsingLastAddress() throws Exception { - final IpPrefix bluetoothPrefix = asIpPrefix(mBluetoothAddress); - final LinkAddress address = requestDownstreamAddress(mHotspotIpServer, - false /* useLastAddress */); - final IpPrefix hotspotPrefix = asIpPrefix(address); - assertNotEquals(hotspotPrefix, bluetoothPrefix); - - final LinkAddress newAddress = requestDownstreamAddress(mHotspotIpServer, - false /* useLastAddress */); - final IpPrefix testDupRequest = asIpPrefix(newAddress); - assertNotEquals(hotspotPrefix, testDupRequest); - assertNotEquals(bluetoothPrefix, testDupRequest); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - - final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer, - false /* useLastAddress */); - final IpPrefix usbPrefix = asIpPrefix(usbAddress); - assertNotEquals(usbPrefix, bluetoothPrefix); - assertNotEquals(usbPrefix, hotspotPrefix); - mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); - } - - @Test - public void testSanitizedAddress() throws Exception { - int fakeSubAddr = 0x2b00; // 43.0. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - LinkAddress actualAddress = requestDownstreamAddress(mHotspotIpServer, - false /* useLastAddress */); - assertEquals(new LinkAddress("192.168.43.2/24"), actualAddress); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - - fakeSubAddr = 0x2d01; // 45.1. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */); - assertEquals(new LinkAddress("192.168.45.2/24"), actualAddress); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - - fakeSubAddr = 0x2eff; // 46.255. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */); - assertEquals(new LinkAddress("192.168.46.254/24"), actualAddress); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - - fakeSubAddr = 0x2f05; // 47.5. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */); - assertEquals(new LinkAddress("192.168.47.5/24"), actualAddress); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - } - - @Test - public void testReservedPrefix() throws Exception { - // - Test bluetooth prefix is reserved. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( - getSubAddress(mBluetoothAddress.getAddress().getAddress())); - final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer, - false /* useLastAddress */); - final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddress); - assertNotEquals(asIpPrefix(mBluetoothAddress), hotspotPrefix); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - - // - Test previous enabled hotspot prefix(cached prefix) is reserved. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( - getSubAddress(hotspotAddress.getAddress().getAddress())); - final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer, - false /* useLastAddress */); - final IpPrefix usbPrefix = asIpPrefix(usbAddress); - assertNotEquals(asIpPrefix(mBluetoothAddress), usbPrefix); - assertNotEquals(hotspotPrefix, usbPrefix); - mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); - - // - Test wifi p2p prefix is reserved. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( - getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress())); - final LinkAddress etherAddress = requestDownstreamAddress(mEthernetIpServer, - false /* useLastAddress */); - final IpPrefix etherPrefix = asIpPrefix(etherAddress); - assertNotEquals(asIpPrefix(mLegacyWifiP2pAddress), etherPrefix); - assertNotEquals(asIpPrefix(mBluetoothAddress), etherPrefix); - assertNotEquals(hotspotPrefix, etherPrefix); - mPrivateAddressCoordinator.releaseDownstream(mEthernetIpServer); - } - - @Test - public void testRequestLastDownstreamAddress() throws Exception { - final int fakeHotspotSubAddr = 0x2b05; // 43.5 - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr); - final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.43.5/24"), hotspotAddress); - - final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.45.5/24"), usbAddress); - - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); - - final int newFakeSubAddr = 0x3c05; - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr); - - final LinkAddress newHotspotAddress = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals(hotspotAddress, newHotspotAddress); - final LinkAddress newUsbAddress = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - assertEquals(usbAddress, newUsbAddress); - - final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork, - new LinkAddress("192.168.88.23/16"), null, - makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream); - verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - verify(mUsbIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - } - - private UpstreamNetworkState buildUpstreamNetworkState(final Network network, - final LinkAddress v4Addr, final LinkAddress v6Addr, final NetworkCapabilities cap) { - final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(TEST_IFNAME); - if (v4Addr != null) prop.addLinkAddress(v4Addr); - - if (v6Addr != null) prop.addLinkAddress(v6Addr); - - return new UpstreamNetworkState(prop, cap, network); - } - - private NetworkCapabilities makeNetworkCapabilities(final int transportType) { - final NetworkCapabilities cap = new NetworkCapabilities(); - cap.addTransportType(transportType); - if (transportType == TRANSPORT_VPN) { - cap.removeCapability(NET_CAPABILITY_NOT_VPN); - } - - return cap; - } - - @Test - public void testNoConflictUpstreamPrefix() throws Exception { - final int fakeHotspotSubAddr = 0x2b05; // 43.5 - final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24"); - // Force always get subAddress "43.5" for conflict testing. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr); - // - Enable hotspot with prefix 192.168.43.0/24 - final LinkAddress hotspotAddr = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddr); - assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix); - // - test mobile network with null NetworkCapabilities. Ideally this should not happen - // because NetworkCapabilities update should always happen before LinkProperties update - // and the UpstreamNetworkState update, just make sure no crash in this case. - final UpstreamNetworkState noCapUpstream = buildUpstreamNetworkState(mMobileNetwork, - new LinkAddress("10.0.0.8/24"), null, null); - mPrivateAddressCoordinator.updateUpstreamPrefix(noCapUpstream); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - test mobile upstream with no address. - final UpstreamNetworkState noAddress = buildUpstreamNetworkState(mMobileNetwork, - null, null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(noCapUpstream); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - Update v6 only mobile network, hotspot prefix should not be removed. - final UpstreamNetworkState v6OnlyMobile = buildUpstreamNetworkState(mMobileNetwork, - null, new LinkAddress("2001:db8::/64"), - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v6OnlyMobile); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - mPrivateAddressCoordinator.removeUpstreamPrefix(mMobileNetwork); - // - Update v4 only mobile network, hotspot prefix should not be removed. - final UpstreamNetworkState v4OnlyMobile = buildUpstreamNetworkState(mMobileNetwork, - new LinkAddress("10.0.0.8/24"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyMobile); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - Update v4v6 mobile network, hotspot prefix should not be removed. - final UpstreamNetworkState v4v6Mobile = buildUpstreamNetworkState(mMobileNetwork, - new LinkAddress("10.0.0.8/24"), new LinkAddress("2001:db8::/64"), - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v4v6Mobile); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - Update v6 only wifi network, hotspot prefix should not be removed. - final UpstreamNetworkState v6OnlyWifi = buildUpstreamNetworkState(mWifiNetwork, - null, new LinkAddress("2001:db8::/64"), makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v6OnlyWifi); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork); - // - Update vpn network, it conflict with hotspot prefix but VPN networks are ignored. - final UpstreamNetworkState v4OnlyVpn = buildUpstreamNetworkState(mVpnNetwork, - new LinkAddress("192.168.43.5/24"), null, makeNetworkCapabilities(TRANSPORT_VPN)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyVpn); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - Update v4 only wifi network, it conflict with hotspot prefix. - final UpstreamNetworkState v4OnlyWifi = buildUpstreamNetworkState(mWifiNetwork, - new LinkAddress("192.168.43.5/24"), null, makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi); - verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - reset(mHotspotIpServer); - // - Restart hotspot again and its prefix is different previous. - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - final LinkAddress hotspotAddr2 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - final IpPrefix hotspotPrefix2 = asIpPrefix(hotspotAddr2); - assertNotEquals(hotspotPrefix, hotspotPrefix2); - mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - Usb tethering can be enabled and its prefix is different with conflict one. - final LinkAddress usbAddr = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - final IpPrefix usbPrefix = asIpPrefix(usbAddr); - assertNotEquals(predefinedPrefix, usbPrefix); - assertNotEquals(hotspotPrefix2, usbPrefix); - // - Disable wifi upstream, then wifi's prefix can be selected again. - mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork); - final LinkAddress ethAddr = requestDownstreamAddress(mEthernetIpServer, - true /* useLastAddress */); - final IpPrefix ethPrefix = asIpPrefix(ethAddr); - assertEquals(predefinedPrefix, ethPrefix); - } - - @Test - public void testChooseAvailablePrefix() throws Exception { - final int randomAddress = 0x8605; // 134.5 - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress); - final LinkAddress addr0 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.134.5. - assertEquals("Wrong prefix: ", new LinkAddress("192.168.134.5/24"), addr0); - final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork, - new LinkAddress("192.168.134.13/26"), null, - makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream); - - // Check whether return address is next prefix of 192.168.134.0/24. - final LinkAddress addr1 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.135.5/24"), addr1); - final UpstreamNetworkState wifiUpstream2 = buildUpstreamNetworkState(mWifiNetwork, - new LinkAddress("192.168.149.16/19"), null, - makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream2); - - - // The conflict range is 128 ~ 159, so the address is 192.168.160.5/24. - final LinkAddress addr2 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.160.5/24"), addr2); - final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork, - new LinkAddress("192.168.129.53/18"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - // Update another conflict upstream which is covered by the previous one (but not the first - // one) and verify whether this would affect the result. - final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2, - new LinkAddress("192.168.170.7/19"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2); - - // The conflict range are 128 ~ 159 and 159 ~ 191, so the address is 192.168.192.5/24. - final LinkAddress addr3 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.192.5/24"), addr3); - final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3, - new LinkAddress("192.168.188.133/17"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3); - - // Conflict range: 128 ~ 255. The next available address is 192.168.0.5 because - // 192.168.134/24 ~ 192.168.255.255/24 is not available. - final LinkAddress addr4 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.0.5/24"), addr4); - final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4, - new LinkAddress("192.168.3.59/21"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4); - - // Conflict ranges: 128 ~ 255 and 0 ~ 7, so the address is 192.168.8.5/24. - final LinkAddress addr5 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr5); - final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5, - new LinkAddress("192.168.68.43/21"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5); - - // Update an upstream that does *not* conflict, check whether return the same address - // 192.168.5/24. - final LinkAddress addr6 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr6); - final UpstreamNetworkState mobileUpstream6 = buildUpstreamNetworkState(mMobileNetwork6, - new LinkAddress("192.168.10.97/21"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream6); - - // Conflict ranges: 0 ~ 15 and 128 ~ 255, so the address is 192.168.16.5/24. - final LinkAddress addr7 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.16.5/24"), addr7); - final UpstreamNetworkState mobileUpstream7 = buildUpstreamNetworkState(mMobileNetwork6, - new LinkAddress("192.168.0.0/17"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream7); - - // Choose prefix from next range(172.16.0.0/12) when no available prefix in 192.168.0.0/16. - final LinkAddress addr8 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.16.134.5/24"), addr8); - } - - @Test - public void testChoosePrefixFromDifferentRanges() throws Exception { - final int randomAddress = 0x1f2b2a; // 31.43.42 - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress); - final LinkAddress classC1 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.43.42. - assertEquals("Wrong prefix: ", new LinkAddress("192.168.43.42/24"), classC1); - final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork, - new LinkAddress("192.168.88.23/17"), null, - makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream); - verifyNotifyConflictAndRelease(mHotspotIpServer); - - // Check whether return address is next address of prefix 192.168.128.0/17. - final LinkAddress classC2 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.128.42/24"), classC2); - final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork, - new LinkAddress("192.1.2.3/8"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream); - verifyNotifyConflictAndRelease(mHotspotIpServer); - - // Check whether return address is under prefix 172.16.0.0/12. - final LinkAddress classB1 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.31.43.42/24"), classB1); - final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2, - new LinkAddress("172.28.123.100/14"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2); - verifyNotifyConflictAndRelease(mHotspotIpServer); - - // 172.28.0.0 ~ 172.31.255.255 is not available. - // Check whether return address is next address of prefix 172.16.0.0/14. - final LinkAddress classB2 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.16.0.42/24"), classB2); - - // Check whether new downstream is next address of address 172.16.0.42/24. - final LinkAddress classB3 = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.16.1.42/24"), classB3); - final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3, - new LinkAddress("172.16.0.1/24"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3); - verifyNotifyConflictAndRelease(mHotspotIpServer); - verify(mUsbIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - - // Check whether return address is next address of prefix 172.16.1.42/24. - final LinkAddress classB4 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.16.2.42/24"), classB4); - final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4, - new LinkAddress("172.16.0.1/13"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4); - verifyNotifyConflictAndRelease(mHotspotIpServer); - verifyNotifyConflictAndRelease(mUsbIpServer); - - // Check whether return address is next address of prefix 172.16.0.1/13. - final LinkAddress classB5 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.24.0.42/24"), classB5); - // Check whether return address is next address of prefix 172.24.0.42/24. - final LinkAddress classB6 = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.24.1.42/24"), classB6); - final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5, - new LinkAddress("172.24.0.1/12"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5); - verifyNotifyConflictAndRelease(mHotspotIpServer); - verifyNotifyConflictAndRelease(mUsbIpServer); - - // Check whether return address is prefix 10.0.0.0/8 + subAddress 0.31.43.42. - final LinkAddress classA1 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("10.31.43.42/24"), classA1); - // Check whether new downstream is next address of address 10.31.43.42/24. - final LinkAddress classA2 = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("10.31.44.42/24"), classA2); - } - - private void verifyNotifyConflictAndRelease(final IpServer ipServer) throws Exception { - verify(ipServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - mPrivateAddressCoordinator.releaseDownstream(ipServer); - reset(ipServer); - setUpIpServers(); - } - - private int getSubAddress(final byte... ipv4Address) { - assertEquals(4, ipv4Address.length); - - int subnet = Byte.toUnsignedInt(ipv4Address[2]); - return (subnet << 8) + ipv4Address[3]; - } - - private void assertReseveredWifiP2pPrefix() throws Exception { - LinkAddress address = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - final IpPrefix hotspotPrefix = asIpPrefix(address); - final IpPrefix legacyWifiP2pPrefix = asIpPrefix(mLegacyWifiP2pAddress); - assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - } - - @Test - public void testEnableLegacyWifiP2PAddress() throws Exception { - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( - getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress())); - // No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix - // is resevered. - assertReseveredWifiP2pPrefix(); - - when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(true); - assertReseveredWifiP2pPrefix(); - - // If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address. - LinkAddress address = requestDownstreamAddress(mWifiP2pIpServer, - true /* useLastAddress */); - assertEquals(mLegacyWifiP2pAddress, address); - mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java deleted file mode 100644 index 237e2c27bfa1..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ /dev/null @@ -1,458 +0,0 @@ -/* - * Copyright (C) 2017 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.networkstack.tethering; - -import static android.net.ConnectivityManager.TYPE_ETHERNET; -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; -import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; -import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; -import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.res.Resources; -import android.net.util.SharedLog; -import android.provider.DeviceConfig; -import android.telephony.TelephonyManager; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.test.BroadcastInterceptingContext; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -import java.util.Arrays; -import java.util.Iterator; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class TetheringConfigurationTest { - private final SharedLog mLog = new SharedLog("TetheringConfigurationTest"); - - private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; - private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - private static final String PROVISIONING_APP_RESPONSE = "app_response"; - @Mock private Context mContext; - @Mock private TelephonyManager mTelephonyManager; - @Mock private Resources mResources; - @Mock private Resources mResourcesForSubId; - private Context mMockContext; - private boolean mHasTelephonyManager; - private boolean mEnableLegacyDhcpServer; - private MockitoSession mMockingSession; - - private class MockTetheringConfiguration extends TetheringConfiguration { - MockTetheringConfiguration(Context ctx, SharedLog log, int id) { - super(ctx, log, id); - } - - @Override - protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { - return mResourcesForSubId; - } - } - - private class MockContext extends BroadcastInterceptingContext { - MockContext(Context base) { - super(base); - } - - @Override - public Resources getResources() { - return mResources; - } - - @Override - public Object getSystemService(String name) { - if (Context.TELEPHONY_SERVICE.equals(name)) { - return mHasTelephonyManager ? mTelephonyManager : null; - } - return super.getSystemService(name); - } - } - - @Before - public void setUp() throws Exception { - // TODO: use a dependencies class instead of mock statics. - mMockingSession = mockitoSession() - .initMocks(this) - .mockStatic(DeviceConfig.class) - .strictness(Strictness.WARN) - .startMocking(); - doReturn(null).when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); - - when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn( - new String[0]); - when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn( - TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) - .thenReturn(new String[]{ "test_wlan\\d" }); - when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn( - new String[0]); - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); - when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) - .thenReturn(new String[0]); - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - false); - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip)) - .thenReturn(false); - initializeBpfOffloadConfiguration(true, null /* unset */); - initEnableSelectAllPrefixRangeFlag(null /* unset */); - - mHasTelephonyManager = true; - mMockContext = new MockContext(mContext); - mEnableLegacyDhcpServer = false; - } - - @After - public void tearDown() throws Exception { - mMockingSession.finishMocking(); - } - - private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) { - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn( - legacyTetherUpstreamTypes); - return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - } - - @Test - public void testNoTelephonyManagerMeansNoDun() { - mHasTelephonyManager = false; - final TetheringConfiguration cfg = getTetheringConfiguration( - new int[]{TYPE_MOBILE_DUN, TYPE_WIFI}); - assertFalse(cfg.isDunRequired); - assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); - // Just to prove we haven't clobbered Wi-Fi: - assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI)); - } - - @Test - public void testDunFromTelephonyManagerMeansDun() { - when(mTelephonyManager.isTetheringApnRequired()).thenReturn(true); - - final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); - final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( - TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI); - final TetheringConfiguration cfgWifiDun = getTetheringConfiguration( - TYPE_WIFI, TYPE_MOBILE_DUN); - final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration( - TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN); - - for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, - cfgWifiDun, cfgMobileWifiHipriDun)) { - String msg = "config=" + cfg.toString(); - assertTrue(msg, cfg.isDunRequired); - assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); - assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); - assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); - // Just to prove we haven't clobbered Wi-Fi: - assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI)); - } - } - - @Test - public void testDunNotRequiredFromTelephonyManagerMeansNoDun() { - when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); - - final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); - final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( - TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI); - final TetheringConfiguration cfgWifiDun = getTetheringConfiguration( - TYPE_WIFI, TYPE_MOBILE_DUN); - final TetheringConfiguration cfgWifiMobile = getTetheringConfiguration( - TYPE_WIFI, TYPE_MOBILE); - final TetheringConfiguration cfgWifiHipri = getTetheringConfiguration( - TYPE_WIFI, TYPE_MOBILE_HIPRI); - final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration( - TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN); - - String msg; - // TYPE_MOBILE_DUN should be present in none of the combinations. - // TYPE_WIFI should not be affected. - for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun, - cfgWifiMobile, cfgWifiHipri, cfgMobileWifiHipriDun)) { - msg = "config=" + cfg.toString(); - assertFalse(msg, cfg.isDunRequired); - assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); - assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI)); - } - - for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun, - cfgMobileWifiHipriDun)) { - msg = "config=" + cfg.toString(); - assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); - assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); - } - msg = "config=" + cfgWifiMobile.toString(); - assertTrue(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); - assertFalse(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); - msg = "config=" + cfgWifiHipri.toString(); - assertFalse(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); - assertTrue(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); - - } - - @Test - public void testNoDefinedUpstreamTypesAddsEthernet() { - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[]{}); - when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); - - final TetheringConfiguration cfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - final Iterator upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); - // The following is because the code always adds some kind of mobile - // upstream, be it DUN or, in this case where DUN is NOT required, - // make sure there is at least one of MOBILE or HIPRI. With the empty - // list of the configuration in this test, it will always add both - // MOBILE and HIPRI, in that order. - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_MOBILE, upstreamIterator.next().intValue()); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue()); - assertFalse(upstreamIterator.hasNext()); - } - - @Test - public void testDefinedUpstreamTypesSansEthernetAddsEthernet() { - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn( - new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); - - final TetheringConfiguration cfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - final Iterator upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_WIFI, upstreamIterator.next().intValue()); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue()); - assertFalse(upstreamIterator.hasNext()); - } - - @Test - public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() { - when(mResources.getIntArray(R.array.config_tether_upstream_types)) - .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); - - final TetheringConfiguration cfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - final Iterator upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_WIFI, upstreamIterator.next().intValue()); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue()); - assertFalse(upstreamIterator.hasNext()); - } - - private void initializeBpfOffloadConfiguration( - final boolean fromRes, final String fromDevConfig) { - when(mResources.getBoolean(R.bool.config_tether_enable_bpf_offload)).thenReturn(fromRes); - doReturn(fromDevConfig).when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD))); - } - - @Test - public void testBpfOffloadEnabledByResource() { - initializeBpfOffloadConfiguration(true, null /* unset */); - final TetheringConfiguration enableByRes = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(enableByRes.isBpfOffloadEnabled()); - } - - @Test - public void testBpfOffloadEnabledByDeviceConfigOverride() { - for (boolean res : new boolean[]{true, false}) { - initializeBpfOffloadConfiguration(res, "true"); - final TetheringConfiguration enableByDevConOverride = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(enableByDevConOverride.isBpfOffloadEnabled()); - } - } - - @Test - public void testBpfOffloadDisabledByResource() { - initializeBpfOffloadConfiguration(false, null /* unset */); - final TetheringConfiguration disableByRes = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(disableByRes.isBpfOffloadEnabled()); - } - - @Test - public void testBpfOffloadDisabledByDeviceConfigOverride() { - for (boolean res : new boolean[]{true, false}) { - initializeBpfOffloadConfiguration(res, "false"); - final TetheringConfiguration disableByDevConOverride = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(disableByDevConOverride.isBpfOffloadEnabled()); - } - } - - @Test - public void testNewDhcpServerDisabled() { - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - true); - doReturn("false").when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); - - final TetheringConfiguration enableByRes = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(enableByRes.enableLegacyDhcpServer); - - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - false); - doReturn("true").when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); - - final TetheringConfiguration enableByDevConfig = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(enableByDevConfig.enableLegacyDhcpServer); - } - - @Test - public void testNewDhcpServerEnabled() { - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - false); - doReturn("false").when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); - - final TetheringConfiguration cfg = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - - assertFalse(cfg.enableLegacyDhcpServer); - } - - @Test - public void testOffloadIntervalByResource() { - final TetheringConfiguration intervalByDefault = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertEquals(TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, - intervalByDefault.getOffloadPollInterval()); - - final int[] testOverrides = {0, 3000, -1}; - for (final int override : testOverrides) { - when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn( - override); - final TetheringConfiguration overrideByRes = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertEquals(override, overrideByRes.getOffloadPollInterval()); - } - } - - @Test - public void testGetResourcesBySubId() { - setUpResourceForSubId(); - final TetheringConfiguration cfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(cfg.provisioningApp.length == 0); - final int anyValidSubId = 1; - final MockTetheringConfiguration mockCfg = - new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId); - assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]); - assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]); - assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME); - assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE); - } - - private void setUpResourceForSubId() { - when(mResourcesForSubId.getStringArray( - R.array.config_tether_dhcp_range)).thenReturn(new String[0]); - when(mResourcesForSubId.getStringArray( - R.array.config_tether_usb_regexs)).thenReturn(new String[0]); - when(mResourcesForSubId.getStringArray( - R.array.config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" }); - when(mResourcesForSubId.getStringArray( - R.array.config_tether_bluetooth_regexs)).thenReturn(new String[0]); - when(mResourcesForSubId.getIntArray(R.array.config_tether_upstream_types)).thenReturn( - new int[0]); - when(mResourcesForSubId.getStringArray( - R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); - when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) - .thenReturn(PROVISIONING_NO_UI_APP_NAME); - when(mResourcesForSubId.getString( - R.string.config_mobile_hotspot_provision_response)).thenReturn( - PROVISIONING_APP_RESPONSE); - } - - @Test - public void testEnableLegacyWifiP2PAddress() throws Exception { - final TetheringConfiguration defaultCfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(defaultCfg.shouldEnableWifiP2pDedicatedIp()); - - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip)) - .thenReturn(true); - final TetheringConfiguration testCfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp()); - } - - private void initEnableSelectAllPrefixRangeFlag(final String value) { - doReturn(value).when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES))); - } - - @Test - public void testSelectAllPrefixRangeFlag() throws Exception { - // Test default value. - final TetheringConfiguration defaultCfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(defaultCfg.isSelectAllPrefixRangeEnabled()); - - // Test disable flag. - initEnableSelectAllPrefixRangeFlag("false"); - final TetheringConfiguration testDisable = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(testDisable.isSelectAllPrefixRangeEnabled()); - - // Test enable flag. - initEnableSelectAllPrefixRangeFlag("true"); - final TetheringConfiguration testEnable = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(testEnable.isSelectAllPrefixRangeEnabled()); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt deleted file mode 100644 index 75c819bb0ced..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering - -import android.app.Notification -import android.app.NotificationManager -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_IMMUTABLE -import android.content.Context -import android.content.Intent -import android.content.pm.ActivityInfo -import android.content.pm.ApplicationInfo -import android.content.pm.PackageManager -import android.content.pm.ResolveInfo -import android.content.res.Resources -import android.net.ConnectivityManager.TETHERING_WIFI -import android.net.NetworkCapabilities -import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING -import android.os.Handler -import android.os.HandlerThread -import android.os.Looper -import android.os.UserHandle -import android.provider.Settings -import android.telephony.TelephonyManager -import androidx.test.filters.SmallTest -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.runner.AndroidJUnit4 -import com.android.internal.util.test.BroadcastInterceptingContext -import com.android.networkstack.tethering.TetheringNotificationUpdater.ACTION_DISABLE_TETHERING -import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE -import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM -import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID -import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID -import com.android.networkstack.tethering.TetheringNotificationUpdater.ROAMING_NOTIFICATION_ID -import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID -import com.android.networkstack.tethering.TetheringNotificationUpdater.getSettingsPackageName -import com.android.testutils.waitForIdle -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Assert.fail -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mock -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.reset -import org.mockito.Mockito.times -import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyZeroInteractions -import org.mockito.MockitoAnnotations - -const val TEST_SUBID = 1 -const val WIFI_MASK = 1 shl TETHERING_WIFI -const val TEST_DISALLOW_TITLE = "Tether function is disallowed" -const val TEST_DISALLOW_MESSAGE = "Please contact your admin" -const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access" -const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet." -const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot" -const val TEST_ROAMING_TITLE = "Hotspot is on" -const val TEST_ROAMING_MESSAGE = "Additional charges may apply while roaming." - -@RunWith(AndroidJUnit4::class) -@SmallTest -class TetheringNotificationUpdaterTest { - // lateinit used here for mocks as they need to be reinitialized between each test and the test - // should crash if they are used before being initialized. - @Mock private lateinit var mockContext: Context - @Mock private lateinit var notificationManager: NotificationManager - @Mock private lateinit var telephonyManager: TelephonyManager - @Mock private lateinit var testResources: Resources - - // lateinit for these classes under test, as they should be reset to a different instance for - // every test but should always be initialized before use (or the test should crash). - private lateinit var context: TestContext - private lateinit var notificationUpdater: TetheringNotificationUpdater - - // Initializing the following members depends on initializing some of the mocks and - // is more logically done in setup(). - private lateinit var fakeTetheringThread: HandlerThread - - private val ROAMING_CAPABILITIES = NetworkCapabilities() - private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING) - private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general - private val TIMEOUT_MS = 500L - private val ACTIVITY_PENDING_INTENT = 0 - private val BROADCAST_PENDING_INTENT = 1 - - private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) { - override fun createContextAsUser(user: UserHandle, flags: Int) = - if (user == UserHandle.ALL) mockContext else this - override fun getSystemService(name: String) = - if (name == Context.TELEPHONY_SERVICE) telephonyManager - else super.getSystemService(name) - } - - private inner class WrappedNotificationUpdater(c: Context, looper: Looper) - : TetheringNotificationUpdater(c, looper) { - override fun getResourcesForSubId(c: Context, subId: Int) = - if (subId == TEST_SUBID) testResources else super.getResourcesForSubId(c, subId) - } - - private fun setupResources() { - doReturn(5).`when`(testResources) - .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) - doReturn(true).`when`(testResources) - .getBoolean(R.bool.config_upstream_roaming_notification) - doReturn(TEST_DISALLOW_TITLE).`when`(testResources) - .getString(R.string.disable_tether_notification_title) - doReturn(TEST_DISALLOW_MESSAGE).`when`(testResources) - .getString(R.string.disable_tether_notification_message) - doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources) - .getString(R.string.no_upstream_notification_title) - doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources) - .getString(R.string.no_upstream_notification_message) - doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources) - .getString(R.string.no_upstream_notification_disable_button) - doReturn(TEST_ROAMING_TITLE).`when`(testResources) - .getString(R.string.upstream_roaming_notification_title) - doReturn(TEST_ROAMING_MESSAGE).`when`(testResources) - .getString(R.string.upstream_roaming_notification_message) - } - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - context = TestContext(InstrumentationRegistry.getInstrumentation().context) - doReturn(notificationManager).`when`(mockContext) - .getSystemService(Context.NOTIFICATION_SERVICE) - fakeTetheringThread = HandlerThread(this::class.java.simpleName) - fakeTetheringThread.start() - notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper) - setupResources() - } - - @After - fun tearDown() { - fakeTetheringThread.quitSafely() - } - - private fun verifyActivityPendingIntent(intent: Intent, flags: Int) { - // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add - // the flag in creating arguments). If the described PendingIntent does not already exist, - // getActivity() will return null instead of PendingIntent object. - val pi = PendingIntent.getActivity( - context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - intent, - flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE, - null /* options */) - assertNotNull("Activity PendingIntent with FLAG_IMMUTABLE does not exist.", pi) - } - - private fun verifyBroadcastPendingIntent(intent: Intent, flags: Int) { - // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add - // the flag in creating arguments). If the described PendingIntent does not already exist, - // getBroadcast() will return null instead of PendingIntent object. - val pi = PendingIntent.getBroadcast( - context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - intent, - flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE) - assertNotNull("Broadcast PendingIntent with FLAG_IMMUTABLE does not exist.", pi) - } - - private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE) - private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT) - - private fun verifyNotification( - iconId: Int, - title: String, - text: String, - id: Int, - intentSenderType: Int, - intent: Intent, - flags: Int - ) { - verify(notificationManager, never()).cancel(any(), eq(id)) - - val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) - verify(notificationManager, times(1)) - .notify(any(), eq(id), notificationCaptor.capture()) - - val notification = notificationCaptor.getValue() - assertEquals(iconId, notification.smallIcon.resId) - assertEquals(title, notification.title()) - assertEquals(text, notification.text()) - - when (intentSenderType) { - ACTIVITY_PENDING_INTENT -> verifyActivityPendingIntent(intent, flags) - BROADCAST_PENDING_INTENT -> verifyBroadcastPendingIntent(intent, flags) - } - - reset(notificationManager) - } - - private fun verifyNotificationCancelled( - notificationIds: List, - resetAfterVerified: Boolean = true - ) { - notificationIds.forEach { - verify(notificationManager, times(1)).cancel(any(), eq(it)) - } - if (resetAfterVerified) reset(notificationManager) - } - - @Test - fun testRestrictedNotification() { - val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS) - .setPackage(getSettingsPackageName(context.packageManager)) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - - // Set test sub id. - notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // User restrictions on. Show restricted notification. - notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE, - RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE) - - // User restrictions off. Clear notification. - notificationUpdater.tetheringRestrictionLifted() - verifyNotificationCancelled(listOf(RESTRICTED_NOTIFICATION_ID)) - - // No downstream. - notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyZeroInteractions(notificationManager) - - // User restrictions on again. Show restricted notification. - notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE, - RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE) - } - - val MAX_BACKOFF_MS = 200L - /** - * Waits for all messages, including delayed ones, to be processed. - * - * This will wait until the handler has no more messages to be processed including - * delayed ones, or the timeout has expired. It uses an exponential backoff strategy - * to wait longer and longer to consume less CPU, with the max granularity being - * MAX_BACKOFF_MS. - * - * @return true if all messages have been processed including delayed ones, false if timeout - * - * TODO: Move this method to com.android.testutils.HandlerUtils.kt. - */ - private fun Handler.waitForDelayedMessage(what: Int?, timeoutMs: Long) { - fun hasMatchingMessages() = - if (what == null) hasMessagesOrCallbacks() else hasMessages(what) - val expiry = System.currentTimeMillis() + timeoutMs - var delay = 5L - while (System.currentTimeMillis() < expiry && hasMatchingMessages()) { - // None of Handler, Looper, Message and MessageQueue expose any way to retrieve - // the time when the next (let alone the last) message will be processed, so - // short of examining the internals with reflection sleep() is the only solution. - Thread.sleep(delay) - delay = (delay * 2) - .coerceAtMost(expiry - System.currentTimeMillis()) - .coerceAtMost(MAX_BACKOFF_MS) - } - - val timeout = expiry - System.currentTimeMillis() - if (timeout <= 0) fail("Delayed message did not process yet after ${timeoutMs}ms") - waitForIdle(timeout) - } - - @Test - fun testNoUpstreamNotification() { - val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName) - - // Set test sub id. - notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Wifi downstream. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // There is no upstream. Show no upstream notification. - notificationUpdater.onUpstreamCapabilitiesChanged(null) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) - verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, - NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent, - FLAG_IMMUTABLE) - - // Same capabilities changed. Nothing happened. - notificationUpdater.onUpstreamCapabilitiesChanged(null) - verifyZeroInteractions(notificationManager) - - // Upstream come back. Clear no upstream notification. - notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID)) - - // No upstream again. Show no upstream notification. - notificationUpdater.onUpstreamCapabilitiesChanged(null) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) - verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, - NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent, - FLAG_IMMUTABLE) - - // No downstream. - notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Wifi downstream and home capabilities. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to -1 and change to no upstream - // again. Don't put up no upstream notification. - doReturn(-1).`when`(testResources) - .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) - notificationUpdater.onUpstreamCapabilitiesChanged(null) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID)) - } - - @Test - fun testGetResourcesForSubId() { - doReturn(telephonyManager).`when`(telephonyManager).createForSubscriptionId(anyInt()) - doReturn(1234).`when`(telephonyManager).getSimCarrierId() - doReturn("000000").`when`(telephonyManager).getSimOperator() - - val subId = -2 // Use invalid subId to avoid getting resource from cache or real subId. - val config = context.resources.configuration - var res = notificationUpdater.getResourcesForSubId(context, subId) - assertEquals(config.mcc, res.configuration.mcc) - assertEquals(config.mnc, res.configuration.mnc) - - doReturn(VERIZON_CARRIER_ID).`when`(telephonyManager).getSimCarrierId() - res = notificationUpdater.getResourcesForSubId(context, subId) - assertEquals(config.mcc, res.configuration.mcc) - assertEquals(config.mnc, res.configuration.mnc) - - doReturn("20404").`when`(telephonyManager).getSimOperator() - res = notificationUpdater.getResourcesForSubId(context, subId) - assertEquals(311, res.configuration.mcc) - assertEquals(480, res.configuration.mnc) - } - - @Test - fun testRoamingNotification() { - val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName) - val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS) - .setPackage(getSettingsPackageName(context.packageManager)) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - - // Set test sub id. - notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Wifi downstream. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Upstream capabilities changed to roaming state. Show roaming notification. - notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) - verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE, - ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE) - - // Same capabilities change. Nothing happened. - notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) - verifyZeroInteractions(notificationManager) - - // Upstream capabilities changed to home state. Clear roaming notification. - notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES) - verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID)) - - // Upstream capabilities changed to roaming state again. Show roaming notification. - notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) - verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE, - ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE) - - // No upstream. Clear roaming notification and show no upstream notification. - notificationUpdater.onUpstreamCapabilitiesChanged(null) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) - verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false) - verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, - NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent, - FLAG_IMMUTABLE) - - // No downstream. - notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Wifi downstream again. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) - verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false) - verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, - NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent, - FLAG_IMMUTABLE) - - // Set R.bool.config_upstream_roaming_notification to false and change upstream - // network to roaming state again. No roaming notification. - doReturn(false).`when`(testResources) - .getBoolean(R.bool.config_upstream_roaming_notification) - notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - } - - @Test - fun testGetSettingsPackageName() { - val defaultSettingsPackageName = "com.android.settings" - val testSettingsPackageName = "com.android.test.settings" - val pm = mock(PackageManager::class.java) - doReturn(null).`when`(pm).resolveActivity(any(), anyInt()) - assertEquals(defaultSettingsPackageName, getSettingsPackageName(pm)) - - val resolveInfo = ResolveInfo().apply { - activityInfo = ActivityInfo().apply { - name = "test" - applicationInfo = ApplicationInfo().apply { - packageName = testSettingsPackageName - } - } - } - doReturn(resolveInfo).`when`(pm).resolveActivity(any(), anyInt()) - assertEquals(testSettingsPackageName, getSettingsPackageName(pm)) - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java deleted file mode 100644 index 7bba67b05f88..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.Manifest.permission.ACCESS_NETWORK_STATE; -import static android.Manifest.permission.TETHER_PRIVILEGED; -import static android.Manifest.permission.WRITE_SETTINGS; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; -import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.app.UiAutomation; -import android.content.Intent; -import android.net.IIntResultListener; -import android.net.ITetheringConnector; -import android.net.ITetheringEventCallback; -import android.net.TetheringRequestParcel; -import android.os.Bundle; -import android.os.Handler; -import android.os.ResultReceiver; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.rule.ServiceTestRule; -import androidx.test.runner.AndroidJUnit4; - -import com.android.networkstack.tethering.MockTetheringService.MockTetheringConnector; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public final class TetheringServiceTest { - private static final String TEST_IFACE_NAME = "test_wlan0"; - private static final String TEST_CALLER_PKG = "com.android.shell"; - private static final String TEST_ATTRIBUTION_TAG = null; - @Mock private ITetheringEventCallback mITetheringEventCallback; - @Rule public ServiceTestRule mServiceTestRule; - private Tethering mTethering; - private Intent mMockServiceIntent; - private ITetheringConnector mTetheringConnector; - private UiAutomation mUiAutomation; - - private class TestTetheringResult extends IIntResultListener.Stub { - private int mResult = -1; // Default value that does not match any result code. - @Override - public void onResult(final int resultCode) { - mResult = resultCode; - } - - public void assertResult(final int expected) { - assertEquals(expected, mResult); - } - } - - private class MyResultReceiver extends ResultReceiver { - MyResultReceiver(Handler handler) { - super(handler); - } - private int mResult = -1; // Default value that does not match any result code. - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - mResult = resultCode; - } - - public void assertResult(int expected) { - assertEquals(expected, mResult); - } - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - mUiAutomation = - InstrumentationRegistry.getInstrumentation().getUiAutomation(); - mServiceTestRule = new ServiceTestRule(); - mMockServiceIntent = new Intent( - InstrumentationRegistry.getTargetContext(), - MockTetheringService.class); - final MockTetheringConnector mockConnector = - (MockTetheringConnector) mServiceTestRule.bindService(mMockServiceIntent); - mTetheringConnector = mockConnector.getTetheringConnector(); - final MockTetheringService service = mockConnector.getService(); - mTethering = service.getTethering(); - } - - @After - public void tearDown() throws Exception { - mServiceTestRule.unbindService(); - mUiAutomation.dropShellPermissionIdentity(); - } - - private interface TestTetheringCall { - void runTetheringCall(TestTetheringResult result) throws Exception; - } - - private void runAsNoPermission(final TestTetheringCall test) throws Exception { - runTetheringCall(test, new String[0]); - } - - private void runAsTetherPrivileged(final TestTetheringCall test) throws Exception { - runTetheringCall(test, TETHER_PRIVILEGED); - } - - private void runAsAccessNetworkState(final TestTetheringCall test) throws Exception { - runTetheringCall(test, ACCESS_NETWORK_STATE); - } - - private void runAsWriteSettings(final TestTetheringCall test) throws Exception { - runTetheringCall(test, WRITE_SETTINGS); - } - - private void runTetheringCall(final TestTetheringCall test, String... permissions) - throws Exception { - if (permissions.length > 0) mUiAutomation.adoptShellPermissionIdentity(permissions); - try { - when(mTethering.isTetheringSupported()).thenReturn(true); - test.runTetheringCall(new TestTetheringResult()); - } finally { - mUiAutomation.dropShellPermissionIdentity(); - } - } - - private void verifyNoMoreInteractionsForTethering() { - verifyNoMoreInteractions(mTethering); - verifyNoMoreInteractions(mITetheringEventCallback); - reset(mTethering, mITetheringEventCallback); - } - - private void runTether(final TestTetheringResult result) throws Exception { - when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); - mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).tether(TEST_IFACE_NAME); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testTether() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runTether(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runTether(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runUnTether(final TestTetheringResult result) throws Exception { - when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); - mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).untether(TEST_IFACE_NAME); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testUntether() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runUnTether(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runUnTether(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runSetUsbTethering(final TestTetheringResult result) throws Exception { - when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR); - mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, - TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).setUsbTethering(true /* enable */); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testSetUsbTethering() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, - TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runSetUsbTethering(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runSetUsbTethering(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - - } - - private void runStartTethering(final TestTetheringResult result, - final TetheringRequestParcel request) throws Exception { - mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).startTethering(eq(request), eq(result)); - } - - @Test - public void testStartTethering() throws Exception { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = TETHERING_WIFI; - - runAsNoPermission((result) -> { - mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runStartTethering(result, request); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runStartTethering(result, request); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runStartTetheringAndVerifyNoPermission(final TestTetheringResult result) - throws Exception { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = TETHERING_WIFI; - request.exemptFromEntitlementCheck = true; - mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - } - - @Test - public void testFailToBypassEntitlementWithoutNeworkStackPermission() throws Exception { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = TETHERING_WIFI; - request.exemptFromEntitlementCheck = true; - - runAsNoPermission((result) -> { - runStartTetheringAndVerifyNoPermission(result); - }); - - runAsTetherPrivileged((result) -> { - runStartTetheringAndVerifyNoPermission(result); - }); - - runAsWriteSettings((result) -> { - runStartTetheringAndVerifyNoPermission(result); - }); - } - - private void runStopTethering(final TestTetheringResult result) throws Exception { - mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, - TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).stopTethering(TETHERING_WIFI); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testStopTethering() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, - TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runStopTethering(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runStopTethering(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runRequestLatestTetheringEntitlementResult() throws Exception { - final MyResultReceiver result = new MyResultReceiver(null); - mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, - true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG); - verify(mTethering).isTetheringSupported(); - verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI), - eq(result), eq(true) /* showEntitlementUi */); - } - - @Test - public void testRequestLatestTetheringEntitlementResult() throws Exception { - // Run as no permission. - final MyResultReceiver result = new MyResultReceiver(null); - mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, - true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractions(mTethering); - - runAsTetherPrivileged((none) -> { - runRequestLatestTetheringEntitlementResult(); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((none) -> { - runRequestLatestTetheringEntitlementResult(); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runRegisterTetheringEventCallback() throws Exception { - mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback, - TEST_CALLER_PKG); - verify(mTethering).registerTetheringEventCallback(eq(mITetheringEventCallback)); - } - - @Test - public void testRegisterTetheringEventCallback() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback, - TEST_CALLER_PKG); - verify(mITetheringEventCallback).onCallbackStopped( - TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((none) -> { - runRegisterTetheringEventCallback(); - verifyNoMoreInteractionsForTethering(); - }); - - runAsAccessNetworkState((none) -> { - runRegisterTetheringEventCallback(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runUnregisterTetheringEventCallback() throws Exception { - mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback, - TEST_CALLER_PKG); - verify(mTethering).unregisterTetheringEventCallback(eq(mITetheringEventCallback)); - } - - @Test - public void testUnregisterTetheringEventCallback() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback, - TEST_CALLER_PKG); - verify(mITetheringEventCallback).onCallbackStopped( - TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((none) -> { - runUnregisterTetheringEventCallback(); - verifyNoMoreInteractionsForTethering(); - }); - - runAsAccessNetworkState((none) -> { - runUnregisterTetheringEventCallback(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runStopAllTethering(final TestTetheringResult result) throws Exception { - mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).untetherAll(); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testStopAllTethering() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runStopAllTethering(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runStopAllTethering(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runIsTetheringSupported(final TestTetheringResult result) throws Exception { - mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetheringSupported(); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testIsTetheringSupported() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runIsTetheringSupported(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runIsTetheringSupported(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java deleted file mode 100644 index 114cb7ca6ec7..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ /dev/null @@ -1,2019 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.content.pm.PackageManager.GET_ACTIVITIES; -import static android.hardware.usb.UsbManager.USB_CONFIGURED; -import static android.hardware.usb.UsbManager.USB_CONNECTED; -import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; -import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; -import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; -import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; -import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; -import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; -import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; -import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; -import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; -import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_NCM; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED; -import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; -import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; - -import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; -import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; -import static com.android.networkstack.tethering.UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.notNull; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.app.usage.NetworkStatsManager; -import android.bluetooth.BluetoothAdapter; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.hardware.usb.UsbManager; -import android.net.ConnectivityManager; -import android.net.EthernetManager; -import android.net.EthernetManager.TetheredInterfaceCallback; -import android.net.EthernetManager.TetheredInterfaceRequest; -import android.net.IIntResultListener; -import android.net.INetd; -import android.net.ITetheringEventCallback; -import android.net.InetAddresses; -import android.net.InterfaceConfigurationParcel; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.MacAddress; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.RouteInfo; -import android.net.TetherStatesParcel; -import android.net.TetheredClient; -import android.net.TetheringCallbackStartedParcel; -import android.net.TetheringConfigurationParcel; -import android.net.TetheringRequestParcel; -import android.net.dhcp.DhcpServerCallbacks; -import android.net.dhcp.DhcpServingParamsParcel; -import android.net.dhcp.IDhcpServer; -import android.net.ip.DadProxy; -import android.net.ip.IpNeighborMonitor; -import android.net.ip.IpServer; -import android.net.ip.RouterAdvertisementDaemon; -import android.net.util.InterfaceParams; -import android.net.util.NetworkConstants; -import android.net.util.SharedLog; -import android.net.wifi.SoftApConfiguration; -import android.net.wifi.WifiManager; -import android.net.wifi.p2p.WifiP2pGroup; -import android.net.wifi.p2p.WifiP2pInfo; -import android.net.wifi.p2p.WifiP2pManager; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.PersistableBundle; -import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; -import android.os.test.TestLooper; -import android.provider.Settings; -import android.telephony.CarrierConfigManager; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; -import android.test.mock.MockContentResolver; - -import androidx.annotation.NonNull; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.StateMachine; -import com.android.internal.util.test.BroadcastInterceptingContext; -import com.android.internal.util.test.FakeSettingsProvider; -import com.android.testutils.MiscAsserts; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Vector; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class TetheringTest { - private static final int IFINDEX_OFFSET = 100; - - private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0"; - private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0"; - private static final String TEST_USB_IFNAME = "test_rndis0"; - private static final String TEST_WIFI_IFNAME = "test_wlan0"; - private static final String TEST_WLAN_IFNAME = "test_wlan1"; - private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; - private static final String TEST_NCM_IFNAME = "test_ncm0"; - private static final String TEST_ETH_IFNAME = "test_eth0"; - private static final String TEST_BT_IFNAME = "test_pan0"; - private static final String TETHERING_NAME = "Tethering"; - private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; - private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - - private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; - - @Mock private ApplicationInfo mApplicationInfo; - @Mock private Context mContext; - @Mock private NetworkStatsManager mStatsManager; - @Mock private OffloadHardwareInterface mOffloadHardwareInterface; - @Mock private OffloadHardwareInterface.ForwardedStats mForwardedStats; - @Mock private Resources mResources; - @Mock private TelephonyManager mTelephonyManager; - @Mock private UsbManager mUsbManager; - @Mock private WifiManager mWifiManager; - @Mock private CarrierConfigManager mCarrierConfigManager; - @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor; - @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator; - @Mock private DadProxy mDadProxy; - @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; - @Mock private IpNeighborMonitor mIpNeighborMonitor; - @Mock private IDhcpServer mDhcpServer; - @Mock private INetd mNetd; - @Mock private UserManager mUserManager; - @Mock private NetworkRequest mNetworkRequest; - @Mock private ConnectivityManager mCm; - @Mock private EthernetManager mEm; - @Mock private TetheringNotificationUpdater mNotificationUpdater; - @Mock private BpfCoordinator mBpfCoordinator; - @Mock private PackageManager mPackageManager; - - private final MockIpServerDependencies mIpServerDependencies = - spy(new MockIpServerDependencies()); - private final MockTetheringDependencies mTetheringDependencies = - new MockTetheringDependencies(); - - // Like so many Android system APIs, these cannot be mocked because it is marked final. - // We have to use the real versions. - private final PersistableBundle mCarrierConfig = new PersistableBundle(); - private final TestLooper mLooper = new TestLooper(); - - private Vector mIntents; - private BroadcastInterceptingContext mServiceContext; - private MockContentResolver mContentResolver; - private BroadcastReceiver mBroadcastReceiver; - private Tethering mTethering; - private PhoneStateListener mPhoneStateListener; - private InterfaceConfigurationParcel mInterfaceConfiguration; - private TetheringConfiguration mConfig; - private EntitlementManager mEntitleMgr; - private OffloadController mOffloadCtrl; - private PrivateAddressCoordinator mPrivateAddressCoordinator; - - private class TestContext extends BroadcastInterceptingContext { - TestContext(Context base) { - super(base); - } - - @Override - public ApplicationInfo getApplicationInfo() { - return mApplicationInfo; - } - - @Override - public ContentResolver getContentResolver() { - return mContentResolver; - } - - @Override - public String getPackageName() { - return "TetheringTest"; - } - - @Override - public Resources getResources() { - return mResources; - } - - @Override - public Object getSystemService(String name) { - if (Context.WIFI_SERVICE.equals(name)) return mWifiManager; - if (Context.USB_SERVICE.equals(name)) return mUsbManager; - if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; - if (Context.USER_SERVICE.equals(name)) return mUserManager; - if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager; - if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm; - if (Context.ETHERNET_SERVICE.equals(name)) return mEm; - return super.getSystemService(name); - } - - @Override - public PackageManager getPackageManager() { - return mPackageManager; - } - - @Override - public String getSystemServiceName(Class serviceClass) { - if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE; - return super.getSystemServiceName(serviceClass); - } - } - - public class MockIpServerDependencies extends IpServer.Dependencies { - @Override - public DadProxy getDadProxy( - Handler handler, InterfaceParams ifParams) { - return mDadProxy; - } - - @Override - public RouterAdvertisementDaemon getRouterAdvertisementDaemon( - InterfaceParams ifParams) { - return mRouterAdvertisementDaemon; - } - - @Override - public InterfaceParams getInterfaceParams(String ifName) { - assertTrue("Non-mocked interface " + ifName, - ifName.equals(TEST_USB_IFNAME) - || ifName.equals(TEST_WLAN_IFNAME) - || ifName.equals(TEST_MOBILE_IFNAME) - || ifName.equals(TEST_P2P_IFNAME) - || ifName.equals(TEST_NCM_IFNAME) - || ifName.equals(TEST_ETH_IFNAME)); - final String[] ifaces = new String[] { - TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME, - TEST_NCM_IFNAME, TEST_ETH_IFNAME}; - return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET, - MacAddress.ALL_ZEROS_ADDRESS); - } - - @Override - public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, - DhcpServerCallbacks cb) { - new Thread(() -> { - try { - cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer); - } catch (RemoteException e) { - fail(e.getMessage()); - } - }).run(); - } - - public IpNeighborMonitor getIpNeighborMonitor(Handler h, SharedLog l, - IpNeighborMonitor.NeighborEventConsumer c) { - return mIpNeighborMonitor; - } - } - - // MyTetheringConfiguration is used to override static method for testing. - private class MyTetheringConfiguration extends TetheringConfiguration { - MyTetheringConfiguration(Context ctx, SharedLog log, int id) { - super(ctx, log, id); - } - - @Override - protected String getDeviceConfigProperty(final String name) { - return null; - } - - @Override - protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { - return mResources; - } - } - - public class MockTetheringDependencies extends TetheringDependencies { - StateMachine mUpstreamNetworkMonitorSM; - ArrayList mIpv6CoordinatorNotifyList; - - public void reset() { - mUpstreamNetworkMonitorSM = null; - mIpv6CoordinatorNotifyList = null; - } - - @Override - public BpfCoordinator getBpfCoordinator( - BpfCoordinator.Dependencies deps) { - return mBpfCoordinator; - } - - @Override - public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) { - return mOffloadHardwareInterface; - } - - @Override - public OffloadController getOffloadController(Handler h, SharedLog log, - OffloadController.Dependencies deps) { - mOffloadCtrl = spy(super.getOffloadController(h, log, deps)); - // Return real object here instead of mock because - // testReportFailCallbackIfOffloadNotSupported depend on real OffloadController object. - return mOffloadCtrl; - } - - @Override - public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, - StateMachine target, SharedLog log, int what) { - mUpstreamNetworkMonitorSM = target; - return mUpstreamNetworkMonitor; - } - - @Override - public IPv6TetheringCoordinator getIPv6TetheringCoordinator( - ArrayList notifyList, SharedLog log) { - mIpv6CoordinatorNotifyList = notifyList; - return mIPv6TetheringCoordinator; - } - - @Override - public IpServer.Dependencies getIpServerDependencies() { - return mIpServerDependencies; - } - - @Override - public NetworkRequest getDefaultNetworkRequest() { - return mNetworkRequest; - } - - @Override - public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log, - Runnable callback) { - mEntitleMgr = spy(super.getEntitlementManager(ctx, h, log, callback)); - return mEntitleMgr; - } - - @Override - public boolean isTetheringSupported() { - return true; - } - - @Override - public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log, - int subId) { - mConfig = spy(new MyTetheringConfiguration(ctx, log, subId)); - return mConfig; - } - - @Override - public INetd getINetd(Context context) { - return mNetd; - } - - @Override - public Looper getTetheringLooper() { - return mLooper.getLooper(); - } - - @Override - public Context getContext() { - return mServiceContext; - } - - @Override - public BluetoothAdapter getBluetoothAdapter() { - // TODO: add test for bluetooth tethering. - return null; - } - - @Override - public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) { - return mNotificationUpdater; - } - - @Override - public boolean isTetheringDenied() { - return false; - } - - - @Override - public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx, - TetheringConfiguration cfg) { - mPrivateAddressCoordinator = super.getPrivateAddressCoordinator(ctx, cfg); - return mPrivateAddressCoordinator; - } - } - - private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4, - boolean withIPv6, boolean with464xlat) { - final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(TEST_MOBILE_IFNAME); - - if (withIPv4) { - prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), - InetAddresses.parseNumericAddress("10.0.0.1"), - TEST_MOBILE_IFNAME, RTN_UNICAST)); - } - - if (withIPv6) { - prop.addDnsServer(InetAddresses.parseNumericAddress("2001:db8::2")); - prop.addLinkAddress( - new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"), - NetworkConstants.RFC7421_PREFIX_LENGTH)); - prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), - InetAddresses.parseNumericAddress("2001:db8::1"), - TEST_MOBILE_IFNAME, RTN_UNICAST)); - } - - if (with464xlat) { - final LinkProperties stackedLink = new LinkProperties(); - stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME); - stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), - InetAddresses.parseNumericAddress("192.0.0.1"), - TEST_XLAT_MOBILE_IFNAME, RTN_UNICAST)); - - prop.addStackedLink(stackedLink); - } - - - final NetworkCapabilities capabilities = new NetworkCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - return new UpstreamNetworkState(prop, capabilities, new Network(100)); - } - - private static UpstreamNetworkState buildMobileIPv4UpstreamState() { - return buildMobileUpstreamState(true, false, false); - } - - private static UpstreamNetworkState buildMobileIPv6UpstreamState() { - return buildMobileUpstreamState(false, true, false); - } - - private static UpstreamNetworkState buildMobileDualStackUpstreamState() { - return buildMobileUpstreamState(true, true, false); - } - - private static UpstreamNetworkState buildMobile464xlatUpstreamState() { - return buildMobileUpstreamState(false, true, true); - } - - // See FakeSettingsProvider#clearSettingsProvider() that this needs to be called before and - // after use. - @BeforeClass - public static void setupOnce() { - FakeSettingsProvider.clearSettingsProvider(); - } - - @AfterClass - public static void tearDownOnce() { - FakeSettingsProvider.clearSettingsProvider(); - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(mResources.getStringArray(R.array.config_tether_dhcp_range)) - .thenReturn(new String[0]); - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - false); - when(mNetd.interfaceGetList()) - .thenReturn(new String[] { - TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME, - TEST_NCM_IFNAME, TEST_ETH_IFNAME}); - when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); - mInterfaceConfiguration = new InterfaceConfigurationParcel(); - mInterfaceConfiguration.flags = new String[0]; - when(mRouterAdvertisementDaemon.start()) - .thenReturn(true); - initOffloadConfiguration(true /* offloadConfig */, true /* offloadControl */, - 0 /* defaultDisabled */); - when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats); - - mServiceContext = new TestContext(mContext); - mContentResolver = new MockContentResolver(mServiceContext); - mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); - setTetheringSupported(true /* supported */); - mIntents = new Vector<>(); - mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - mIntents.addElement(intent); - } - }; - mServiceContext.registerReceiver(mBroadcastReceiver, - new IntentFilter(ACTION_TETHER_STATE_CHANGED)); - mTethering = makeTethering(); - verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any()); - verify(mNetd).registerUnsolicitedEventListener(any()); - final ArgumentCaptor phoneListenerCaptor = - ArgumentCaptor.forClass(PhoneStateListener.class); - verify(mTelephonyManager).listen(phoneListenerCaptor.capture(), - eq(PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)); - verify(mWifiManager).registerSoftApCallback(any(), any()); - mPhoneStateListener = phoneListenerCaptor.getValue(); - } - - private void setTetheringSupported(final boolean supported) { - Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED, - supported ? 1 : 0); - when(mUserManager.hasUserRestriction( - UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(!supported); - // Setup tetherable configuration. - when(mResources.getStringArray(R.array.config_tether_usb_regexs)) - .thenReturn(new String[] { "test_rndis\\d" }); - when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) - .thenReturn(new String[]{ "test_wlan\\d" }); - when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) - .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); - when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) - .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) - .thenReturn(new String[] { "test_ncm\\d" }); - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); - when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true); - } - - private void initTetheringUpstream(UpstreamNetworkState upstreamState) { - when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); - } - - private Tethering makeTethering() { - mTetheringDependencies.reset(); - return new Tethering(mTetheringDependencies); - } - - private TetheringRequestParcel createTetheringRequestParcel(final int type) { - return createTetheringRequestParcel(type, null, null, false); - } - - private TetheringRequestParcel createTetheringRequestParcel(final int type, - final LinkAddress serverAddr, final LinkAddress clientAddr, final boolean exempt) { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = type; - request.localIPv4Address = serverAddr; - request.staticClientAddress = clientAddr; - request.exemptFromEntitlementCheck = exempt; - request.showProvisioningUi = false; - - return request; - } - - @After - public void tearDown() { - mServiceContext.unregisterReceiver(mBroadcastReceiver); - } - - private void sendWifiApStateChanged(int state) { - final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); - intent.putExtra(EXTRA_WIFI_AP_STATE, state); - mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - private void sendWifiApStateChanged(int state, String ifname, int ipmode) { - final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); - intent.putExtra(EXTRA_WIFI_AP_STATE, state); - intent.putExtra(EXTRA_WIFI_AP_INTERFACE_NAME, ifname); - intent.putExtra(EXTRA_WIFI_AP_MODE, ipmode); - mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - private static final String[] P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST = { - android.Manifest.permission.ACCESS_FINE_LOCATION, - android.Manifest.permission.ACCESS_WIFI_STATE - }; - - private void sendWifiP2pConnectionChanged( - boolean isGroupFormed, boolean isGroupOwner, String ifname) { - WifiP2pGroup group = null; - WifiP2pInfo p2pInfo = new WifiP2pInfo(); - p2pInfo.groupFormed = isGroupFormed; - if (isGroupFormed) { - p2pInfo.isGroupOwner = isGroupOwner; - group = mock(WifiP2pGroup.class); - when(group.isGroupOwner()).thenReturn(isGroupOwner); - when(group.getInterface()).thenReturn(ifname); - } - - final Intent intent = mock(Intent.class); - when(intent.getAction()).thenReturn(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); - when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO)).thenReturn(p2pInfo); - when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)).thenReturn(group); - - mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL, - P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST); - } - - private void sendUsbBroadcast(boolean connected, boolean configured, boolean function, - int type) { - final Intent intent = new Intent(UsbManager.ACTION_USB_STATE); - intent.putExtra(USB_CONNECTED, connected); - intent.putExtra(USB_CONFIGURED, configured); - if (type == TETHERING_USB) { - intent.putExtra(USB_FUNCTION_RNDIS, function); - } else { - intent.putExtra(USB_FUNCTION_NCM, function); - } - mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - private void sendConfigurationChanged() { - final Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); - mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - private void verifyInterfaceServingModeStarted(String ifname) throws Exception { - verify(mNetd, times(1)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, times(1)).tetherInterfaceAdd(ifname); - verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, ifname); - verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(ifname), - anyString(), anyString()); - } - - private void verifyTetheringBroadcast(String ifname, String whichExtra) { - // Verify that ifname is in the whichExtra array of the tether state changed broadcast. - final Intent bcast = mIntents.get(0); - assertEquals(ACTION_TETHER_STATE_CHANGED, bcast.getAction()); - final ArrayList ifnames = bcast.getStringArrayListExtra(whichExtra); - assertTrue(ifnames.contains(ifname)); - mIntents.remove(bcast); - } - - public void failingLocalOnlyHotspotLegacyApBroadcast( - boolean emulateInterfaceStatusChanged) throws Exception { - // Emulate externally-visible WifiManager effects, causing the - // per-interface state machine to start up, and telling us that - // hotspot mode is to be started. - if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - } - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); - mLooper.dispatchAll(); - - // If, and only if, Tethering received an interface status changed then - // it creates a IpServer and sends out a broadcast indicating that the - // interface is "available". - if (emulateInterfaceStatusChanged) { - // There is 1 IpServer state change event: STATE_AVAILABLE - verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - } - verifyNoMoreInteractions(mNetd); - verifyNoMoreInteractions(mWifiManager); - } - - private void prepareNcmTethering() { - // Emulate startTethering(TETHERING_NCM) called - mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), null); - mLooper.dispatchAll(); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM); - - mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true); - } - - private void prepareUsbTethering(UpstreamNetworkState upstreamState) { - initTetheringUpstream(upstreamState); - - // Emulate pressing the USB tethering button in Settings UI. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null); - mLooper.dispatchAll(); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - - mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); - } - - @Test - public void testUsbConfiguredBroadcastStartsTethering() throws Exception { - UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - prepareUsbTethering(upstreamState); - - // This should produce no activity of any kind. - verifyNoMoreInteractions(mNetd); - - // Pretend we then receive USB configured broadcast. - sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); - // Now we should see the start of tethering mechanics (in this case: - // tetherMatchingInterfaces() which starts by fetching all interfaces). - verify(mNetd, times(1)).interfaceGetList(); - - // UpstreamNetworkMonitor should receive selected upstream - verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream(); - verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); - } - - @Test - public void failingLocalOnlyHotspotLegacyApBroadcastWithIfaceStatusChanged() throws Exception { - failingLocalOnlyHotspotLegacyApBroadcast(true); - } - - @Test - public void failingLocalOnlyHotspotLegacyApBroadcastSansIfaceStatusChanged() throws Exception { - failingLocalOnlyHotspotLegacyApBroadcast(false); - } - - public void workingLocalOnlyHotspotEnrichedApBroadcast( - boolean emulateInterfaceStatusChanged) throws Exception { - // Emulate externally-visible WifiManager effects, causing the - // per-interface state machine to start up, and telling us that - // hotspot mode is to be started. - if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - } - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY); - mLooper.dispatchAll(); - - verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); - verify(mNetd, times(1)).tetherStartWithConfiguration(any()); - verifyNoMoreInteractions(mNetd); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY); - verifyNoMoreInteractions(mWifiManager); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY); - verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); - // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY - verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE); - - // Emulate externally-visible WifiManager effects, when hotspot mode - // is being torn down. - sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); - mTethering.interfaceRemoved(TEST_WLAN_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); - verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); - // interfaceSetCfg() called once for enabling and twice disabling IPv4. - verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, times(1)).tetherStop(); - verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); - verify(mWifiManager, times(3)).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNetd); - verifyNoMoreInteractions(mWifiManager); - // Asking for the last error after the per-interface state machine - // has been reaped yields an unknown interface error. - assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME)); - } - - /** - * Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator. - */ - private void sendIPv6TetherUpdates(UpstreamNetworkState upstreamState) { - // IPv6TetheringCoordinator must have been notified of downstream - verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream( - argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)), - eq(IpServer.STATE_TETHERED)); - - for (IpServer ipSrv : mTetheringDependencies.mIpv6CoordinatorNotifyList) { - UpstreamNetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false); - ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, - upstreamState.linkProperties.isIpv6Provisioned() - ? ipv6OnlyState.linkProperties - : null); - } - mLooper.dispatchAll(); - } - - private void runUsbTethering(UpstreamNetworkState upstreamState) { - prepareUsbTethering(upstreamState); - sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); - } - - @Test - public void workingMobileUsbTethering_IPv4() throws Exception { - UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - runUsbTethering(upstreamState); - - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - - sendIPv6TetherUpdates(upstreamState); - verify(mDadProxy, never()).setUpstreamIface(notNull()); - verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull()); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - } - - @Test - public void workingMobileUsbTethering_IPv4LegacyDhcp() { - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - true); - sendConfigurationChanged(); - final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - runUsbTethering(upstreamState); - sendIPv6TetherUpdates(upstreamState); - - verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any()); - } - - @Test - public void workingMobileUsbTethering_IPv6() throws Exception { - UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState(); - runUsbTethering(upstreamState); - - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - - sendIPv6TetherUpdates(upstreamState); - // TODO: add interfaceParams to compare in verify. - verify(mDadProxy, times(1)).setUpstreamIface(notNull()); - verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - } - - @Test - public void workingMobileUsbTethering_DualStack() throws Exception { - UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); - runUsbTethering(upstreamState); - - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mRouterAdvertisementDaemon, times(1)).start(); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - - sendIPv6TetherUpdates(upstreamState); - verify(mDadProxy, times(1)).setUpstreamIface(notNull()); - verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - } - - @Test - public void workingMobileUsbTethering_MultipleUpstreams() throws Exception { - UpstreamNetworkState upstreamState = buildMobile464xlatUpstreamState(); - runUsbTethering(upstreamState); - - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - - sendIPv6TetherUpdates(upstreamState); - verify(mDadProxy, times(1)).setUpstreamIface(notNull()); - verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - } - - @Test - public void workingMobileUsbTethering_v6Then464xlat() throws Exception { - // Setup IPv6 - UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState(); - runUsbTethering(upstreamState); - - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - - // Then 464xlat comes up - upstreamState = buildMobile464xlatUpstreamState(); - initTetheringUpstream(upstreamState); - - // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES. - mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage( - Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK, - UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, - 0, - upstreamState); - mLooper.dispatchAll(); - - // Forwarding is added for 464xlat - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - // Forwarding was not re-added for v6 (still times(1)) - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - // DHCP not restarted on downstream (still times(1)) - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - } - - @Test - public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception { - when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true); - sendConfigurationChanged(); - - // Setup IPv6 - final UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState(); - runUsbTethering(upstreamState); - - // UpstreamNetworkMonitor should choose upstream automatically - // (in this specific case: choose the default network). - verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream(); - verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any()); - - verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); - } - - private void runNcmTethering() { - prepareNcmTethering(); - sendUsbBroadcast(true, true, true, TETHERING_NCM); - mLooper.dispatchAll(); - } - - @Test - public void workingNcmTethering() throws Exception { - runNcmTethering(); - - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - } - - @Test - public void workingNcmTethering_LegacyDhcp() { - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - true); - sendConfigurationChanged(); - runNcmTethering(); - - verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any()); - } - - @Test - public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception { - workingLocalOnlyHotspotEnrichedApBroadcast(true); - } - - @Test - public void workingLocalOnlyHotspotEnrichedApBroadcastSansIfaceChanged() throws Exception { - workingLocalOnlyHotspotEnrichedApBroadcast(false); - } - - // TODO: Test with and without interfaceStatusChanged(). - @Test - public void failingWifiTetheringLegacyApBroadcast() throws Exception { - when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); - - // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); - mLooper.dispatchAll(); - verify(mWifiManager, times(1)).startTetheredHotspot(null); - verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNetd); - - // Emulate externally-visible WifiManager effects, causing the - // per-interface state machine to start up, and telling us that - // tethering mode is to be started. - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); - mLooper.dispatchAll(); - - // There is 1 IpServer state change event: STATE_AVAILABLE - verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNetd); - verifyNoMoreInteractions(mWifiManager); - } - - // TODO: Test with and without interfaceStatusChanged(). - @Test - public void workingWifiTetheringEnrichedApBroadcast() throws Exception { - when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); - - // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); - mLooper.dispatchAll(); - verify(mWifiManager, times(1)).startTetheredHotspot(null); - verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNetd); - - // Emulate externally-visible WifiManager effects, causing the - // per-interface state machine to start up, and telling us that - // tethering mode is to be started. - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); - mLooper.dispatchAll(); - - verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); - verify(mNetd, times(1)).tetherStartWithConfiguration(any()); - verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME), - anyString(), anyString()); - verifyNoMoreInteractions(mNetd); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED); - verifyNoMoreInteractions(mWifiManager); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER); - verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); - // In tethering mode, in the default configuration, an explicit request - // for a mobile network is also made. - verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest(); - // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_TETHERED - verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE); - verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI)); - - ///// - // We do not currently emulate any upstream being found. - // - // This is why there are no calls to verify mNetd.tetherAddForward() or - // mNetd.ipfwdAddInterfaceForward(). - ///// - - // Emulate pressing the WiFi tethering button. - mTethering.stopTethering(TETHERING_WIFI); - mLooper.dispatchAll(); - verify(mWifiManager, times(1)).stopSoftAp(); - verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNetd); - - // Emulate externally-visible WifiManager effects, when tethering mode - // is being torn down. - sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); - mTethering.interfaceRemoved(TEST_WLAN_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); - verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); - // interfaceSetCfg() called once for enabling and twice for disabling IPv4. - verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, times(1)).tetherStop(); - verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); - verify(mWifiManager, times(3)).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNetd); - verifyNoMoreInteractions(mWifiManager); - // Asking for the last error after the per-interface state machine - // has been reaped yields an unknown interface error. - assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME)); - } - - // TODO: Test with and without interfaceStatusChanged(). - @Test - public void failureEnablingIpForwarding() throws Exception { - when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); - doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME); - - // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); - mLooper.dispatchAll(); - verify(mWifiManager, times(1)).startTetheredHotspot(null); - verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNetd); - - // Emulate externally-visible WifiManager effects, causing the - // per-interface state machine to start up, and telling us that - // tethering mode is to be started. - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); - mLooper.dispatchAll(); - - // We verify get/set called three times here: twice for setup and once during - // teardown because all events happen over the course of the single - // dispatchAll() above. Note that once the IpServer IPv4 address config - // code is refactored the two calls during shutdown will revert to one. - verify(mNetd, times(3)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName))); - verify(mNetd, times(1)).tetherInterfaceAdd(TEST_WLAN_IFNAME); - verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); - verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME), - anyString(), anyString()); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED); - // There are 3 IpServer state change event: - // STATE_AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE. - verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE); - verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI)); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - // This is called, but will throw. - verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); - // This never gets called because of the exception thrown above. - verify(mNetd, times(0)).tetherStartWithConfiguration(any()); - // When the main state machine transitions to an error state it tells - // downstream interfaces, which causes us to tell Wi-Fi about the error - // so it can take down AP mode. - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); - verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); - - verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNetd); - } - - private void runUserRestrictionsChange( - boolean currentDisallow, boolean nextDisallow, boolean isTetheringActive, - int expectedInteractionsWithShowNotification) throws Exception { - final Bundle newRestrictions = new Bundle(); - newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow); - final Tethering mockTethering = mock(Tethering.class); - when(mockTethering.isTetheringActive()).thenReturn(isTetheringActive); - when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions); - - final Tethering.UserRestrictionActionListener ural = - new Tethering.UserRestrictionActionListener( - mUserManager, mockTethering, mNotificationUpdater); - ural.mDisallowTethering = currentDisallow; - - ural.onUserRestrictionsChanged(); - - verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification)) - .notifyTetheringDisabledByRestriction(); - verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll(); - } - - @Test - public void testDisallowTetheringWhenTetheringIsNotActive() throws Exception { - final boolean isTetheringActive = false; - final boolean currDisallow = false; - final boolean nextDisallow = true; - final int expectedInteractionsWithShowNotification = 0; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - } - - @Test - public void testDisallowTetheringWhenTetheringIsActive() throws Exception { - final boolean isTetheringActive = true; - final boolean currDisallow = false; - final boolean nextDisallow = true; - final int expectedInteractionsWithShowNotification = 1; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - } - - @Test - public void testAllowTetheringWhenTetheringIsNotActive() throws Exception { - final boolean isTetheringActive = false; - final boolean currDisallow = true; - final boolean nextDisallow = false; - final int expectedInteractionsWithShowNotification = 0; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - } - - @Test - public void testAllowTetheringWhenTetheringIsActive() throws Exception { - final boolean isTetheringActive = true; - final boolean currDisallow = true; - final boolean nextDisallow = false; - final int expectedInteractionsWithShowNotification = 0; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - } - - @Test - public void testDisallowTetheringUnchanged() throws Exception { - final boolean isTetheringActive = true; - final int expectedInteractionsWithShowNotification = 0; - boolean currDisallow = true; - boolean nextDisallow = true; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - - currDisallow = false; - nextDisallow = false; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - } - - private class TestTetheringEventCallback extends ITetheringEventCallback.Stub { - private final ArrayList mActualUpstreams = new ArrayList<>(); - private final ArrayList mTetheringConfigs = - new ArrayList<>(); - private final ArrayList mTetherStates = new ArrayList<>(); - private final ArrayList mOffloadStatus = new ArrayList<>(); - - // This function will remove the recorded callbacks, so it must be called once for - // each callback. If this is called after multiple callback, the order matters. - // onCallbackCreated counts as the first call to expectUpstreamChanged with - // @see onCallbackCreated. - public void expectUpstreamChanged(Network... networks) { - if (networks == null) { - assertNoUpstreamChangeCallback(); - return; - } - - final ArrayList expectedUpstreams = - new ArrayList(Arrays.asList(networks)); - for (Network upstream : expectedUpstreams) { - // throws OOB if no expectations - assertEquals(mActualUpstreams.remove(0), upstream); - } - assertNoUpstreamChangeCallback(); - } - - // This function will remove the recorded callbacks, so it must be called once - // for each callback. If this is called after multiple callback, the order matters. - // onCallbackCreated counts as the first call to onConfigurationChanged with - // @see onCallbackCreated. - public void expectConfigurationChanged(TetheringConfigurationParcel... tetherConfigs) { - final ArrayList expectedTetherConfig = - new ArrayList(Arrays.asList(tetherConfigs)); - for (TetheringConfigurationParcel config : expectedTetherConfig) { - // throws OOB if no expectations - final TetheringConfigurationParcel actualConfig = mTetheringConfigs.remove(0); - assertTetherConfigParcelEqual(actualConfig, config); - } - assertNoConfigChangeCallback(); - } - - public void expectOffloadStatusChanged(final int expectedStatus) { - assertOffloadStatusChangedCallback(); - assertEquals(mOffloadStatus.remove(0), new Integer(expectedStatus)); - } - - public TetherStatesParcel pollTetherStatesChanged() { - assertStateChangeCallback(); - return mTetherStates.remove(0); - } - - @Override - public void onUpstreamChanged(Network network) { - mActualUpstreams.add(network); - } - - @Override - public void onConfigurationChanged(TetheringConfigurationParcel config) { - mTetheringConfigs.add(config); - } - - @Override - public void onTetherStatesChanged(TetherStatesParcel states) { - mTetherStates.add(states); - } - - @Override - public void onTetherClientsChanged(List clients) { - // TODO: check this - } - - @Override - public void onOffloadStatusChanged(final int status) { - mOffloadStatus.add(status); - } - - @Override - public void onCallbackStarted(TetheringCallbackStartedParcel parcel) { - mActualUpstreams.add(parcel.upstreamNetwork); - mTetheringConfigs.add(parcel.config); - mTetherStates.add(parcel.states); - mOffloadStatus.add(parcel.offloadStatus); - } - - @Override - public void onCallbackStopped(int errorCode) { } - - public void assertNoUpstreamChangeCallback() { - assertTrue(mActualUpstreams.isEmpty()); - } - - public void assertNoConfigChangeCallback() { - assertTrue(mTetheringConfigs.isEmpty()); - } - - public void assertNoStateChangeCallback() { - assertTrue(mTetherStates.isEmpty()); - } - - public void assertStateChangeCallback() { - assertFalse(mTetherStates.isEmpty()); - } - - public void assertOffloadStatusChangedCallback() { - assertFalse(mOffloadStatus.isEmpty()); - } - - public void assertNoCallback() { - assertNoUpstreamChangeCallback(); - assertNoConfigChangeCallback(); - assertNoStateChangeCallback(); - } - - private void assertTetherConfigParcelEqual(@NonNull TetheringConfigurationParcel actual, - @NonNull TetheringConfigurationParcel expect) { - assertEquals(actual.subId, expect.subId); - assertArrayEquals(actual.tetherableUsbRegexs, expect.tetherableUsbRegexs); - assertArrayEquals(actual.tetherableWifiRegexs, expect.tetherableWifiRegexs); - assertArrayEquals(actual.tetherableBluetoothRegexs, expect.tetherableBluetoothRegexs); - assertEquals(actual.isDunRequired, expect.isDunRequired); - assertEquals(actual.chooseUpstreamAutomatically, expect.chooseUpstreamAutomatically); - assertArrayEquals(actual.preferredUpstreamIfaceTypes, - expect.preferredUpstreamIfaceTypes); - assertArrayEquals(actual.legacyDhcpRanges, expect.legacyDhcpRanges); - assertArrayEquals(actual.defaultIPv4DNS, expect.defaultIPv4DNS); - assertEquals(actual.enableLegacyDhcpServer, expect.enableLegacyDhcpServer); - assertArrayEquals(actual.provisioningApp, expect.provisioningApp); - assertEquals(actual.provisioningAppNoUi, expect.provisioningAppNoUi); - assertEquals(actual.provisioningCheckPeriod, expect.provisioningCheckPeriod); - } - } - - private void assertTetherStatesNotNullButEmpty(final TetherStatesParcel parcel) { - assertFalse(parcel == null); - assertEquals(0, parcel.availableList.length); - assertEquals(0, parcel.tetheredList.length); - assertEquals(0, parcel.localOnlyList.length); - assertEquals(0, parcel.erroredIfaceList.length); - assertEquals(0, parcel.lastErrorList.length); - MiscAsserts.assertFieldCountEquals(5, TetherStatesParcel.class); - } - - @Test - public void testRegisterTetheringEventCallback() throws Exception { - TestTetheringEventCallback callback = new TestTetheringEventCallback(); - TestTetheringEventCallback callback2 = new TestTetheringEventCallback(); - - // 1. Register one callback before running any tethering. - mTethering.registerTetheringEventCallback(callback); - mLooper.dispatchAll(); - callback.expectUpstreamChanged(new Network[] {null}); - callback.expectConfigurationChanged( - mTethering.getTetheringConfiguration().toStableParcelable()); - TetherStatesParcel tetherState = callback.pollTetherStatesChanged(); - assertTetherStatesNotNullButEmpty(tetherState); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - // 2. Enable wifi tethering. - UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); - initTetheringUpstream(upstreamState); - when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - mLooper.dispatchAll(); - tetherState = callback.pollTetherStatesChanged(); - assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); - - mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); - mLooper.dispatchAll(); - tetherState = callback.pollTetherStatesChanged(); - assertArrayEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME}); - callback.expectUpstreamChanged(upstreamState.network); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED); - - // 3. Register second callback. - mTethering.registerTetheringEventCallback(callback2); - mLooper.dispatchAll(); - callback2.expectUpstreamChanged(upstreamState.network); - callback2.expectConfigurationChanged( - mTethering.getTetheringConfiguration().toStableParcelable()); - tetherState = callback2.pollTetherStatesChanged(); - assertEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME}); - callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED); - - // 4. Unregister first callback and disable wifi tethering - mTethering.unregisterTetheringEventCallback(callback); - mLooper.dispatchAll(); - mTethering.stopTethering(TETHERING_WIFI); - sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); - mLooper.dispatchAll(); - tetherState = callback2.pollTetherStatesChanged(); - assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); - mLooper.dispatchAll(); - callback2.expectUpstreamChanged(new Network[] {null}); - callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - callback.assertNoCallback(); - } - - @Test - public void testReportFailCallbackIfOffloadNotSupported() throws Exception { - final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); - TestTetheringEventCallback callback = new TestTetheringEventCallback(); - mTethering.registerTetheringEventCallback(callback); - mLooper.dispatchAll(); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - - // 1. Offload fail if no OffloadConfig. - initOffloadConfiguration(false /* offloadConfig */, true /* offloadControl */, - 0 /* defaultDisabled */); - runUsbTethering(upstreamState); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED); - runStopUSBTethering(); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - reset(mUsbManager); - // 2. Offload fail if no OffloadControl. - initOffloadConfiguration(true /* offloadConfig */, false /* offloadControl */, - 0 /* defaultDisabled */); - runUsbTethering(upstreamState); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED); - runStopUSBTethering(); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - reset(mUsbManager); - // 3. Offload fail if disabled by settings. - initOffloadConfiguration(true /* offloadConfig */, true /* offloadControl */, - 1 /* defaultDisabled */); - runUsbTethering(upstreamState); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED); - runStopUSBTethering(); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - } - - private void runStopUSBTethering() { - mTethering.stopTethering(TETHERING_USB); - mLooper.dispatchAll(); - mTethering.interfaceRemoved(TEST_USB_IFNAME); - mLooper.dispatchAll(); - } - - private void initOffloadConfiguration(final boolean offloadConfig, - final boolean offloadControl, final int defaultDisabled) { - when(mOffloadHardwareInterface.initOffloadConfig()).thenReturn(offloadConfig); - when(mOffloadHardwareInterface.initOffloadControl(any())).thenReturn(offloadControl); - when(mOffloadHardwareInterface.getDefaultTetherOffloadDisabled()).thenReturn( - defaultDisabled); - } - - @Test - public void testMultiSimAware() throws Exception { - final TetheringConfiguration initailConfig = mTethering.getTetheringConfiguration(); - assertEquals(INVALID_SUBSCRIPTION_ID, initailConfig.activeDataSubId); - - final int fakeSubId = 1234; - mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); - final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration(); - assertEquals(fakeSubId, newConfig.activeDataSubId); - verify(mNotificationUpdater, times(1)).onActiveDataSubscriptionIdChanged(eq(fakeSubId)); - } - - @Test - public void testNoDuplicatedEthernetRequest() throws Exception { - final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class); - when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest); - mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null); - mLooper.dispatchAll(); - verify(mEm, times(1)).requestTetheredInterface(any(), any()); - mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null); - mLooper.dispatchAll(); - verifyNoMoreInteractions(mEm); - mTethering.stopTethering(TETHERING_ETHERNET); - mLooper.dispatchAll(); - verify(mockRequest, times(1)).release(); - mTethering.stopTethering(TETHERING_ETHERNET); - mLooper.dispatchAll(); - verifyNoMoreInteractions(mEm); - } - - private void workingWifiP2pGroupOwner( - boolean emulateInterfaceStatusChanged) throws Exception { - if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); - } - sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME); - mLooper.dispatchAll(); - - verifyInterfaceServingModeStarted(TEST_P2P_IFNAME); - verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); - verify(mNetd, times(1)).tetherStartWithConfiguration(any()); - verifyNoMoreInteractions(mNetd); - verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY); - verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); - // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY - verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE); - - assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME)); - - // Emulate externally-visible WifiP2pManager effects, when wifi p2p group - // is being removed. - sendWifiP2pConnectionChanged(false, true, TEST_P2P_IFNAME); - mTethering.interfaceRemoved(TEST_P2P_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - verify(mNetd, times(1)).tetherInterfaceRemove(TEST_P2P_IFNAME); - verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); - // interfaceSetCfg() called once for enabling and twice for disabling IPv4. - verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, times(1)).tetherStop(); - verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); - verify(mUpstreamNetworkMonitor, never()).getCurrentPreferredUpstream(); - verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any()); - verifyNoMoreInteractions(mNetd); - // Asking for the last error after the per-interface state machine - // has been reaped yields an unknown interface error. - assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); - } - - private void workingWifiP2pGroupClient( - boolean emulateInterfaceStatusChanged) throws Exception { - if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); - } - sendWifiP2pConnectionChanged(true, false, TEST_P2P_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME); - verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); - verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME); - verify(mNetd, never()).tetherStartWithConfiguration(any()); - - // Emulate externally-visible WifiP2pManager effects, when wifi p2p group - // is being removed. - sendWifiP2pConnectionChanged(false, false, TEST_P2P_IFNAME); - mTethering.interfaceRemoved(TEST_P2P_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, never()).tetherApplyDnsInterfaces(); - verify(mNetd, never()).tetherInterfaceRemove(TEST_P2P_IFNAME); - verify(mNetd, never()).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); - verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, never()).tetherStop(); - verify(mNetd, never()).ipfwdDisableForwarding(TETHERING_NAME); - verifyNoMoreInteractions(mNetd); - // Asking for the last error after the per-interface state machine - // has been reaped yields an unknown interface error. - assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); - } - - @Test - public void workingWifiP2pGroupOwnerWithIfaceChanged() throws Exception { - workingWifiP2pGroupOwner(true); - } - - @Test - public void workingWifiP2pGroupOwnerSansIfaceChanged() throws Exception { - workingWifiP2pGroupOwner(false); - } - - private void workingWifiP2pGroupOwnerLegacyMode( - boolean emulateInterfaceStatusChanged) throws Exception { - // change to legacy mode and update tethering information by chaning SIM - when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) - .thenReturn(new String[]{}); - final int fakeSubId = 1234; - mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); - - if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); - } - sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME); - verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); - verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME); - verify(mNetd, never()).tetherStartWithConfiguration(any()); - assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); - } - @Test - public void workingWifiP2pGroupOwnerLegacyModeWithIfaceChanged() throws Exception { - workingWifiP2pGroupOwnerLegacyMode(true); - } - - @Test - public void workingWifiP2pGroupOwnerLegacyModeSansIfaceChanged() throws Exception { - workingWifiP2pGroupOwnerLegacyMode(false); - } - - @Test - public void workingWifiP2pGroupClientWithIfaceChanged() throws Exception { - workingWifiP2pGroupClient(true); - } - - @Test - public void workingWifiP2pGroupClientSansIfaceChanged() throws Exception { - workingWifiP2pGroupClient(false); - } - - private void setDataSaverEnabled(boolean enabled) { - final Intent intent = new Intent(ACTION_RESTRICT_BACKGROUND_CHANGED); - mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL); - - final int status = enabled ? RESTRICT_BACKGROUND_STATUS_ENABLED - : RESTRICT_BACKGROUND_STATUS_DISABLED; - when(mCm.getRestrictBackgroundStatus()).thenReturn(status); - mLooper.dispatchAll(); - } - - @Test - public void testDataSaverChanged() { - // Start Tethering. - final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - runUsbTethering(upstreamState); - assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); - // Data saver is ON. - setDataSaverEnabled(true); - // Verify that tethering should be disabled. - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); - mTethering.interfaceRemoved(TEST_USB_IFNAME); - mLooper.dispatchAll(); - assertEquals(mTethering.getTetheredIfaces(), new String[0]); - reset(mUsbManager); - - runUsbTethering(upstreamState); - // Verify that user can start tethering again without turning OFF data saver. - assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); - - // If data saver is keep ON with change event, tethering should not be OFF this time. - setDataSaverEnabled(true); - verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE); - assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); - - // If data saver is turned OFF, it should not change tethering. - setDataSaverEnabled(false); - verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE); - assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); - } - - private static void assertContains(Collection collection, T element) { - assertTrue(element + " not found in " + collection, collection.contains(element)); - } - - private class ResultListener extends IIntResultListener.Stub { - private final int mExpectedResult; - private boolean mHasResult = false; - ResultListener(final int expectedResult) { - mExpectedResult = expectedResult; - } - - @Override - public void onResult(final int resultCode) { - mHasResult = true; - if (resultCode != mExpectedResult) { - fail("expected result: " + mExpectedResult + " but actual result: " + resultCode); - } - } - - public void assertHasResult() { - if (!mHasResult) fail("No callback result"); - } - } - - @Test - public void testMultipleStartTethering() throws Exception { - final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24"); - final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24"); - final String serverAddr = "192.168.20.1"; - final ResultListener firstResult = new ResultListener(TETHER_ERROR_NO_ERROR); - final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR); - final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR); - - // Enable USB tethering and check that Tethering starts USB. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - null, null, false), firstResult); - mLooper.dispatchAll(); - firstResult.assertHasResult(); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - verifyNoMoreInteractions(mUsbManager); - - // Enable USB tethering again with the same request and expect no change to USB. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - null, null, false), secondResult); - mLooper.dispatchAll(); - secondResult.assertHasResult(); - verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE); - reset(mUsbManager); - - // Enable USB tethering with a different request and expect that USB is stopped and - // started. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - serverLinkAddr, clientLinkAddr, false), thirdResult); - mLooper.dispatchAll(); - thirdResult.assertHasResult(); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - - // Expect that when USB comes up, the DHCP server is configured with the requested address. - mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); - sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr))); - } - - @Test - public void testRequestStaticIp() throws Exception { - final LinkAddress serverLinkAddr = new LinkAddress("192.168.0.123/24"); - final LinkAddress clientLinkAddr = new LinkAddress("192.168.0.42/24"); - final String serverAddr = "192.168.0.123"; - final int clientAddrParceled = 0xc0a8002a; - final ArgumentCaptor dhcpParamsCaptor = - ArgumentCaptor.forClass(DhcpServingParamsParcel.class); - mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - serverLinkAddr, clientLinkAddr, false), null); - mLooper.dispatchAll(); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); - sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); - verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr))); - verify(mIpServerDependencies, times(1)).makeDhcpServer(any(), dhcpParamsCaptor.capture(), - any()); - final DhcpServingParamsParcel params = dhcpParamsCaptor.getValue(); - assertEquals(serverAddr, intToInet4AddressHTH(params.serverAddr).getHostAddress()); - assertEquals(24, params.serverAddrPrefixLength); - assertEquals(clientAddrParceled, params.singleClientAddr); - } - - @Test - public void testUpstreamNetworkChanged() { - final Tethering.TetherMainSM stateMachine = (Tethering.TetherMainSM) - mTetheringDependencies.mUpstreamNetworkMonitorSM; - final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - initTetheringUpstream(upstreamState); - stateMachine.chooseUpstreamType(true); - - verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network)); - verify(mNotificationUpdater, times(1)).onUpstreamCapabilitiesChanged(any()); - } - - @Test - public void testUpstreamCapabilitiesChanged() { - final Tethering.TetherMainSM stateMachine = (Tethering.TetherMainSM) - mTetheringDependencies.mUpstreamNetworkMonitorSM; - final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - initTetheringUpstream(upstreamState); - stateMachine.chooseUpstreamType(true); - - stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState); - // Should have two onUpstreamCapabilitiesChanged(). - // One is called by reportUpstreamChanged(). One is called by EVENT_ON_CAPABILITIES. - verify(mNotificationUpdater, times(2)).onUpstreamCapabilitiesChanged(any()); - reset(mNotificationUpdater); - - // Verify that onUpstreamCapabilitiesChanged won't be called if not current upstream network - // capabilities changed. - final UpstreamNetworkState upstreamState2 = new UpstreamNetworkState( - upstreamState.linkProperties, upstreamState.networkCapabilities, new Network(101)); - stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState2); - verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any()); - } - - @Test - public void testDumpTetheringLog() throws Exception { - final FileDescriptor mockFd = mock(FileDescriptor.class); - final PrintWriter mockPw = mock(PrintWriter.class); - runUsbTethering(null); - mLooper.startAutoDispatch(); - mTethering.dump(mockFd, mockPw, new String[0]); - verify(mConfig).dump(any()); - verify(mEntitleMgr).dump(any()); - verify(mOffloadCtrl).dump(any()); - mLooper.stopAutoDispatch(); - } - - @Test - public void testExemptFromEntitlementCheck() throws Exception { - setupForRequiredProvisioning(); - final TetheringRequestParcel wifiNotExemptRequest = - createTetheringRequestParcel(TETHERING_WIFI, null, null, false); - mTethering.startTethering(wifiNotExemptRequest, null); - mLooper.dispatchAll(); - verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); - verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI); - assertFalse(mEntitleMgr.isCellularUpstreamPermitted()); - mTethering.stopTethering(TETHERING_WIFI); - mLooper.dispatchAll(); - verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); - reset(mEntitleMgr); - - setupForRequiredProvisioning(); - final TetheringRequestParcel wifiExemptRequest = - createTetheringRequestParcel(TETHERING_WIFI, null, null, true); - mTethering.startTethering(wifiExemptRequest, null); - mLooper.dispatchAll(); - verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); - verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI); - assertTrue(mEntitleMgr.isCellularUpstreamPermitted()); - mTethering.stopTethering(TETHERING_WIFI); - mLooper.dispatchAll(); - verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); - reset(mEntitleMgr); - - // If one app enables tethering without provisioning check first, then another app enables - // tethering of the same type but does not disable the provisioning check. - setupForRequiredProvisioning(); - mTethering.startTethering(wifiExemptRequest, null); - mLooper.dispatchAll(); - verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); - verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI); - assertTrue(mEntitleMgr.isCellularUpstreamPermitted()); - reset(mEntitleMgr); - setupForRequiredProvisioning(); - mTethering.startTethering(wifiNotExemptRequest, null); - mLooper.dispatchAll(); - verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); - verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI); - assertFalse(mEntitleMgr.isCellularUpstreamPermitted()); - mTethering.stopTethering(TETHERING_WIFI); - mLooper.dispatchAll(); - verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); - reset(mEntitleMgr); - } - - private void setupForRequiredProvisioning() { - // Produce some acceptable looking provision app setting if requested. - when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) - .thenReturn(PROVISIONING_APP_NAME); - when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) - .thenReturn(PROVISIONING_NO_UI_APP_NAME); - // Act like the CarrierConfigManager is present and ready unless told otherwise. - when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) - .thenReturn(mCarrierConfigManager); - when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true); - sendConfigurationChanged(); - } - - private static UpstreamNetworkState buildV4UpstreamState(final LinkAddress address, - final Network network, final String iface, final int transportType) { - final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(iface); - - prop.addLinkAddress(address); - - final NetworkCapabilities capabilities = new NetworkCapabilities() - .addTransportType(transportType); - return new UpstreamNetworkState(prop, capabilities, network); - } - - private void updateV4Upstream(final LinkAddress ipv4Address, final Network network, - final String iface, final int transportType) { - final UpstreamNetworkState upstream = buildV4UpstreamState(ipv4Address, network, iface, - transportType); - mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage( - Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK, - UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, - 0, - upstream); - mLooper.dispatchAll(); - } - - @Test - public void testHandleIpConflict() throws Exception { - final Network wifiNetwork = new Network(200); - final Network[] allNetworks = { wifiNetwork }; - when(mCm.getAllNetworks()).thenReturn(allNetworks); - runUsbTethering(null); - final ArgumentCaptor ifaceConfigCaptor = - ArgumentCaptor.forClass(InterfaceConfigurationParcel.class); - verify(mNetd).interfaceSetCfg(ifaceConfigCaptor.capture()); - final String ipv4Address = ifaceConfigCaptor.getValue().ipv4Addr; - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - reset(mNetd, mUsbManager); - - // Cause a prefix conflict by assigning a /30 out of the downstream's /24 to the upstream. - updateV4Upstream(new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address), 30), - wifiNetwork, TEST_WIFI_IFNAME, TRANSPORT_WIFI); - // verify turn off usb tethering - verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE); - mTethering.interfaceRemoved(TEST_USB_IFNAME); - mLooper.dispatchAll(); - // verify restart usb tethering - verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - } - - @Test - public void testNoAddressAvailable() throws Exception { - final Network wifiNetwork = new Network(200); - final Network btNetwork = new Network(201); - final Network mobileNetwork = new Network(202); - final Network[] allNetworks = { wifiNetwork, btNetwork, mobileNetwork }; - when(mCm.getAllNetworks()).thenReturn(allNetworks); - runUsbTethering(null); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - reset(mUsbManager); - final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class); - when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest); - final ArgumentCaptor callbackCaptor = - ArgumentCaptor.forClass(TetheredInterfaceCallback.class); - mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null); - mLooper.dispatchAll(); - verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture()); - TetheredInterfaceCallback ethCallback = callbackCaptor.getValue(); - ethCallback.onAvailable(TEST_ETH_IFNAME); - mLooper.dispatchAll(); - reset(mUsbManager, mEm); - - updateV4Upstream(new LinkAddress("192.168.0.100/16"), wifiNetwork, TEST_WIFI_IFNAME, - TRANSPORT_WIFI); - updateV4Upstream(new LinkAddress("172.16.0.0/12"), btNetwork, TEST_BT_IFNAME, - TRANSPORT_BLUETOOTH); - updateV4Upstream(new LinkAddress("10.0.0.0/8"), mobileNetwork, TEST_MOBILE_IFNAME, - TRANSPORT_CELLULAR); - - mLooper.dispatchAll(); - // verify turn off usb tethering - verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE); - // verify turn off ethernet tethering - verify(mockRequest).release(); - mTethering.interfaceRemoved(TEST_USB_IFNAME); - ethCallback.onUnavailable(); - mLooper.dispatchAll(); - // verify restart usb tethering - verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - // verify restart ethernet tethering - verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture()); - ethCallback = callbackCaptor.getValue(); - ethCallback.onAvailable(TEST_ETH_IFNAME); - - reset(mUsbManager, mEm); - when(mNetd.interfaceGetList()) - .thenReturn(new String[] { - TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME, - TEST_NCM_IFNAME, TEST_ETH_IFNAME}); - - mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); - sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); - assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_USB_IFNAME); - assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_ETH_IFNAME); - assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_USB_IFNAME)); - assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_ETH_IFNAME)); - } - - @Test - public void testProvisioningNeededButUnavailable() throws Exception { - assertTrue(mTethering.isTetheringSupported()); - verify(mPackageManager, never()).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); - - setupForRequiredProvisioning(); - assertTrue(mTethering.isTetheringSupported()); - verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); - reset(mPackageManager); - - doThrow(PackageManager.NameNotFoundException.class).when(mPackageManager).getPackageInfo( - PROVISIONING_APP_NAME[0], GET_ACTIVITIES); - setupForRequiredProvisioning(); - assertFalse(mTethering.isTetheringSupported()); - verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); - } - - // TODO: Test that a request for hotspot mode doesn't interfere with an - // already operating tethering mode interface. -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java deleted file mode 100644 index 232588c7eec0..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java +++ /dev/null @@ -1,800 +0,0 @@ -/* - * Copyright (C) 2017 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.networkstack.tethering; - -import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; -import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; -import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; - -import static com.android.networkstack.tethering.UpstreamNetworkMonitor.TYPE_NONE; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.ConnectivityManager.NetworkCallback; -import android.net.IConnectivityManager; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.Message; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class UpstreamNetworkMonitorTest { - private static final int EVENT_UNM_UPDATE = 1; - - private static final boolean INCLUDES = true; - private static final boolean EXCLUDES = false; - - // Actual contents of the request don't matter for this test. The lack of - // any specific TRANSPORT_* is sufficient to identify this request. - private static final NetworkRequest sDefaultRequest = new NetworkRequest.Builder().build(); - - @Mock private Context mContext; - @Mock private EntitlementManager mEntitleMgr; - @Mock private IConnectivityManager mCS; - @Mock private SharedLog mLog; - - private TestStateMachine mSM; - private TestConnectivityManager mCM; - private UpstreamNetworkMonitor mUNM; - - @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - reset(mContext); - reset(mCS); - reset(mLog); - when(mLog.forSubComponent(anyString())).thenReturn(mLog); - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true); - - mCM = spy(new TestConnectivityManager(mContext, mCS)); - mSM = new TestStateMachine(); - mUNM = new UpstreamNetworkMonitor( - (ConnectivityManager) mCM, mSM, mLog, EVENT_UNM_UPDATE); - } - - @After public void tearDown() throws Exception { - if (mSM != null) { - mSM.quit(); - mSM = null; - } - } - - @Test - public void testStopWithoutStartIsNonFatal() { - mUNM.stop(); - mUNM.stop(); - mUNM.stop(); - } - - @Test - public void testDoesNothingBeforeTrackDefaultAndStarted() throws Exception { - assertTrue(mCM.hasNoCallbacks()); - assertFalse(mUNM.mobileNetworkRequested()); - - mUNM.updateMobileRequiresDun(true); - assertTrue(mCM.hasNoCallbacks()); - mUNM.updateMobileRequiresDun(false); - assertTrue(mCM.hasNoCallbacks()); - } - - @Test - public void testDefaultNetworkIsTracked() throws Exception { - assertTrue(mCM.hasNoCallbacks()); - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - - mUNM.startObserveAllNetworks(); - assertEquals(1, mCM.trackingDefault.size()); - - mUNM.stop(); - assertTrue(mCM.onlyHasDefaultCallbacks()); - } - - @Test - public void testListensForAllNetworks() throws Exception { - assertTrue(mCM.listening.isEmpty()); - - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - mUNM.startObserveAllNetworks(); - assertFalse(mCM.listening.isEmpty()); - assertTrue(mCM.isListeningForAll()); - - mUNM.stop(); - assertTrue(mCM.onlyHasDefaultCallbacks()); - } - - @Test - public void testCallbacksRegistered() { - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - verify(mCM, times(1)).requestNetwork( - eq(sDefaultRequest), any(NetworkCallback.class), any(Handler.class)); - mUNM.startObserveAllNetworks(); - verify(mCM, times(1)).registerNetworkCallback( - any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class)); - - mUNM.stop(); - verify(mCM, times(1)).unregisterNetworkCallback(any(NetworkCallback.class)); - } - - @Test - public void testRequestsMobileNetwork() throws Exception { - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.startObserveAllNetworks(); - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.updateMobileRequiresDun(false); - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.registerMobileNetworkRequest(); - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI); - assertFalse(isDunRequested()); - - mUNM.stop(); - assertFalse(mUNM.mobileNetworkRequested()); - assertTrue(mCM.hasNoCallbacks()); - } - - @Test - public void testDuplicateMobileRequestsIgnored() throws Exception { - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.startObserveAllNetworks(); - verify(mCM, times(1)).registerNetworkCallback( - any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class)); - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.updateMobileRequiresDun(true); - mUNM.registerMobileNetworkRequest(); - verify(mCM, times(1)).requestNetwork( - any(NetworkRequest.class), anyInt(), anyInt(), any(Handler.class), - any(NetworkCallback.class)); - - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(isDunRequested()); - - // Try a few things that must not result in any state change. - mUNM.registerMobileNetworkRequest(); - mUNM.updateMobileRequiresDun(true); - mUNM.registerMobileNetworkRequest(); - - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(isDunRequested()); - - mUNM.stop(); - verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class)); - - verifyNoMoreInteractions(mCM); - } - - @Test - public void testRequestsDunNetwork() throws Exception { - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.startObserveAllNetworks(); - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.updateMobileRequiresDun(true); - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.registerMobileNetworkRequest(); - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(isDunRequested()); - - mUNM.stop(); - assertFalse(mUNM.mobileNetworkRequested()); - assertTrue(mCM.hasNoCallbacks()); - } - - @Test - public void testUpdateMobileRequiresDun() throws Exception { - mUNM.startObserveAllNetworks(); - - // Test going from no-DUN to DUN correctly re-registers callbacks. - mUNM.updateMobileRequiresDun(false); - mUNM.registerMobileNetworkRequest(); - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI); - assertFalse(isDunRequested()); - mUNM.updateMobileRequiresDun(true); - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(isDunRequested()); - - // Test going from DUN to no-DUN correctly re-registers callbacks. - mUNM.updateMobileRequiresDun(false); - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI); - assertFalse(isDunRequested()); - - mUNM.stop(); - assertFalse(mUNM.mobileNetworkRequested()); - } - - @Test - public void testSelectPreferredUpstreamType() throws Exception { - final Collection preferredTypes = new ArrayList<>(); - preferredTypes.add(TYPE_WIFI); - - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - mUNM.startObserveAllNetworks(); - // There are no networks, so there is nothing to select. - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - - final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); - wifiAgent.fakeConnect(); - // WiFi is up, we should prefer it. - assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); - wifiAgent.fakeDisconnect(); - // There are no networks, so there is nothing to select. - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - - final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - cellAgent.fakeConnect(); - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - - preferredTypes.add(TYPE_MOBILE_DUN); - // This is coupled with preferred types in TetheringConfiguration. - mUNM.updateMobileRequiresDun(true); - // DUN is available, but only use regular cell: no upstream selected. - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - preferredTypes.remove(TYPE_MOBILE_DUN); - // No WiFi, but our preferred flavour of cell is up. - preferredTypes.add(TYPE_MOBILE_HIPRI); - // This is coupled with preferred types in TetheringConfiguration. - mUNM.updateMobileRequiresDun(false); - assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI, - mUNM.selectPreferredUpstreamType(preferredTypes)); - // Check to see we filed an explicit request. - assertEquals(1, mCM.requested.size()); - NetworkRequest netReq = (NetworkRequest) mCM.requested.values().toArray()[0]; - assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)); - assertFalse(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)); - // mobile is not permitted, we should not use HIPRI. - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false); - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - assertEquals(0, mCM.requested.size()); - // mobile change back to permitted, HIRPI should come back - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true); - assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI, - mUNM.selectPreferredUpstreamType(preferredTypes)); - - wifiAgent.fakeConnect(); - // WiFi is up, and we should prefer it over cell. - assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); - assertEquals(0, mCM.requested.size()); - - preferredTypes.remove(TYPE_MOBILE_HIPRI); - preferredTypes.add(TYPE_MOBILE_DUN); - // This is coupled with preferred types in TetheringConfiguration. - mUNM.updateMobileRequiresDun(true); - assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); - - final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN); - dunAgent.fakeConnect(); - - // WiFi is still preferred. - assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); - - // WiFi goes down, cell and DUN are still up but only DUN is preferred. - wifiAgent.fakeDisconnect(); - assertSatisfiesLegacyType(TYPE_MOBILE_DUN, - mUNM.selectPreferredUpstreamType(preferredTypes)); - // Check to see we filed an explicit request. - assertEquals(1, mCM.requested.size()); - netReq = (NetworkRequest) mCM.requested.values().toArray()[0]; - assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)); - assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)); - // mobile is not permitted, we should not use DUN. - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false); - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - assertEquals(0, mCM.requested.size()); - // mobile change back to permitted, DUN should come back - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true); - assertSatisfiesLegacyType(TYPE_MOBILE_DUN, - mUNM.selectPreferredUpstreamType(preferredTypes)); - } - - @Test - public void testGetCurrentPreferredUpstream() throws Exception { - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - mUNM.startObserveAllNetworks(); - mUNM.updateMobileRequiresDun(false); - - // [0] Mobile connects, DUN not required -> mobile selected. - final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - cellAgent.fakeConnect(); - mCM.makeDefaultNetwork(cellAgent); - assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network); - - // [1] Mobile connects but not permitted -> null selected - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false); - assertEquals(null, mUNM.getCurrentPreferredUpstream()); - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true); - - // [2] WiFi connects but not validated/promoted to default -> mobile selected. - final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); - wifiAgent.fakeConnect(); - assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network); - - // [3] WiFi validates and is promoted to the default network -> WiFi selected. - mCM.makeDefaultNetwork(wifiAgent); - assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network); - - // [4] DUN required, no other changes -> WiFi still selected - mUNM.updateMobileRequiresDun(true); - assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network); - - // [5] WiFi no longer validated, mobile becomes default, DUN required -> null selected. - mCM.makeDefaultNetwork(cellAgent); - assertEquals(null, mUNM.getCurrentPreferredUpstream()); - // TODO: make sure that a DUN request has been filed. This is currently - // triggered by code over in Tethering, but once that has been moved - // into UNM we should test for this here. - - // [6] DUN network arrives -> DUN selected - final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN); - dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); - dunAgent.fakeConnect(); - assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network); - - // [7] Mobile is not permitted -> null selected - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false); - assertEquals(null, mUNM.getCurrentPreferredUpstream()); - } - - @Test - public void testLocalPrefixes() throws Exception { - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - mUNM.startObserveAllNetworks(); - - // [0] Test minimum set of local prefixes. - Set local = mUNM.getLocalPrefixes(); - assertTrue(local.isEmpty()); - - final Set alreadySeen = new HashSet<>(); - - // [1] Pretend Wi-Fi connects. - final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); - final LinkProperties wifiLp = wifiAgent.linkProperties; - wifiLp.setInterfaceName("wlan0"); - final String[] wifi_addrs = { - "fe80::827a:bfff:fe6f:374d", "100.112.103.18", - "2001:db8:4:fd00:827a:bfff:fe6f:374d", - "2001:db8:4:fd00:6dea:325a:fdae:4ef4", - "fd6a:a640:60bf:e985::123", // ULA address for good measure. - }; - for (String addrStr : wifi_addrs) { - final String cidr = addrStr.contains(":") ? "/64" : "/20"; - wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr)); - } - wifiAgent.fakeConnect(); - wifiAgent.sendLinkProperties(); - - local = mUNM.getLocalPrefixes(); - assertPrefixSet(local, INCLUDES, alreadySeen); - final String[] wifiLinkPrefixes = { - // Link-local prefixes are excluded and dealt with elsewhere. - "100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64", - }; - assertPrefixSet(local, INCLUDES, wifiLinkPrefixes); - Collections.addAll(alreadySeen, wifiLinkPrefixes); - assertEquals(alreadySeen.size(), local.size()); - - // [2] Pretend mobile connects. - final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - final LinkProperties cellLp = cellAgent.linkProperties; - cellLp.setInterfaceName("rmnet_data0"); - final String[] cell_addrs = { - "10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d", - }; - for (String addrStr : cell_addrs) { - final String cidr = addrStr.contains(":") ? "/64" : "/27"; - cellLp.addLinkAddress(new LinkAddress(addrStr + cidr)); - } - cellAgent.fakeConnect(); - cellAgent.sendLinkProperties(); - - local = mUNM.getLocalPrefixes(); - assertPrefixSet(local, INCLUDES, alreadySeen); - final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" }; - assertPrefixSet(local, INCLUDES, cellLinkPrefixes); - Collections.addAll(alreadySeen, cellLinkPrefixes); - assertEquals(alreadySeen.size(), local.size()); - - // [3] Pretend DUN connects. - final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN); - dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); - final LinkProperties dunLp = dunAgent.linkProperties; - dunLp.setInterfaceName("rmnet_data1"); - final String[] dun_addrs = { - "192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d", - }; - for (String addrStr : dun_addrs) { - final String cidr = addrStr.contains(":") ? "/64" : "/27"; - dunLp.addLinkAddress(new LinkAddress(addrStr + cidr)); - } - dunAgent.fakeConnect(); - dunAgent.sendLinkProperties(); - - local = mUNM.getLocalPrefixes(); - assertPrefixSet(local, INCLUDES, alreadySeen); - final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" }; - assertPrefixSet(local, INCLUDES, dunLinkPrefixes); - Collections.addAll(alreadySeen, dunLinkPrefixes); - assertEquals(alreadySeen.size(), local.size()); - - // [4] Pretend Wi-Fi disconnected. It's addresses/prefixes should no - // longer be included (should be properly removed). - wifiAgent.fakeDisconnect(); - local = mUNM.getLocalPrefixes(); - assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes); - assertPrefixSet(local, INCLUDES, cellLinkPrefixes); - assertPrefixSet(local, INCLUDES, dunLinkPrefixes); - - // [5] Pretend mobile disconnected. - cellAgent.fakeDisconnect(); - local = mUNM.getLocalPrefixes(); - assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes); - assertPrefixSet(local, EXCLUDES, cellLinkPrefixes); - assertPrefixSet(local, INCLUDES, dunLinkPrefixes); - - // [6] Pretend DUN disconnected. - dunAgent.fakeDisconnect(); - local = mUNM.getLocalPrefixes(); - assertTrue(local.isEmpty()); - } - - @Test - public void testSelectMobileWhenMobileIsNotDefault() { - final Collection preferredTypes = new ArrayList<>(); - // Mobile has higher pirority than wifi. - preferredTypes.add(TYPE_MOBILE_HIPRI); - preferredTypes.add(TYPE_WIFI); - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - mUNM.startObserveAllNetworks(); - // Setup wifi and make wifi as default network. - final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); - wifiAgent.fakeConnect(); - mCM.makeDefaultNetwork(wifiAgent); - // Setup mobile network. - final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - cellAgent.fakeConnect(); - - assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI, - mUNM.selectPreferredUpstreamType(preferredTypes)); - verify(mEntitleMgr, times(1)).maybeRunProvisioning(); - } - - private void assertSatisfiesLegacyType(int legacyType, UpstreamNetworkState ns) { - if (legacyType == TYPE_NONE) { - assertTrue(ns == null); - return; - } - - final NetworkCapabilities nc = - UpstreamNetworkMonitor.networkCapabilitiesForType(legacyType); - assertTrue(nc.satisfiedByNetworkCapabilities(ns.networkCapabilities)); - } - - private void assertUpstreamTypeRequested(int upstreamType) throws Exception { - assertEquals(1, mCM.requested.size()); - assertEquals(1, mCM.legacyTypeMap.size()); - assertEquals(Integer.valueOf(upstreamType), - mCM.legacyTypeMap.values().iterator().next()); - } - - private boolean isDunRequested() { - for (NetworkRequest req : mCM.requested.values()) { - if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) { - return true; - } - } - return false; - } - - public static class TestConnectivityManager extends ConnectivityManager { - public Map allCallbacks = new HashMap<>(); - public Set trackingDefault = new HashSet<>(); - public TestNetworkAgent defaultNetwork = null; - public Map listening = new HashMap<>(); - public Map requested = new HashMap<>(); - public Map legacyTypeMap = new HashMap<>(); - - private int mNetworkId = 100; - - public TestConnectivityManager(Context ctx, IConnectivityManager svc) { - super(ctx, svc); - } - - boolean hasNoCallbacks() { - return allCallbacks.isEmpty() - && trackingDefault.isEmpty() - && listening.isEmpty() - && requested.isEmpty() - && legacyTypeMap.isEmpty(); - } - - boolean onlyHasDefaultCallbacks() { - return (allCallbacks.size() == 1) - && (trackingDefault.size() == 1) - && listening.isEmpty() - && requested.isEmpty() - && legacyTypeMap.isEmpty(); - } - - boolean isListeningForAll() { - final NetworkCapabilities empty = new NetworkCapabilities(); - empty.clearAll(); - - for (NetworkRequest req : listening.values()) { - if (req.networkCapabilities.equalRequestableCapabilities(empty)) { - return true; - } - } - return false; - } - - int getNetworkId() { - return ++mNetworkId; - } - - void makeDefaultNetwork(TestNetworkAgent agent) { - if (Objects.equals(defaultNetwork, agent)) return; - - final TestNetworkAgent formerDefault = defaultNetwork; - defaultNetwork = agent; - - for (NetworkCallback cb : trackingDefault) { - if (defaultNetwork != null) { - cb.onAvailable(defaultNetwork.networkId); - cb.onCapabilitiesChanged( - defaultNetwork.networkId, defaultNetwork.networkCapabilities); - cb.onLinkPropertiesChanged( - defaultNetwork.networkId, defaultNetwork.linkProperties); - } - } - } - - @Override - public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) { - assertFalse(allCallbacks.containsKey(cb)); - allCallbacks.put(cb, h); - if (sDefaultRequest.equals(req)) { - assertFalse(trackingDefault.contains(cb)); - trackingDefault.add(cb); - } else { - assertFalse(requested.containsKey(cb)); - requested.put(cb, req); - } - } - - @Override - public void requestNetwork(NetworkRequest req, NetworkCallback cb) { - fail("Should never be called."); - } - - @Override - public void requestNetwork(NetworkRequest req, - int timeoutMs, int legacyType, Handler h, NetworkCallback cb) { - assertFalse(allCallbacks.containsKey(cb)); - allCallbacks.put(cb, h); - assertFalse(requested.containsKey(cb)); - requested.put(cb, req); - assertFalse(legacyTypeMap.containsKey(cb)); - if (legacyType != ConnectivityManager.TYPE_NONE) { - legacyTypeMap.put(cb, legacyType); - } - } - - @Override - public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) { - assertFalse(allCallbacks.containsKey(cb)); - allCallbacks.put(cb, h); - assertFalse(listening.containsKey(cb)); - listening.put(cb, req); - } - - @Override - public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) { - fail("Should never be called."); - } - - @Override - public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) { - fail("Should never be called."); - } - - @Override - public void registerDefaultNetworkCallback(NetworkCallback cb) { - fail("Should never be called."); - } - - @Override - public void unregisterNetworkCallback(NetworkCallback cb) { - if (trackingDefault.contains(cb)) { - trackingDefault.remove(cb); - } else if (listening.containsKey(cb)) { - listening.remove(cb); - } else if (requested.containsKey(cb)) { - requested.remove(cb); - legacyTypeMap.remove(cb); - } else { - fail("Unexpected callback removed"); - } - allCallbacks.remove(cb); - - assertFalse(allCallbacks.containsKey(cb)); - assertFalse(trackingDefault.contains(cb)); - assertFalse(listening.containsKey(cb)); - assertFalse(requested.containsKey(cb)); - } - } - - public static class TestNetworkAgent { - public final TestConnectivityManager cm; - public final Network networkId; - public final int transportType; - public final NetworkCapabilities networkCapabilities; - public final LinkProperties linkProperties; - - public TestNetworkAgent(TestConnectivityManager cm, int transportType) { - this.cm = cm; - this.networkId = new Network(cm.getNetworkId()); - this.transportType = transportType; - networkCapabilities = new NetworkCapabilities(); - networkCapabilities.addTransportType(transportType); - networkCapabilities.addCapability(NET_CAPABILITY_INTERNET); - linkProperties = new LinkProperties(); - } - - public void fakeConnect() { - for (NetworkCallback cb : cm.listening.keySet()) { - cb.onAvailable(networkId); - cb.onCapabilitiesChanged(networkId, copy(networkCapabilities)); - cb.onLinkPropertiesChanged(networkId, copy(linkProperties)); - } - } - - public void fakeDisconnect() { - for (NetworkCallback cb : cm.listening.keySet()) { - cb.onLost(networkId); - } - } - - public void sendLinkProperties() { - for (NetworkCallback cb : cm.listening.keySet()) { - cb.onLinkPropertiesChanged(networkId, copy(linkProperties)); - } - } - - @Override - public String toString() { - return String.format("TestNetworkAgent: %s %s", networkId, networkCapabilities); - } - } - - public static class TestStateMachine extends StateMachine { - public final ArrayList messages = new ArrayList<>(); - private final State mLoggingState = new LoggingState(); - - class LoggingState extends State { - @Override public void enter() { - messages.clear(); - } - - @Override public void exit() { - messages.clear(); - } - - @Override public boolean processMessage(Message msg) { - messages.add(msg); - return true; - } - } - - public TestStateMachine() { - super("UpstreamNetworkMonitor.TestStateMachine"); - addState(mLoggingState); - setInitialState(mLoggingState); - super.start(); - } - } - - static NetworkCapabilities copy(NetworkCapabilities nc) { - return new NetworkCapabilities(nc); - } - - static LinkProperties copy(LinkProperties lp) { - return new LinkProperties(lp); - } - - static void assertPrefixSet(Set prefixes, boolean expectation, String... expected) { - final Set expectedSet = new HashSet<>(); - Collections.addAll(expectedSet, expected); - assertPrefixSet(prefixes, expectation, expectedSet); - } - - static void assertPrefixSet(Set prefixes, boolean expectation, Set expected) { - for (String expectedPrefix : expected) { - final String errStr = expectation ? "did not find" : "found"; - assertEquals( - String.format("Failed expectation: %s prefix: %s", errStr, expectedPrefix), - expectation, prefixes.contains(new IpPrefix(expectedPrefix))); - } - } -} -- GitLab From 2e1f5a665397df98fe6208fa45680cf65f4e1650 Mon Sep 17 00:00:00 2001 From: Santiago Seifert Date: Wed, 4 Nov 2020 21:58:25 +0000 Subject: [PATCH 3/3] Address API feedback on setOnRtpRxNoticeListener 1. Naming 'Ims' in all the related variables & functions has changed to 'Rtp'. ex) setOnImsRxNoticeListener ----> setOnRtpRxNoticeListener 2. Change viewing of the parcel's data structure. Before the parcel is parsed based on it is byte[] type. From now it is analyzed as (int type, int[] data) 3. Add IMS permission checking when register the callback Change-Id: I38ff84bc3c723ca07e36ece1d94344fc7b11125b Test: Manually built the tree. Bug: 158546060 Signed-off-by: Kim Sungyeon --- api/system-current.txt | 6 +- api/system-lint-baseline.txt | 8 +- core/api/system-current.txt | 6 +- core/api/system-lint-baseline.txt | 6 +- media/java/android/media/MediaPlayer.java | 106 ++++++++++++---------- 5 files changed, 73 insertions(+), 59 deletions(-) diff --git a/api/system-current.txt b/api/system-current.txt index 115b4d88857f..0416265dea3b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4423,11 +4423,11 @@ package android.media { } public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation { - method @RequiresPermission("android.permission.BIND_IMS_SERVICE") public void setOnImsRxNoticeListener(@Nullable android.media.MediaPlayer.OnImsRxNoticeListener, @Nullable android.os.Handler); + method @RequiresPermission("android.permission.BIND_IMS_SERVICE") public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener, @Nullable android.os.Handler); } - public static interface MediaPlayer.OnImsRxNoticeListener { - method public void onImsRxNotice(@NonNull android.media.MediaPlayer, @NonNull byte[]); + public static interface MediaPlayer.OnRtpRxNoticeListener { + method public void onRtpRxNotice(@NonNull android.media.MediaPlayer, int, @NonNull int[]); } public final class MediaRecorder.AudioSource { diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt index 773ecd034a8e..3ef1f354d9e8 100644 --- a/api/system-lint-baseline.txt +++ b/api/system-lint-baseline.txt @@ -41,8 +41,8 @@ BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(jav Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.IpSecTransform.Builder.buildTunnelModeTransform(java.net.InetAddress,android.net.IpSecManager.SecurityParameterIndex) -ExecutorRegistration: android.media.MediaPlayer#setOnImsRxNoticeListener(android.media.MediaPlayer.OnImsRxNoticeListener, android.os.Handler): - Registration methods should have overload that accepts delivery Executor: `setOnImsRxNoticeListener` +ExecutorRegistration: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler): + Registration methods should have overload that accepts delivery Executor: `setOnRtpRxNoticeListener` ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#deletePersistentGroup(android.net.wifi.p2p.WifiP2pManager.Channel, int, android.net.wifi.p2p.WifiP2pManager.ActionListener): ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#factoryReset(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener): @@ -377,8 +377,8 @@ SamShouldBeLast: android.media.AudioRouting#addOnRoutingChangedListener(android. SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions -SamShouldBeLast: android.media.MediaPlayer#setOnImsRxNoticeListener(android.media.MediaPlayer.OnImsRxNoticeListener, android.os.Handler): - SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaPlayer.setOnImsRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler): + SAM-compatible parameters (such as parameter 2, "listener", in android.media.MediaPlayer.setOnRtpRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.MediaRecorder#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): SamShouldBeLast: android.media.MediaRecorder#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback): diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 1e3a2c04f1f5..d1e2542d8bf4 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4363,11 +4363,11 @@ package android.media { } public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation { - method @RequiresPermission("android.permission.BIND_IMS_SERVICE") public void setOnImsRxNoticeListener(@Nullable android.media.MediaPlayer.OnImsRxNoticeListener, @Nullable android.os.Handler); + method @RequiresPermission("android.permission.BIND_IMS_SERVICE") public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener, @Nullable android.os.Handler); } - public static interface MediaPlayer.OnImsRxNoticeListener { - method public void onImsRxNotice(@NonNull android.media.MediaPlayer, @NonNull byte[]); + public static interface MediaPlayer.OnRtpRxNoticeListener { + method public void onRtpRxNotice(@NonNull android.media.MediaPlayer, int, @NonNull int[]); } public final class MediaRecorder.AudioSource { diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index 4db55e7e07eb..a3fb06cbd92f 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -6,7 +6,8 @@ ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions(): BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex): Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.IpSecTransform.Builder.buildTunnelModeTransform(java.net.InetAddress,android.net.IpSecManager.SecurityParameterIndex) -ExecutorRegistration: android.media.MediaPlayer#setOnImsRxNoticeListener(android.media.MediaPlayer.OnImsRxNoticeListener, android.os.Handler): +ExecutorRegistration: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler): + Registration methods should have overload that accepts delivery Executor: `setOnRtpRxNoticeListener` GenericException: android.app.prediction.AppPredictor#finalize(): @@ -182,7 +183,8 @@ SamShouldBeLast: android.media.AudioRecordingMonitor#registerAudioRecordingCallb SamShouldBeLast: android.media.AudioRouting#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): -SamShouldBeLast: android.media.MediaPlayer#setOnImsRxNoticeListener(android.media.MediaPlayer.OnImsRxNoticeListener, android.os.Handler): +SamShouldBeLast: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler): + SAM-compatible parameters (such as parameter 2, "listener", in android.media.MediaPlayer.setOnRtpRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 42e39101de13..655454466b3d 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -16,6 +16,9 @@ package android.media; +import static android.Manifest.permission.BIND_IMS_SERVICE; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -83,6 +86,7 @@ import java.util.BitSet; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Scanner; import java.util.Set; import java.util.UUID; @@ -2109,8 +2113,8 @@ public class MediaPlayer extends PlayerBase mOnInfoListener = null; mOnVideoSizeChangedListener = null; mOnTimedTextListener = null; - mOnImsRxNoticeListener = null; - mOnImsRxNoticeHandler = null; + mOnRtpRxNoticeListener = null; + mOnRtpRxNoticeHandler = null; synchronized (mTimeProviderLock) { if (mTimeProvider != null) { mTimeProvider.close(); @@ -3323,7 +3327,7 @@ public class MediaPlayer extends PlayerBase private static final int MEDIA_META_DATA = 202; private static final int MEDIA_DRM_INFO = 210; private static final int MEDIA_TIME_DISCONTINUITY = 211; - private static final int MEDIA_IMS_RX_NOTICE = 300; + private static final int MEDIA_RTP_RX_NOTICE = 300; private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000; private TimeProvider mTimeProvider; @@ -3633,31 +3637,34 @@ public class MediaPlayer extends PlayerBase } return; - case MEDIA_IMS_RX_NOTICE: - final OnImsRxNoticeListener imsRxNoticeListener; - final Handler imsRxNoticeHandler; - imsRxNoticeListener = mOnImsRxNoticeListener; - imsRxNoticeHandler = mOnImsRxNoticeHandler; - if (imsRxNoticeListener == null) { + case MEDIA_RTP_RX_NOTICE: + final OnRtpRxNoticeListener rtpRxNoticeListener = mOnRtpRxNoticeListener; + final Handler rtpRxNoticeHandler = mOnRtpRxNoticeHandler; + if (rtpRxNoticeListener == null) { return; } if (msg.obj instanceof Parcel) { Parcel parcel = (Parcel) msg.obj; - byte[] event; + parcel.setDataPosition(0); + int noticeType; + int[] data; try { - event = parcel.marshall(); + noticeType = parcel.readInt(); + int numOfArgs = parcel.dataAvail() / 4; + data = new int[numOfArgs]; + for (int i = 0; i < numOfArgs; i++) { + data[i] = parcel.readInt(); + } } finally { parcel.recycle(); } - if (imsRxNoticeHandler == null) { - imsRxNoticeListener.onImsRxNotice(mMediaPlayer, event); + if (rtpRxNoticeHandler == null) { + rtpRxNoticeListener.onRtpRxNotice(mMediaPlayer, noticeType, data); } else { - imsRxNoticeHandler.post(new Runnable() { - @Override - public void run() { - imsRxNoticeListener.onImsRxNotice(mMediaPlayer, event); - } - }); + rtpRxNoticeHandler.post( + () -> + rtpRxNoticeListener + .onRtpRxNotice(mMediaPlayer, noticeType, data)); } } return; @@ -4103,18 +4110,18 @@ public class MediaPlayer extends PlayerBase /** * Interface definition of a callback to be invoked when - * IMS Rx connection has a notice. + * RTP Rx connection has a notice. * - * @see MediaPlayer.setOnImsRxNoticeListener + * @see #setOnRtpRxNoticeListener * * @hide */ @SystemApi - public interface OnImsRxNoticeListener + public interface OnRtpRxNoticeListener { /** - * Called to indicate an IMS event noticed from native media frameworks. - *

+ * Called when an RTP Rx connection has a notice. + *

* Basic format. All TYPE and ARG are 4 bytes unsigned integer in native byte order. *

{@code
          * 0                4               8                12
@@ -4169,14 +4176,14 @@ public class MediaPlayer extends PlayerBase
          * TYPE 205 - Transport layer Feedback message. (RFC-5104 Sec.4.2)
          * 0                4               8                12
          * +----------------+---------------+----------------+----------------+
-         * |      205       |      SSRC     | FB type(1 or 3)|     value      |
+         * |      205       |FB type(1 or 3)|      SSRC      |      Value     |
          * +----------------+---------------+----------------+----------------+
-         * SSRC
-         *      - Remote side's SSRC value of the media sender (RFC-3550 Sec.5.1)
          * Feedback (FB) type: determines the type of the event.
          *      - if 1, we received a NACK request from the remote side.
          *      - if 3, we received a TMMBR (Temporary Maximum Media Stream Bit Rate Request) from
          *        the remote side.
+         * SSRC
+         *      - Remote side's SSRC value of the media sender (RFC-3550 Sec.5.1)
          * Value: the FCI (Feedback Control Information) depending on the value of FB type
          *      - if FB type is 1, the Generic NACK as specified in RFC-4585 Sec.6.2.1
          *      - if FB type is 3, the TMMBR as specified in RFC-5104 Sec.4.2.1.1
@@ -4185,13 +4192,13 @@ public class MediaPlayer extends PlayerBase
          * TYPE 206 - Payload-specific Feedback message. (RFC-5104 Sec.4.3)
          * 0                4               8
          * +----------------+---------------+----------------+
-         * |      206       |      SSRC     | FB type(1 or 4)|
+         * |      206       |FB type(1 or 4)|      SSRC      |
          * +----------------+---------------+----------------+
-         * SSRC
-         *      - Remote side's SSRC value of the media sender (RFC-3550 Sec.5.1)
          * Feedback (FB) type: determines the type of the event.
          *      - if 1, we received a PLI request from the remote side.
          *      - if 4, we received a FIR request from the remote side.
+         * SSRC
+         *      - Remote side's SSRC value of the media sender (RFC-3550 Sec.5.1)
          *
          *
          * TYPE 300 - CVO (RTP Extension) message.
@@ -4212,34 +4219,39 @@ public class MediaPlayer extends PlayerBase
          * }
* * @param mp the {@code MediaPlayer} associated with this callback. - * @param event an IMS media event serialized as byte[] array. + * @param noticeType TYPE of the event. + * @param params RTP Rx media data serialized as int[] array. */ - void onImsRxNotice(@NonNull MediaPlayer mp, @NonNull byte[] event); + void onRtpRxNotice(@NonNull MediaPlayer mp, int noticeType, @NonNull int[] params); } /** - * Register a callback to be invoked when IMS Rx connection has a notice. - * The callback required if mediaplayer configured for RTPSource by - * MediaPlayer.setDataSource(String8 rtpParams) of mediaplayer.h + * Sets the listener to be invoked when an RTP Rx connection has a notice. + * The listener is required if MediaPlayer is configured for RTPSource by + * MediaPlayer.setDataSource(String8 rtpParams) of mediaplayer.h. * - * @see MediaPlayer.OnImsRxNoticeListener + * @see OnRtpRxNoticeListener * - * @param listener the callback that will be run - * @param handler Specifies Handler object for the thread on which to execute - * the callback. If null, the handler on the main looper will be used. + * @param listener the listener called after a notice from RTP Rx + * @param handler the {@link Handler} that receives RTP Tx events * * @hide */ @SystemApi @RequiresPermission("android.permission.BIND_IMS_SERVICE") - public void setOnImsRxNoticeListener( - @Nullable OnImsRxNoticeListener listener, @Nullable Handler handler) { - mOnImsRxNoticeListener = listener; - mOnImsRxNoticeHandler = handler; - } - - private OnImsRxNoticeListener mOnImsRxNoticeListener; - private Handler mOnImsRxNoticeHandler; + public void setOnRtpRxNoticeListener( + @NonNull Context context, + @NonNull OnRtpRxNoticeListener listener, @Nullable Handler handler) { + Objects.requireNonNull(context); + Preconditions.checkArgument( + context.checkSelfPermission(BIND_IMS_SERVICE) == PERMISSION_GRANTED, + "android.permission.BIND_IMS_SERVICE permission not granted."); + mOnRtpRxNoticeListener = Objects.requireNonNull(listener); + mOnRtpRxNoticeHandler = handler; + } + + private OnRtpRxNoticeListener mOnRtpRxNoticeListener; + private Handler mOnRtpRxNoticeHandler; /** * Register a callback to be invoked when a selected track has timed metadata available. -- GitLab