diff --git a/packages/CaptivePortalLogin/Android.bp b/packages/CaptivePortalLogin/Android.bp
deleted file mode 100644
index 1f6c2aef657ca..0000000000000
--- a/packages/CaptivePortalLogin/Android.bp
+++ /dev/null
@@ -1,43 +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_defaults {
- name: "CaptivePortalLoginDefaults",
- srcs: ["src/**/*.java"],
- sdk_version: "system_current",
- min_sdk_version: "28",
- static_libs: [
- "androidx.legacy_legacy-support-v4",
- "metrics-constants-protos",
- "captiveportal-lib",
- ],
- manifest: "AndroidManifest.xml",
-}
-
-android_app {
- name: "CaptivePortalLogin",
- defaults: ["CaptivePortalLoginDefaults"],
- certificate: "networkstack",
-}
-
-// Alternative CaptivePortalLogin signed with the platform cert, to use
-// with InProcessNetworkStack.
-android_app {
- name: "PlatformCaptivePortalLogin",
- defaults: ["CaptivePortalLoginDefaults"],
- certificate: "platform",
- overrides: ["CaptivePortalLogin"],
-}
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
deleted file mode 100644
index 86d6d442c6728..0000000000000
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/CaptivePortalLogin/OWNERS b/packages/CaptivePortalLogin/OWNERS
deleted file mode 100644
index 52193eb2a12b7..0000000000000
--- a/packages/CaptivePortalLogin/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-set noparent
-
-lorenzo@google.com
-baligh@google.com
-delphij@google.com
diff --git a/packages/CaptivePortalLogin/assets/quantum_ic_warning_amber_96.png b/packages/CaptivePortalLogin/assets/quantum_ic_warning_amber_96.png
deleted file mode 100644
index 08294cee45875..0000000000000
Binary files a/packages/CaptivePortalLogin/assets/quantum_ic_warning_amber_96.png and /dev/null differ
diff --git a/packages/CaptivePortalLogin/res/drawable/app_icon.xml b/packages/CaptivePortalLogin/res/drawable/app_icon.xml
deleted file mode 100644
index 456ca83f52270..0000000000000
--- a/packages/CaptivePortalLogin/res/drawable/app_icon.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/CaptivePortalLogin/res/drawable/maybe_wifi.xml b/packages/CaptivePortalLogin/res/drawable/maybe_wifi.xml
deleted file mode 100644
index 207aade406efa..0000000000000
--- a/packages/CaptivePortalLogin/res/drawable/maybe_wifi.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
diff --git a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml b/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml
deleted file mode 100644
index f095d4e15b01e..0000000000000
--- a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml b/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
deleted file mode 100644
index ce05e78757a1b..0000000000000
--- a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/CaptivePortalLogin/res/menu/captive_portal_login.xml b/packages/CaptivePortalLogin/res/menu/captive_portal_login.xml
deleted file mode 100644
index 1a88c5c846cc2..0000000000000
--- a/packages/CaptivePortalLogin/res/menu/captive_portal_login.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
diff --git a/packages/CaptivePortalLogin/res/values-af/strings.xml b/packages/CaptivePortalLogin/res/values-af/strings.xml
deleted file mode 100644
index 37acf457ff421..0000000000000
--- a/packages/CaptivePortalLogin/res/values-af/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortal-aanmelding"
- "Gebruik hierdie netwerk nes dit is"
- "Moenie hierdie netwerk gebruik nie"
- "Meld by netwerk aan"
- "Meld aan by %1$s"
- "Die netwerk waarby jy probeer aansluit, het sekuriteitkwessies."
- "Byvoorbeeld, die aanmeldbladsy behoort dalk nie aan die organisasie wat gewys word nie."
- "Gaan in elk geval deur blaaier voort"
- "Hierdie sertifikaat is nie van \'n betroubare owerheid nie."
- "Die naam van die werf stem nie ooreen met die naam op die sertifikaat nie."
- "Hierdie sertifikaat het verval."
- "Hierdie sertifikaat is nog nie geldig nie."
- "Hierdie sertifikaat het \'n ongeldige datum."
- "Hierdie sertifikaat is ongeldig."
- "Onbekende sertifikaatfout."
- "Sekuriteitswaarskuwing"
- "Bekyk sertifikaat"
- "OK"
- "Adres:"
- "Bladsy-inligting"
-
diff --git a/packages/CaptivePortalLogin/res/values-am/strings.xml b/packages/CaptivePortalLogin/res/values-am/strings.xml
deleted file mode 100644
index 4e35e1970cd08..0000000000000
--- a/packages/CaptivePortalLogin/res/values-am/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "ይህን አውታረ መረብ እንዳለ ተጠቀምበት"
- "ይህን አውታረ መረብ አትጠቀምበት"
- "ወደ አውታረ መረብ በመለያ ይግቡ"
- "ወደ %1$s ይግቡ"
- "ለመቀላቀል እየሞከሩ ያሉት አውታረ መረብ የደህንነት ችግሮች አሉበት።"
- "ለምሳሌ፣ የመግቢያ ገጹ የሚታየው ድርጅት ላይሆን ይችላል።"
- "ለማንኛውም በአሳሽ በኩል ይቀጥሉ"
- "ይህ የእውቅና ማረጋገጫ ከታማኝ ቦታ አይደለም።"
- "የጣቢያው ስም ከእውቅና ማረጋገጫው ስም ጋር አይዛመድም።"
- "ይህ የእውቅና ማረጋገጫ ጊዜው አልፏል።"
- "ይህ የእውቅና ማረጋገጫ ገና የሚሰራ አይደለም።"
- "ይህ የእውቅና ማረጋገጫ ልክ ያልሆነ ቀን ነው ያለው።"
- "ይህ የእውቅና ማረጋገጫ ልክ ያልሆነ ነው።"
- "ያልታወቀ የእውቅና ማረጋገጫ ስህተት።"
- "የደህንነት ቅንብሮች"
- "የእውቅና ማረጋገጫን ይመልከቱ"
- "እሺ"
- "አድራሻ፦"
- "የገጽ መረጃ"
-
diff --git a/packages/CaptivePortalLogin/res/values-ar/strings.xml b/packages/CaptivePortalLogin/res/values-ar/strings.xml
deleted file mode 100644
index 9f3aefa5467fb..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ar/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
- "CaptivePortalLogin"
- "استخدام هذه الشبكة كما هي"
- "عدم استخدام هذه الشبكة"
- "تسجيل الدخول إلى الشبكة"
-
-
-
- "الشبكة التي تحاول الانضمام إليها بها مشاكل أمنية."
- "على سبيل المثال، قد لا تنتمي صفحة تسجيل الدخول إلى المنظمة المعروضة."
- "المتابعة على أي حال عبر المتصفح"
- "هذه الشهادة ليست من جهة موثوق بها."
- "لا يتطابق اسم الموقع مع الاسم على الشهادة."
- "انتهت صلاحية هذه الشهادة."
- "هذه الشهادة ليست صالحة بعد."
- "تشتمل هذه الشهادة على تاريخ غير صالح."
- "هذه الشهادة غير صالحة."
- "حدث خطأ غير معروف بالشهادة."
- "تحذير أمني"
- "عرض الشهادة"
- "حسنًا"
- "العنوان:"
- "معلومات الصفحة"
-
diff --git a/packages/CaptivePortalLogin/res/values-as/strings.xml b/packages/CaptivePortalLogin/res/values-as/strings.xml
deleted file mode 100644
index 696483c051754..0000000000000
--- a/packages/CaptivePortalLogin/res/values-as/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "এই নেটৱৰ্কটো এইদৰে ব্যৱহাৰ কৰক"
- "এই নেটৱৰ্কটো ব্যৱহাৰ নকৰিব"
- "নেটৱৰ্কত ছাইন ইন কৰক"
- "%1$st ছাইন ইন কৰক"
- "আপুনি সংযোগ কৰিবলৈ চেষ্টা কৰি থকা নেটৱৰ্কটোত সুৰক্ষাজনিত সমস্যা আছে।"
- "উদাহৰণস্বৰূপে, আপোনাক দেখুওৱা লগ ইনৰ পৃষ্ঠাটো প্ৰতিষ্ঠানটোৰ নিজা নহ\'বও পাৰে।"
- "তথাপি ব্ৰাউজাৰৰ জৰিয়তে অব্যাহত ৰাখক"
- "এই প্ৰমাণপত্ৰখন কোনো বিশ্বাসী কর্তৃপক্ষৰ নহয়।"
- "ৱেবচাইটটোৰ নাম আৰু প্ৰমাণপত্ৰখনত থকা নামটোৰ মিল নাই।"
- "এই প্ৰমাণপত্ৰখনৰ ম্যাদ উকলি গৈছে।"
- "এই প্ৰমাণপত্ৰখন এই পর্যন্ত মান্য হোৱা নাই।"
- "এই প্ৰমাণপত্ৰখনত থকা তাৰিখটো মান্য নহয়।"
- "এই প্ৰমাণপত্ৰখন মান্য নহয়।"
- "প্ৰমাণপত্ৰত অজ্ঞাত আসোঁৱাহ।"
- "সুৰক্ষা সম্পর্কীয় সতৰ্কতা"
- "প্ৰমাণপত্ৰ চাওক"
- "ঠিক আছে"
- "ঠিকনা:"
- "পৃষ্ঠাৰ তথ্য"
-
diff --git a/packages/CaptivePortalLogin/res/values-az/strings.xml b/packages/CaptivePortalLogin/res/values-az/strings.xml
deleted file mode 100644
index f002544a61739..0000000000000
--- a/packages/CaptivePortalLogin/res/values-az/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Bu şəbəkəni olduğu kimi istifadə edin"
- "Bu şəbəkəni istifadə etməyin"
- "Şəbəkəyə daxil olun"
- "Daxil olun: %1$s"
- "Qoşulmaq istədiyiniz şəbəkənin təhlükəsizlik problemləri var."
- "Məsələn, giriş səhifəsi göstərilən təşkilata aid olmaya bilər."
- "Hər bir halda brazuer ilə davam edin"
- "Bu sertifikat etibarlı orqan tərəfindən verilməyib."
- "Saytın adı sertifikatdakı ada uyğun deyil."
- "Bu sertifikatın müddəti bitib."
- "Bu sertifikat hələ etibarlı deyil."
- "Bu sertifikatın tarixi yanlışdır."
- "Bu sertifikat etibarsızdır."
- "Bilinməyən sertifikat xətası."
- "Təhlükəsizlik xəbərdarlığı"
- "Sertifikata baxın"
- "OK"
- "Ünvan:"
- "Səhifə məlumatı"
-
diff --git a/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml b/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index 401548b57da80..0000000000000
--- a/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Koristi ovu mrežu takvu kakva je"
- "Ne koristi ovu mrežu"
- "Prijavi me na mrežu"
- "Prijavite se u: %1$s"
- "Mreža kojoj pokušavate da se pridružite ima bezbednosnih problema."
- "Na primer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."
- "Ipak nastavi preko pregledača"
- "Ovaj sertifikat ne potiče iz pouzdanog izvora."
- "Naziv sajta se ne podudara sa nazivom na sertifikatu."
- "Ovaj sertifikat je istekao."
- "Ovaj sertifikat još uvek nije važeći."
- "Datum ovog sertifikata je nevažeći."
- "Ovaj sertifikat je nevažeći."
- "Nepoznata greška sertifikata."
- "Bezbednosno upozorenje"
- "Prikaži sertifikat"
- "Potvrdi"
- "Adresa:"
- "Informacije o stranici"
-
diff --git a/packages/CaptivePortalLogin/res/values-be/strings.xml b/packages/CaptivePortalLogin/res/values-be/strings.xml
deleted file mode 100644
index 5f2bd90a6811b..0000000000000
--- a/packages/CaptivePortalLogin/res/values-be/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Выкарыстоўваць гэтую сетку як ёсць"
- "Не выкарыстоўваць гэту сетку"
- "Увайсці ў сетку"
- "Увайсці ў %1$s"
- "У сеткі, да якой вы спрабуеце далучыцца, ёсць праблемы з бяспекай."
- "Напрыклад, старонка ўваходу можа не належаць указанай арганізацыі."
- "Усё роўна працягнуць праз браўзер"
- "Гэты сертыфікат не выдадзены давераным цэнтрам."
- "Назва сайта не адпавядае назве ў сертыфікаце."
- "Тэрмін дзеяння сертыфіката мінуў."
- "Гэты сертыфікат яшчэ не дзейнічае."
- "Гэты сертыфікат мае несапраўдную дату."
- "Гэты сертыфікат несапраўдны."
- "Невядомая памылка сертыфіката."
- "Папярэджанне сістэмы бяспекі"
- "Прагледзець сертыфікат"
- "ОК"
- "Адрас:"
- "Інфармацыя пра старонку"
-
diff --git a/packages/CaptivePortalLogin/res/values-bg/strings.xml b/packages/CaptivePortalLogin/res/values-bg/strings.xml
deleted file mode 100644
index c5354e8794e2c..0000000000000
--- a/packages/CaptivePortalLogin/res/values-bg/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Директно използване на тази мрежа"
- "Без използване на тази мрежа"
- "Вход в мрежата"
- "Влезте в/ъв %1$s"
- "Мрежата, към която опитвате да се присъедините, има проблеми със сигурността."
- "Например страницата за вход може да не принадлежи на показаната организация."
- "Продължаване през браузър въпреки това"
- "Този сертификат не е от надежден орган."
- "Името на сайта не съответства на името в сертификата."
- "Този сертификат е изтекъл."
- "Този сертификат още не е валиден."
- "Този сертификат е с невалидна дата."
- "Този сертификат е невалиден."
- "Неизвестна грешка в сертификата."
- "Предупреждение относно сигурността"
- "Преглед на сертификата"
- "OK"
- "Адрес:"
- "Данни за страницата"
-
diff --git a/packages/CaptivePortalLogin/res/values-bn/strings.xml b/packages/CaptivePortalLogin/res/values-bn/strings.xml
deleted file mode 100644
index 3fbe2f26cdb1d..0000000000000
--- a/packages/CaptivePortalLogin/res/values-bn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "যেভাবে আছে সেভাবেই এই নেটওয়ার্ক ব্যবহার করুন"
- "এই নেটওয়ার্ক ব্যবহার করবেন না"
- "নেটওয়ার্কে সাইন-ইন করুন"
- "%1$s তে সাইন-ইন করুন"
- "আপনি যে নেটওয়ার্কে যোগ দেওয়ার চেষ্টা করছেন তাতে নিরাপত্তার সমস্যা আছে।"
- "উদাহরণস্বরূপ, লগ-ইন পৃষ্ঠাটি প্রদর্শিত প্রতিষ্ঠানের অন্তর্গত নাও হতে পারে৷"
- "যাই হোক না কেন ব্রাউজারের মাধ্যমে অবিরত রাখুন"
- "এই সার্টিফিকেটটি একটি বিশ্বস্ত কর্তৃপক্ষের তরফ থেকে নয়।"
- "সাইটের নামটি সার্টিফিকেটে লেখা নামের সাথে মিলছে না।"
- "এই সার্টিফিকেটের মেয়াদ শেষ হয়ে গেছে।"
- "সার্টিফিকেটটি এখনও সঠিক নয়।"
- "এই সার্টিফিকেটের তারিখটি সঠিক নয়।"
- "সার্টিফিকেটটি সঠিক নয়।"
- "সার্টিফিকেটের সমস্যাটি অজানা।"
- "নিরাপত্তার সতর্কতা"
- "সার্টিফিকেট দেখুন"
- "ঠিক আছে"
- "ঠিকানা:"
- "পৃষ্ঠার তথ্য"
-
diff --git a/packages/CaptivePortalLogin/res/values-bs/strings.xml b/packages/CaptivePortalLogin/res/values-bs/strings.xml
deleted file mode 100644
index 83b5067235e2b..0000000000000
--- a/packages/CaptivePortalLogin/res/values-bs/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "Prijava na zaštitnom portalu"
- "Koristi ovu mrežu kakva jeste"
- "Ne koristi ovu mrežu"
- "Prijava na mrežu"
- "Prijava na %1$s"
- "Mreža kojoj pokušavate pristupiti ima sigurnosnih problema."
- "Naprimjer, stranica za prijavu možda ne pripada prikazanoj organizaciji."
- "Ipak nastavi preko preglednika"
- "Ova potvrda ne potiče iz pouzdanog izvora."
- "Naziv web lokacije se ne podudara s nazivom na potvrdi."
- "Ova potvrda je istekla."
- "Ova potvrda još uvijek nije važeća."
- "Ova potvrda sadrži nevažeći datum."
- "Ova potvrda je nevažeća."
- "Nepoznata greška potvrde."
- "Sigurnosno upozorenje"
- "Prikaži potvrdu"
- "Uredu"
- "Adresa:"
- "Informacije o stranici"
-
diff --git a/packages/CaptivePortalLogin/res/values-ca/strings.xml b/packages/CaptivePortalLogin/res/values-ca/strings.xml
deleted file mode 100644
index 557c3f467acc8..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ca/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Fes servir aquesta xarxa tal com està."
- "No facis servir aquesta xarxa."
- "Inicia la sessió a la xarxa"
- "Inicia la sessió a %1$s"
- "La xarxa a què et vols connectar té problemes de seguretat."
- "Per exemple, la pàgina d\'inici de sessió podria no pertànyer a l\'organització que es mostra."
- "Continua igualment mitjançant el navegador"
- "Aquest certificat no és d\'una autoritat de confiança."
- "El nom del lloc web no coincideix amb el que inclou el certificat."
- "Aquest certificat ha caducat."
- "Aquest certificat encara no és vàlid."
- "Aquest certificat té una data no vàlida."
- "Aquest certificat no és vàlid."
- "S\'ha produït un error desconegut de certificat."
- "Advertiment de seguretat"
- "Mostra el certificat"
- "D\'acord"
- "Adreça:"
- "Informació de la pàgina"
-
diff --git a/packages/CaptivePortalLogin/res/values-cs/strings.xml b/packages/CaptivePortalLogin/res/values-cs/strings.xml
deleted file mode 100644
index 0328c7691dabd..0000000000000
--- a/packages/CaptivePortalLogin/res/values-cs/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Použít tuto síť tak, jak je"
- "Tuto síť nepoužívat"
- "Přihlásit se k síti"
- "Přihlaste se k síti %1$s"
- "Síť, ke které se pokoušíte připojit, má bezpečnostní problémy."
- "Například přihlašovací stránka nemusí patřit do zobrazované organizace."
- "Přesto pokračovat prostřednictvím prohlížeče"
- "Tento certifikát nepochází od důvěryhodné autority."
- "Název webu se neshoduje s názvem uvedeným v certifikátu."
- "Platnost certifikátu vypršela."
- "Tento certifikát ještě není platný."
- "Datum tohoto certifikátu není platné."
- "Tento certifikát je neplatný."
- "Neznámá chyba certifikátu."
- "Bezpečnostní upozornění"
- "Zobrazit certifikát"
- "OK"
- "Adresa:"
- "Informace o stránce"
-
diff --git a/packages/CaptivePortalLogin/res/values-da/strings.xml b/packages/CaptivePortalLogin/res/values-da/strings.xml
deleted file mode 100644
index b9cf7fe93f953..0000000000000
--- a/packages/CaptivePortalLogin/res/values-da/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "Login til captive portal"
- "Brug dette netværk, som det er"
- "Brug ikke dette netværk"
- "Log ind på netværk"
- "Log ind på %1$s"
- "Der er sikkerhedsproblemer på det netværk, du forsøger at logge ind på."
- "Det er f.eks. ikke sikkert, at loginsiden tilhører den anførte organisation."
- "Fortsæt alligevel via browseren"
- "Dette certifikat stammer ikke fra en pålidelig kilde."
- "Navnet på websitet stemmer ikke overens med navnet på certifikatet."
- "Dette certifikat er udløbet."
- "Dette certifikat er ikke gyldigt endnu."
- "Dette certifikat har en ugyldig dato."
- "Dette certifikat er ugyldigt."
- "Ukendt certifikatfejl."
- "Sikkerhedsadvarsel"
- "Se certifikat"
- "OK"
- "Adresse:"
- "Sideoplysninger"
-
diff --git a/packages/CaptivePortalLogin/res/values-de/strings.xml b/packages/CaptivePortalLogin/res/values-de/strings.xml
deleted file mode 100644
index 4276bf9c2be98..0000000000000
--- a/packages/CaptivePortalLogin/res/values-de/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Netzwerk im Istzustand verwenden"
- "Netzwerk nicht verwenden"
- "Im Netzwerk anmelden"
- "In %1$s anmelden"
- "Im Netzwerk, zu dem du eine Verbindung herstellen möchtest, liegen Sicherheitsprobleme vor."
- "Beispiel: Die Log-in-Seite gehört eventuell nicht zur angezeigten Organisation."
- "Trotzdem in einem Browser fortfahren"
- "Dieses Zertifikat stammt nicht von einer vertrauenswürdigen Stelle."
- "Der Name der Website stimmt nicht mit dem Namen auf dem Zertifikat überein."
- "Dieses Zertifikat ist abgelaufen."
- "Dieses Zertifikat ist noch nicht gültig."
- "Dieses Zertifikat hat ein ungültiges Datum."
- "Dieses Zertifikat ist ungültig."
- "Unbekannter Zertifikatfehler."
- "Sicherheitswarnung"
- "Zertifikat ansehen"
- "Ok"
- "Adresse:"
- "Seiteninformationen"
-
diff --git a/packages/CaptivePortalLogin/res/values-el/strings.xml b/packages/CaptivePortalLogin/res/values-el/strings.xml
deleted file mode 100644
index f4de7c39ffbb7..0000000000000
--- a/packages/CaptivePortalLogin/res/values-el/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Χρήση αυτού του δικτύου ως έχει"
- "Να μη χρησιμοποιείται αυτό το δίκτυο"
- "Σύνδεση στο δίκτυο"
- "Συνδεθείτε στο %1$s"
- "Παρουσιάζονται προβλήματα ασφάλειας στο δίκτυο στο οποίο προσπαθείτε να συνδεθείτε."
- "Για παράδειγμα, η σελίδα σύνδεσης ενδέχεται να μην ανήκει στον οργανισμό που εμφανίζεται."
- "Συνέχεια ούτως ή άλλως μέσω του προγράμματος περιήγησης"
- "Αυτό το πιστοποιητικό δεν προέρχεται από αξιόπιστη αρχή."
- "Το όνομα του ιστοτόπου δεν αντιστοιχεί στο όνομα στο πιστοποιητικό."
- "Αυτό το πιστοποιητικό έχει λήξει."
- "Αυτό το πιστοποιητικό δεν είναι έγκυρο ακόμα."
- "Αυτό το πιστοποιητικό δεν έχει έγκυρη ημερομηνία."
- "Αυτό το πιστοποιητικό δεν είναι έγκυρο."
- "Άγνωστο σφάλμα πιστοποιητικού."
- "Προειδοποίηση ασφαλείας"
- "Προβολή πιστοποιητικού"
- "ΟΚ"
- "Διεύθυνση:"
- "Πληροφορίες σελίδας"
-
diff --git a/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml b/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml
deleted file mode 100644
index 976333901b436..0000000000000
--- a/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Use this network as is"
- "Do not use this network"
- "Sign in to network"
- "Sign in to %1$s"
- "The network that you’re trying to join has security issues."
- "For example, the login page might not belong to the organisation shown."
- "Continue anyway via browser"
- "This certificate isn\'t from a trusted authority."
- "The name of the site doesn\'t match the name on the certificate."
- "This certificate has expired."
- "This certificate isn\'t valid yet."
- "This certificate has an invalid date."
- "This certificate is invalid."
- "Unknown certificate error."
- "Security warning"
- "View certificate"
- "OK"
- "Address:"
- "Page info"
-
diff --git a/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml b/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml
deleted file mode 100644
index 976333901b436..0000000000000
--- a/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Use this network as is"
- "Do not use this network"
- "Sign in to network"
- "Sign in to %1$s"
- "The network that you’re trying to join has security issues."
- "For example, the login page might not belong to the organisation shown."
- "Continue anyway via browser"
- "This certificate isn\'t from a trusted authority."
- "The name of the site doesn\'t match the name on the certificate."
- "This certificate has expired."
- "This certificate isn\'t valid yet."
- "This certificate has an invalid date."
- "This certificate is invalid."
- "Unknown certificate error."
- "Security warning"
- "View certificate"
- "OK"
- "Address:"
- "Page info"
-
diff --git a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml b/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
deleted file mode 100644
index 976333901b436..0000000000000
--- a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Use this network as is"
- "Do not use this network"
- "Sign in to network"
- "Sign in to %1$s"
- "The network that you’re trying to join has security issues."
- "For example, the login page might not belong to the organisation shown."
- "Continue anyway via browser"
- "This certificate isn\'t from a trusted authority."
- "The name of the site doesn\'t match the name on the certificate."
- "This certificate has expired."
- "This certificate isn\'t valid yet."
- "This certificate has an invalid date."
- "This certificate is invalid."
- "Unknown certificate error."
- "Security warning"
- "View certificate"
- "OK"
- "Address:"
- "Page info"
-
diff --git a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml b/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
deleted file mode 100644
index 976333901b436..0000000000000
--- a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Use this network as is"
- "Do not use this network"
- "Sign in to network"
- "Sign in to %1$s"
- "The network that you’re trying to join has security issues."
- "For example, the login page might not belong to the organisation shown."
- "Continue anyway via browser"
- "This certificate isn\'t from a trusted authority."
- "The name of the site doesn\'t match the name on the certificate."
- "This certificate has expired."
- "This certificate isn\'t valid yet."
- "This certificate has an invalid date."
- "This certificate is invalid."
- "Unknown certificate error."
- "Security warning"
- "View certificate"
- "OK"
- "Address:"
- "Page info"
-
diff --git a/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml b/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml
deleted file mode 100644
index 8b3ca29357e1c..0000000000000
--- a/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Use this network as is"
- "Do not use this network"
- "Sign in to network"
- "Sign in to %1$s"
- "The network you’re trying to join has security issues."
- "For example, the login page may not belong to the organization shown."
- "Continue anyway via browser"
- "This certificate isn\'t from a trusted authority."
- "The name of the site doesn\'t match the name on the certificate."
- "This certificate has expired."
- "This certificate isn\'t valid yet."
- "This certificate has an invalid date."
- "This certificate is invalid."
- "Unknown certificate error."
- "Security warning"
- "View certificate"
- "OK"
- "Address:"
- "Page info"
-
diff --git a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml b/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
deleted file mode 100644
index d7ddaa75b2d34..0000000000000
--- a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Usar esta red como está"
- "No usar esta red"
- "Acceder a la red"
- "Acceder a %1$s"
- "La red a la que intentas conectarte tiene problemas de seguridad."
- "Por ejemplo, es posible que la página de acceso no pertenezca a la organización que aparece."
- "Continuar de todos modos desde el navegador"
- "Este certificado no proviene de una autoridad confiable."
- "El nombre del sitio no coincide con el nombre del certificado."
- "Este certificado venció."
- "Este certificado aún no es válido."
- "La fecha de este certificado no es válida."
- "Este certificado no es válido."
- "Error de certificado desconocido"
- "Advertencia de seguridad"
- "Ver certificado"
- "Aceptar"
- "Dirección:"
- "Información de la página"
-
diff --git a/packages/CaptivePortalLogin/res/values-es/strings.xml b/packages/CaptivePortalLogin/res/values-es/strings.xml
deleted file mode 100644
index e10380f1147e8..0000000000000
--- a/packages/CaptivePortalLogin/res/values-es/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Utilizar esta red tal cual"
- "No utilizar esta red"
- "Iniciar sesión en la red"
- "Inicia sesión en %1$s"
- "La red a la que intentas unirte tiene problemas de seguridad."
- "Por ejemplo, es posible que la página de inicio de sesión no pertenezca a la organización mostrada."
- "Continuar de todos modos a través del navegador"
- "Este certificado no procede de una entidad de confianza."
- "El nombre del sitio web no coincide con el del certificado."
- "Este certificado ha caducado."
- "Este certificado aún no es válido."
- "La fecha de este certificado no es válida."
- "Este certificado no es válido."
- "Error de certificado desconocido."
- "Advertencia de seguridad"
- "Ver certificado"
- "Aceptar"
- "Dirección:"
- "Información de la página"
-
diff --git a/packages/CaptivePortalLogin/res/values-et/strings.xml b/packages/CaptivePortalLogin/res/values-et/strings.xml
deleted file mode 100644
index 2894383c678f7..0000000000000
--- a/packages/CaptivePortalLogin/res/values-et/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Kasuta seda võrku olemasoleval kujul"
- "Ära kasuta seda võrku"
- "Logi võrku sisse"
- "Logige sisse: %1$s"
- "Võrgul, millega üritate ühenduse luua, on turvaprobleeme."
- "Näiteks ei pruugi sisselogimisleht kuuluda kuvatavale organisatsioonile."
- "Jätka siiski brauseris"
- "See sertifikaat ei pärine usaldusväärselt asutuselt."
- "Saidi nimi ei vasta sertifikaadil olevale nimele."
- "See sertifikaat on aegunud."
- "See sertifikaat pole veel kehtiv."
- "Sellel sertifikaadil on kehtetu kuupäev."
- "See sertifikaat on kehtetu."
- "Tundmatu sertifikaadiviga."
- "Turvahoiatus"
- "Kuva sertifikaat"
- "OK"
- "Aadress:"
- "Lehe teave"
-
diff --git a/packages/CaptivePortalLogin/res/values-eu/strings.xml b/packages/CaptivePortalLogin/res/values-eu/strings.xml
deleted file mode 100644
index b7233ab68fb04..0000000000000
--- a/packages/CaptivePortalLogin/res/values-eu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Erabili sare hau bere horretan"
- "Ez erabili sare hau"
- "Hasi saioa sarean"
- "Hasi saioa %1$s sarean"
- "Erabili nahi duzun sareak segurtasun-arazoak ditu."
- "Adibidez, baliteke saioa hasteko orria adierazitako erakundearena ez izatea."
- "Jarraitu arakatzailearen bidez, halere"
- "Ziurtagiria ez da autoritate fidagarri batena."
- "Webgunearen izena ez dator bat ziurtagirian agertzen den izenarekin."
- "Ziurtagiria iraungi da."
- "Ziurtagiriak ez du balio oraindik."
- "Ziurtagiriaren datak ez du balio."
- "Ziurtagiriak ez du balio."
- "Errore ezezagun bat gertatu da ziurtagiriarekin."
- "Segurtasun-abisua"
- "Ikusi ziurtagiria"
- "Ados"
- "Helbidea:"
- "Orriari buruzko informazioa"
-
diff --git a/packages/CaptivePortalLogin/res/values-fa/strings.xml b/packages/CaptivePortalLogin/res/values-fa/strings.xml
deleted file mode 100644
index db9f7e8ef3a41..0000000000000
--- a/packages/CaptivePortalLogin/res/values-fa/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "از این شبکه همانطور که هست استفاده شود"
- "از این شبکه استفاده نشود"
- "ورود به سیستم شبکه"
- "ورود به سیستم %1$s"
- "شبکهای که میخواهید به آن بپیوندید مشکلات امنیتی دارد."
- "به عنوان مثال، صفحه ورود به سیستم ممکن است متعلق به سازمان نشان داده شده نباشد."
- "در هر صورت از طریق مرورگر ادامه یابد"
- "این گواهی از یک منبع مورداطمینان صادر نشده است."
- "نام سایت با نام موجود در گواهی مطابقت ندارد."
- "این گواهی منقضی شده است."
- "این گواهی هنوز معتبر نیست."
- "تاریخ این گواهی نامعتبر است."
- "این گواهی نامعتبر است."
- "خطای ناشناخته در گواهی."
- "اخطار امنیتی"
- "مشاهده گواهی"
- "تأیید"
- "نشانی:"
- "اطلاعات صفحه"
-
diff --git a/packages/CaptivePortalLogin/res/values-fi/strings.xml b/packages/CaptivePortalLogin/res/values-fi/strings.xml
deleted file mode 100644
index 1d7fcb0efd9ef..0000000000000
--- a/packages/CaptivePortalLogin/res/values-fi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Käytä tätä verkkoa sellaisenaan"
- "Älä käytä tätä verkkoa"
- "Kirjaudu verkkoon"
- "Kirjaudu sisään kohteeseen %1$s"
- "Verkossa, johon yrität muodostaa yhteyttä, on turvallisuusongelmia."
- "Kirjautumissivu ei välttämättä kuulu näytetylle organisaatiolle."
- "Jatka silti selaimen kautta."
- "Varmenteen myöntäjä ei ole luotettava taho."
- "Sivuston nimi ei vastaa varmenteessa olevaa nimeä."
- "Varmenne ei ole enää voimassa."
- "Varmenne ei ole vielä voimassa."
- "Varmenteen päiväys ei kelpaa."
- "Varmenne on virheellinen."
- "Tuntematon varmennevirhe."
- "Suojausvaroitus"
- "Näytä varmenne"
- "OK"
- "Osoite:"
- "Sivun tiedot"
-
diff --git a/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml b/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml
deleted file mode 100644
index 979e236c83a6b..0000000000000
--- a/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Utiliser ce réseau tel quel"
- "Ne pas utiliser ce réseau"
- "Connectez-vous au réseau"
- "Connexion à %1$s"
- "Le réseau que vous essayez de rejoindre présente des problèmes de sécurité."
- "Par exemple, la page de connexion pourrait ne pas appartenir à l\'organisation représentée."
- "Continuer quand même dans un navigateur"
- "Ce certificat ne provient pas d\'une autorité de confiance."
- "Le nom du site ne correspond pas au nom figurant sur le certificat."
- "Ce certificat a expiré."
- "Ce certificat n\'est pas encore valide."
- "Ce certificat contient une date non valide."
- "Ce certificat n\'est pas valide."
- "Erreur de certificat inconnue."
- "Avertissement de sécurité"
- "Afficher le certificat"
- "OK"
- "Adresse :"
- "Renseignements sur la page"
-
diff --git a/packages/CaptivePortalLogin/res/values-fr/strings.xml b/packages/CaptivePortalLogin/res/values-fr/strings.xml
deleted file mode 100644
index e348a4db7745e..0000000000000
--- a/packages/CaptivePortalLogin/res/values-fr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Utiliser ce réseau tel quel"
- "Ne pas utiliser ce réseau"
- "Se connecter au réseau"
- "Se connecter à %1$s"
- "Le réseau que vous essayez de rejoindre présente des problèmes de sécurité."
- "Par exemple, la page de connexion peut ne pas appartenir à l\'organisation représentée."
- "Continuer quand même dans le navigateur"
- "Ce certificat provient d\'une autorité non approuvée."
- "Le nom du site ne correspond pas au nom indiqué dans le certificat."
- "Ce certificat a expiré."
- "Ce certificat n\'est pas encore valide."
- "La date de ce certificat n\'est pas valide."
- "Ce certificat n\'est pas valide."
- "Erreur de certificat inconnue."
- "Avertissement de sécurité"
- "Afficher le certificat"
- "OK"
- "Adresse :"
- "Informations sur la page"
-
diff --git a/packages/CaptivePortalLogin/res/values-gl/strings.xml b/packages/CaptivePortalLogin/res/values-gl/strings.xml
deleted file mode 100644
index 64195168f76fd..0000000000000
--- a/packages/CaptivePortalLogin/res/values-gl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Utilizar esta rede tal como está"
- "Non utilizar esta rede"
- "Inicia sesión na rede"
- "Iniciar sesión en %1$s"
- "A rede á que tentas unirte ten problemas de seguranza."
- "Por exemplo, é posible que a páxina de inicio de sesión non pertenza á organización que se mostra."
- "Continuar igualmente co navegador"
- "Este certificado non procede dunha autoridade de confianza."
- "O nome do sitio non coincide co nome que aparece no certificado."
- "Este certificado caducou."
- "Este certificado aínda non é válido."
- "Este certificado ten unha data non válida."
- "Este certificado non é válido."
- "Produciuse un erro descoñecido relacionado co certificado."
- "Advertencia de seguranza"
- "Ver certificado"
- "Aceptar"
- "Enderezo:"
- "Información da páxina"
-
diff --git a/packages/CaptivePortalLogin/res/values-gu/strings.xml b/packages/CaptivePortalLogin/res/values-gu/strings.xml
deleted file mode 100644
index aef043f2ba583..0000000000000
--- a/packages/CaptivePortalLogin/res/values-gu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "આ નેટવર્કનો જેમનો તેમ ઉપયોગ કરો"
- "આ નેટવર્કનો ઉપયોગ કરશો નહીં"
- "નેટવર્ક પર સાઇન ઇન કરો"
- "%1$sમાં સઇન ઇન કરો"
- "તમે જોડાવાનો પ્રયાસ કરી રહ્યાં છો તે નેટવર્કમાં સુરક્ષા સમસ્યાઓ છે."
- "ઉદાહરણ તરીકે, લોગિન પૃષ્ઠ દર્શાવેલ સંસ્થાનું હોઈ શકતું નથી."
- "બ્રાઉઝર મારફતે કોઈપણ રીતે ચાલુ રાખો"
- "આ પ્રમાણપત્ર વિશ્વસનીય સત્તાધિકારી તરફથી મળ્યું નથી."
- "સાઇટનું નામ એ પ્રમાણપત્ર પર રહેલાં નામ સાથે મેળ ખાતું નથી."
- "આ પ્રમાણપત્રની સમયસીમા સમાપ્ત થઈ ગઈ છે."
- "આ પ્રમાણપત્ર હજુ માન્ય નથી."
- "આ પ્રમાણપત્રમાં અમાન્ય તારીખ છે."
- "આ પ્રમાણપત્ર અમાન્ય છે."
- "પ્રમાણપત્રમાં અજાણી ભૂલ."
- "સુરક્ષા વિશે ચેતવણી"
- "પ્રમાણપત્ર જુઓ"
- "ઓકે"
- "સરનામું:"
- "પેજ વિશે માહિતી"
-
diff --git a/packages/CaptivePortalLogin/res/values-hi/strings.xml b/packages/CaptivePortalLogin/res/values-hi/strings.xml
deleted file mode 100644
index b8ae82abe8e02..0000000000000
--- a/packages/CaptivePortalLogin/res/values-hi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "इस नेटवर्क का उपयोग जैसा है वैसा ही करें"
- "इस नेटवर्क का उपयोग न करें"
- "नेटवर्क में साइन इन करें"
- "%1$s में साइन इन करें"
- "आप जिस नेटवर्क में शामिल होने का प्रयास कर रहे हैं उसमें सुरक्षा समस्याएं हैं."
- "उदाहरण के लिए, हो सकता है कि लॉगिन पृष्ठ दिखाए गए संगठन से संबद्ध ना हो."
- "ब्राउज़र के द्वारा फिर जारी रखें"
- "यह प्रमाणपत्र किसी भरोसेमंद अधिकारी का नहीं है."
- "साइट के नाम का मिलान प्रमाणपत्र में दिए नाम से नहीं हो रहा है."
- "इस प्रमाणपत्र की समय-सीमा खत्म हो चुकी है."
- "यह प्रमाणपत्र अभी तक मान्य नहीं है."
- "इस प्रमाणपत्र की तारीख गलत है."
- "यह प्रमाणपत्र अमान्य है."
- "प्रमाणपत्र में अनजान गड़बड़ी."
- "सुरक्षा चेतावनी"
- "प्रमाणपत्र देखें"
- "ठीक है"
- "पता:"
- "पेज की जानकारी"
-
diff --git a/packages/CaptivePortalLogin/res/values-hr/strings.xml b/packages/CaptivePortalLogin/res/values-hr/strings.xml
deleted file mode 100644
index f6adcdb9238f1..0000000000000
--- a/packages/CaptivePortalLogin/res/values-hr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Upotrebljavaj ovu mrežu u zatečenom stanju"
- "Ne upotrebljavaj ovu mrežu"
- "Prijava na mrežu"
- "Prijavite se na %1$s"
- "Mreža kojoj se pokušavate pridružiti ima sigurnosne poteškoće."
- "Na primjer, stranica za prijavu možda ne pripada prikazanoj organizaciji."
- "Ipak nastavi putem preglednika"
- "Ovaj certifikat ne potječe iz pouzdanog izvora."
- "Naziv web-lokacije ne podudara se s nazivom na certifikatu."
- "Ovaj je certifikat istekao."
- "Ovaj certifikat još nije važeći."
- "Ovaj certifikat ima nevažeći datum."
- "Ovaj certifikat nije važeći."
- "Nepoznata pogreška certifikata."
- "Sigurnosno upozorenje"
- "Pregledajte certifikat"
- "U redu"
- "Adresa:"
- "Informacije o stranici"
-
diff --git a/packages/CaptivePortalLogin/res/values-hu/strings.xml b/packages/CaptivePortalLogin/res/values-hu/strings.xml
deleted file mode 100644
index ab289d1d6fa22..0000000000000
--- a/packages/CaptivePortalLogin/res/values-hu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Hálózat használata jelen állapotában"
- "Ne használja ezt a hálózatot"
- "Bejelentkezés a hálózatba"
- "Bejelentkezés a következőbe: %1$s"
- "Biztonsági problémák vannak azzal a hálózattal, amelyhez csatlakozni szeretne."
- "Például lehet, hogy a bejelentkezési oldal nem a megjelenített szervezethez tartozik."
- "Folytatás ennek ellenére böngészőn keresztül"
- "Ez a tanúsítvány nem hiteles tanúsítványkibocsátótól származik."
- "A webhely neve nem egyezik a tanúsítványon lévő névvel."
- "A tanúsítvány lejárt."
- "A tanúsítvány még nem érvényes."
- "A tanúsítvány dátuma érvénytelen."
- "Ez a tanúsítvány érvénytelen."
- "Ismeretlen tanúsítványhiba."
- "Biztonsági figyelmeztetés"
- "Tanúsítvány megtekintése"
- "OK"
- "Cím:"
- "Oldaladatok"
-
diff --git a/packages/CaptivePortalLogin/res/values-hy/strings.xml b/packages/CaptivePortalLogin/res/values-hy/strings.xml
deleted file mode 100644
index d859d903a5a4b..0000000000000
--- a/packages/CaptivePortalLogin/res/values-hy/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Օգտագործել այս ցանցն ինչպես կա"
- "Չօգտագործել այս ցանցը"
- "Մուտք գործել ցանց"
- "Մուտք գործել %1$s"
- "Ցանցը, որին փորձում եք միանալ, անվտանգության խնդիրներ ունի:"
- "Օրինակ՝ մուտքի էջը կարող է ցուցադրված կազմակերպության էջը չլինել:"
- "Շարունակել այնուամենայնիվ դիտարկիչի միջոցով"
- "Հավաստագրի աղբյուրը վստահելի չէ:"
- "Կայքի անունը չի համապատասխանում հավաստագրում նշված անվանը:"
- "Հավաստագրի ժամկետը լրացել է:"
- "Հավաստագիրը դեռ վավեր չէ:"
- "Հավաստագրի ամսաթիվն անվավեր է:"
- "Հավաստագիրն անվավեր է:"
- "Հավաստագրի հետ կապված անհայտ սխալ առաջացավ:"
- "Զգուշացում"
- "Դիտել հավաստագիրը"
- "Եղավ"
- "Հասցե՝"
- "Էջի մասին"
-
diff --git a/packages/CaptivePortalLogin/res/values-in/strings.xml b/packages/CaptivePortalLogin/res/values-in/strings.xml
deleted file mode 100644
index 7036318e71bd1..0000000000000
--- a/packages/CaptivePortalLogin/res/values-in/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Gunakan jaringan ini sebagaimana adanya"
- "Jangan gunakan jaringan ini"
- "Login ke jaringan"
- "Login ke %1$s"
- "Jaringan yang ingin Anda masuki mengalami masalah keamanan."
- "Misalnya, halaman masuk mungkin bukan milik organisasi yang ditampilkan."
- "Tetap lanjutkan melalui browser"
- "Sertifikat ini tidak berasal dari otoritas tepercaya."
- "Nama situs tidak cocok dengan nama pada sertifikat."
- "Masa berlaku sertifikat ini telah berakhir."
- "Sertifikat ini belum valid."
- "Tanggal sertifikat ini tidak valid."
- "Sertifikat ini tidak valid."
- "Error sertifikat tak dikenal."
- "Peringatan keamanan"
- "Lihat sertifikat"
- "Oke"
- "Alamat:"
- "Info halaman"
-
diff --git a/packages/CaptivePortalLogin/res/values-is/strings.xml b/packages/CaptivePortalLogin/res/values-is/strings.xml
deleted file mode 100644
index 7c2a5e41093d4..0000000000000
--- a/packages/CaptivePortalLogin/res/values-is/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Nota þetta net óbreytt"
- "Ekki nota þetta net"
- "Skrá inn á net"
- "Skrá inn á %1$s"
- "Öryggisvandamál eru á netinu sem þú ert að reyna að tengjast."
- "Til dæmis getur verið að innskráningarsíðan tilheyri ekki fyrirtækinu sem birtist."
- "Halda samt áfram í vafra"
- "Þetta vottorð er ekki frá traustum aðila."
- "Nafnið á síðunni passar ekki við nafnið á vottorðinu."
- "Þetta vottorð er útrunnið."
- "Þetta vottorð hefur ekki enn tekið gildi."
- "Þetta vottorð er með ógilda dagsetningu."
- "Þetta vottorð er ógilt."
- "Óþekkt vottorðsvilla."
- "Öryggisviðvörun"
- "Skoða vottorð"
- "Í lagi"
- "Netfang:"
- "Upplýsingar um síðu"
-
diff --git a/packages/CaptivePortalLogin/res/values-it/strings.xml b/packages/CaptivePortalLogin/res/values-it/strings.xml
deleted file mode 100644
index 3896cb4f34635..0000000000000
--- a/packages/CaptivePortalLogin/res/values-it/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Utilizza questa rete così com\'è"
- "Non utilizzare questa rete"
- "Accedi alla rete"
- "Accedi a %1$s"
- "La rete a cui stai tentando di accedere presenta problemi di sicurezza."
- "Ad esempio, la pagina di accesso potrebbe non appartenere all\'organizzazione indicata."
- "Continua comunque dal browser"
- "Questo certificato non proviene da un\'autorità attendibile."
- "Il nome del sito non corrisponde al nome nel certificato."
- "Il certificato è scaduto."
- "Questo certificato non è ancora valido."
- "Questo certificato presenta una data non valida."
- "Questo certificato non è valido."
- "Errore certificato sconosciuto."
- "Avviso di sicurezza"
- "Visualizza certificato"
- "OK"
- "Indirizzo:"
- "Informazioni sulla pagina"
-
diff --git a/packages/CaptivePortalLogin/res/values-iw/strings.xml b/packages/CaptivePortalLogin/res/values-iw/strings.xml
deleted file mode 100644
index 4ce98eea14939..0000000000000
--- a/packages/CaptivePortalLogin/res/values-iw/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "השתמש ברשת זו כפי שהיא"
- "אל תשתמש ברשת זו"
- "היכנס לרשת"
- "כניסה אל %1$s"
- "יש בעיות אבטחה ברשת שאליה אתה מנסה להתחבר."
- "לדוגמה, ייתכן שדף ההתחברות אינו שייך לארגון המוצג."
- "המשך בכל זאת באמצעות דפדפן"
- "אישור זה אינו מגיע מרשות אמינה."
- "שם האתר לא תואם לשם באישור."
- "פג תוקפו של אישור זה."
- "אישור זה אינו חוקי עדיין."
- "לאישור זה יש תאריך בלתי חוקי."
- "אישור זה אינו חוקי."
- "שגיאת אישור לא ידועה."
- "אזהרת אבטחה"
- "הצגת אישור"
- "אישור"
- "כתובת:"
- "פרטי דף"
-
diff --git a/packages/CaptivePortalLogin/res/values-ja/strings.xml b/packages/CaptivePortalLogin/res/values-ja/strings.xml
deleted file mode 100644
index bab90262eba5b..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ja/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "このネットワークをそのまま使用する"
- "このネットワークを使用しない"
- "ネットワークにログイン"
- "%1$s にログイン"
- "接続しようとしているネットワークにセキュリティの問題があります。"
- "たとえば、ログインページが表示されている組織に属していない可能性があります。"
- "ブラウザから続行"
- "この証明書は信頼できる認証機関のものではありません。"
- "サイト名と証明書上の名前が一致しません。"
- "この証明書は有効期限切れです。"
- "この証明書はまだ有効ではありません。"
- "この証明書の日付は無効です。"
- "この証明書は無効です。"
- "不明な証明書エラーです。"
- "セキュリティに関する警告"
- "証明書を表示"
- "OK"
- "メールアドレス:"
- "ページ情報"
-
diff --git a/packages/CaptivePortalLogin/res/values-ka/strings.xml b/packages/CaptivePortalLogin/res/values-ka/strings.xml
deleted file mode 100644
index ec820e58dcb9e..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ka/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "ამ ქსელის გამოყენება, როგორც არის"
- "ეს ქსელი არ გამოიყენო"
- "ქსელში შესვლა"
- "%1$s-ში შესვლა"
- "ქსელს, რომელზედაც მიერთებას ცდილობთ, უსაფრთხოების პრობლემები აქვს."
- "მაგალითად, სისტემაში შესვლის გვერდი შეიძლება არ ეკუთვნოდეს ნაჩვენებ ორგანიზაციას."
- "ბრაუზერში გაგრძელება"
- "ეს სერტიფიკატი არ არის გაცემული ნდობით აღჭურვილი ორგანოს მიერ."
- "საიტის სახელი არ ემთხვევა სერტიფიკატში მითითებულს."
- "ეს სერტიფიკატი ვადაგასულია."
- "ეს სერტიფიკატი ჯერ არ არის ძალაში."
- "ამ სერტიფიკატში მითითებულია არასწორი თარიღი."
- "ეს სერტიფიკატი არასწორია."
- "წარმოიშვა სერტიფიკატთან დაკავშირებული უცნობი შეცდომა."
- "გაფრთხილება უსაფრთხოების შესახებ"
- "სერტიფიკატის ნახვა"
- "კარგი"
- "მისამართი:"
- "გვერდის ინფორმაცია"
-
diff --git a/packages/CaptivePortalLogin/res/values-kk/strings.xml b/packages/CaptivePortalLogin/res/values-kk/strings.xml
deleted file mode 100644
index a23eafdf15482..0000000000000
--- a/packages/CaptivePortalLogin/res/values-kk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Осы желіні бар күйінде пайдалану"
- "Осы желіні пайдаланбау"
- "Желіге кіру"
- "%1$s жүйесіне кіру"
- "Қосылайын деп жатқан желіңіз қауіпсіз болуы мүмкін."
- "Мысалы, кіру беті көрсетілген ұйымға тиесілі болмауы мүмкін."
- "Бәрібір браузер арқылы жалғастыру"
- "Сертификатты сенімді орган бермеген."
- "Сайттың атауы мен сертификатта көрсетілген атау сәйкес келмейді."
- "Сертификаттың жарамдылық мерзімі аяқталған."
- "Сертификат әлі жарамсыз."
- "Сертификат күні жарамсыз."
- "Сертификат жарамсыз."
- "Сертификатқа қатысты белгісіз қате."
- "Қауіпсіздік туралы ескерту"
- "Сертификатты көру"
- "Жарайды"
- "Мекенжай:"
- "Бет туралы ақпарат"
-
diff --git a/packages/CaptivePortalLogin/res/values-km/strings.xml b/packages/CaptivePortalLogin/res/values-km/strings.xml
deleted file mode 100644
index b4274e9dbc7a4..0000000000000
--- a/packages/CaptivePortalLogin/res/values-km/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "ប្រើបណ្ដាញនេះជា"
- "កុំប្រើបណ្ដាញនេះ"
- "ចូលទៅបណ្ដាញ"
- "ចូលទៅ %1$s"
- "បណ្តាញដែលអ្នកកំពុងព្យាយាមចូលមានបញ្ហាសុវត្ថិភាព។"
- "ឧបករណ៍៖ ទំព័រចូលនេះអាចនឹងមិនមែនជាកម្មសិទ្ធិរបស់ស្ថាប័នដែលបានបង្ហាញនេះទេ។"
- "យ៉ាងណាក៏ដោយនៅតែបន្តតាមរយៈកម្មវិធីរុករក"
- "វិញ្ញាបនបត្រនេះមិនបានចេញដោយអាជ្ញាធរដែលគួរឱ្យទុកចិត្តទេ។"
- "ឈ្មោះគេហទំព័រនេះមិនត្រូវនឹងឈ្មោះនៅលើវិញ្ញាបនបត្រទេ។"
- "វិញ្ញាបនបត្រនេះផុតកំណត់ហើយ។"
- "វិញ្ញាបនបត្រនេះមិនទាន់មានសុពលភាពនៅឡើយទេ។"
- "វិញ្ញាបនបត្រនេះមានកាលបរិច្ឆេទដែលគ្មានសុពលភាព។"
- "វិញ្ញាបនបត្រនេះគ្មានសុពលភាពទេ។"
- "មានបញ្ហាដោយសារមិនស្គាល់វិញ្ញាបនបត្រ។"
- "ការព្រមានផ្នែកសុវត្ថិភាព"
- "មើលវិញ្ញាបនបត្រ"
- "យល់ព្រម"
- "អាសយដ្ឋាន៖"
- "ព័ត៌មានទំព័រ"
-
diff --git a/packages/CaptivePortalLogin/res/values-kn/strings.xml b/packages/CaptivePortalLogin/res/values-kn/strings.xml
deleted file mode 100644
index 731bbcc9d94a8..0000000000000
--- a/packages/CaptivePortalLogin/res/values-kn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "ಈ ನೆಟ್ವರ್ಕ್ ಅನ್ನು ಹೀಗೆ ಬಳಸಿ"
- "ಈ ನೆಟ್ವರ್ಕ್ ಬಳಸಬೇಡಿ"
- "ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"
- "%1$s ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"
- "ನೀವು ಸೇರಬೇಕೆಂದಿರುವ ನೆಟ್ವರ್ಕ್ ಭದ್ರತೆ ಸಮಸ್ಯೆಗಳನ್ನು ಹೊಂದಿದೆ."
- "ಉದಾಹರಣೆಗೆ, ಲಾಗಿನ್ ಪುಟವು ತೋರಿಸಲಾಗಿರುವ ಸಂಸ್ಥೆಗೆ ಸಂಬಂಧಿಸಿರುವುದಿಲ್ಲ."
- "ಹೇಗಾದರೂ ಬ್ರೌಸರ್ ಮೂಲಕ ಮುಂದುವರಿಸಿ"
- "ಈ ಪ್ರಮಾಣಪತ್ರವು ವಿಶ್ವಾಸಾರ್ಹ ಪ್ರಾಧಿಕಾರದಿಂದ ಬಂದಿಲ್ಲ."
- "ಸೈಟ್ನ ಹೆಸರು ಪ್ರಮಾಣಪತ್ರದಲ್ಲಿನ ಹೆಸರಿನ ಜೊತೆಗೆ ಹೊಂದಾಣಿಕೆಯಾಗುತ್ತಿಲ್ಲ."
- "ಈ ಪ್ರಮಾಣಪತ್ರದ ಅವಧಿ ಮೀರಿದೆ."
- "ಈ ಪ್ರಮಾಣಪತ್ರವು ಇನ್ನೂ ಮಾನ್ಯವಾಗಿಲ್ಲ."
- "ಈ ಪ್ರಮಾಣಪತ್ರವು ಅಮಾನ್ಯವಾದ ದಿನಾಂಕವನ್ನು ಹೊಂದಿದೆ."
- "ಈ ಪ್ರಮಾಣಪತ್ರವು ಅಮಾನ್ಯವಾಗಿದೆ."
- "ಪ್ರಮಾಣಪತ್ರದ ಅಜ್ಞಾತ ದೋಷ."
- "ಭದ್ರತಾ ಎಚ್ಚರಿಕೆ"
- "ಪ್ರಮಾಣಪತ್ರವನ್ನು ವೀಕ್ಷಿಸಿ"
- "ಸರಿ"
- "ವಿಳಾಸ:"
- "ಪುಟದ ಮಾಹಿತಿ"
-
diff --git a/packages/CaptivePortalLogin/res/values-ko/strings.xml b/packages/CaptivePortalLogin/res/values-ko/strings.xml
deleted file mode 100644
index 8c2d9d2257cd1..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ko/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "현재 상태로 이 네트워크 사용"
- "이 네트워크 사용 안함"
- "네트워크에 로그인"
- "%1$s에 로그인"
- "가입하려는 네트워크에 보안 문제가 있습니다."
- "예를 들어 로그인 페이지가 표시된 조직에 속하지 않을 수 있습니다."
- "브라우저를 통해 계속하기"
- "신뢰할 수 있는 인증 기관에서 발급한 인증서가 아닙니다."
- "사이트 이름이 인증서상의 이름과 일치하지 않습니다."
- "인증서가 만료되었습니다."
- "인증서가 아직 유효하지 않습니다."
- "인증서의 날짜가 잘못되었습니다."
- "인증서가 잘못되었습니다."
- "알 수 없는 인증서 오류입니다."
- "보안 경고"
- "인증서 보기"
- "확인"
- "주소:"
- "페이지 정보"
-
diff --git a/packages/CaptivePortalLogin/res/values-ky/strings.xml b/packages/CaptivePortalLogin/res/values-ky/strings.xml
deleted file mode 100644
index 6d663cb2f045a..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ky/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Бул тармак кандай болсо, ошондой колдонулсун"
- "Бул тармак колдонулбасын"
- "Тармакка кирүү"
- "%1$s каттоо эсебине кириңиз"
- "Кошулайын деген тармагыңызда коопсуздук көйгөйлөрү бар."
- "Мисалы, каттоо эсебине кирүү баракчасы көрсөтүлгөн уюмга таандык эмес болушу мүмкүн."
- "Баары бир серепчи аркылуу улантуу"
- "Бул тастыктама ишеничтүү мекеме аркылуу берилген эмес."
- "Сайттын аталышы тастыктаманын аталышына дал келбейт."
- "Бул тастыктаманын мөөнөтү өтүп кеткен."
- "Бул тастыктама азырынча жараксыз."
- "Бул тастыктаманын күнү жараксыз."
- "Бул тастыктама жараксыз."
- "Тастыктамага байланыштуу белгисиз ката."
- "Коопсуздук эскертүүсү"
- "Тастыктаманы көрүү"
- "ЖАРАЙТ"
- "Дарек:"
- "Барак жөнүндө маалымат"
-
diff --git a/packages/CaptivePortalLogin/res/values-lo/strings.xml b/packages/CaptivePortalLogin/res/values-lo/strings.xml
deleted file mode 100644
index 673bf2c061f94..0000000000000
--- a/packages/CaptivePortalLogin/res/values-lo/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "ໃຊ້ເຄືອຂ່າຍນີ້ຕາມທີ່ມັນເປັນ"
- "ບໍ່ໃຊ້ເຄືອຂ່າຍນີ້"
- "ລົງຊື່ເຂົ້າເຄືອຂ່າຍ"
- "ເຂົ້າສູ່ລະບົບ %1$s"
- "ເຄືອຂ່າຍທີ່ທ່ານກຳລັງເຂົ້າຮ່ວມມີບັນຫາຄວາມປອດໄພ."
- "ຕົວຢ່າງ, ໜ້າລົງຊື່ເຂົ້າໃຊ້ອາດຈະບໍ່ເປັນຂອງອົງການທີ່ສະແດງຂຶ້ນ."
- "ແນວໃດກໍ່ສືບຕໍ່ຜ່ານບຣາວເຊີ"
- "ໃບຮັບຮອງນີ້ບໍ່ໄດ້ມາຈາກໜ່ວຍງານທີ່ເຊື່ອຖືໄດ້."
- "ຊື່ຂອງເວັບໄຊບໍ່ກົງກັບຊື່ຢູ່ໃບຮັບຮອງ."
- "ໃບຮັບຮອງນີ້ຫມົດອາຍຸແລ້ວ."
- "ໃບຮັບຮອງນີ້ບໍ່ຖືກຕ້ອງເທື່ອ."
- "ໃບຮັບຮອງນີ້ມີວັນທີບໍ່ຖືກຕ້ອງ."
- "ໃບຮັບຮອງນີ້ບໍ່ຖືກຕ້ອງ."
- "ໃບຮັບຮອງມີຄວາມຜິດພາດທີ່ບໍ່ຮູ້ຈັກ."
- "ຄຳເຕືອນຄວາມປອດໄພ"
- "ເບິ່ງໃບຮັບຮອງ"
- "ຕົກລົງ"
- "ທີ່ຢູ່:"
- "ຂໍ້ມູນໜ້າ"
-
diff --git a/packages/CaptivePortalLogin/res/values-lt/strings.xml b/packages/CaptivePortalLogin/res/values-lt/strings.xml
deleted file mode 100644
index eb4c46ca5769c..0000000000000
--- a/packages/CaptivePortalLogin/res/values-lt/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Naudoti šį tinklą tokį, koks yra"
- "Nenaudoti šio tinklo"
- "Prisijungti prie tinklo"
- "Prisijungimas prie „%1$s“"
- "Kilo tinklo, prie kurio bandote prisijungti, problemų."
- "Pavyzdžiui, prisijungimo puslapis gali nepriklausyti rodomai organizacijai."
- "Vis tiek tęsti naudojant naršyklę"
- "Šį sertifikatą išdavė nepatikima įstaiga."
- "Svetainės pavadinimas neatitinka sertifikate nurodyto pavadinimo."
- "Šio sertifikato galiojimo laikas baigėsi."
- "Šis sertifikatas dar negalioja."
- "Šio sertifikato data netinkama."
- "Šis sertifikatas netinkamas."
- "Nežinoma sertifikato klaida."
- "Saugos įspėjimas"
- "Žiūrėti sertifikatą"
- "Gerai"
- "Adresas"
- "Puslapio informacija"
-
diff --git a/packages/CaptivePortalLogin/res/values-lv/strings.xml b/packages/CaptivePortalLogin/res/values-lv/strings.xml
deleted file mode 100644
index 5554343b739cb..0000000000000
--- a/packages/CaptivePortalLogin/res/values-lv/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Izmantot tīklu ar pašreizējiem iestatījumiem"
- "Neizmantot šo tīklu"
- "Pierakstīties tīklā"
- "Pierakstieties produktā %1$s"
- "Tīklam, kuram mēģināt pievienoties, ir drošības problēmas."
- "Piemēram, pieteikšanās lapa, iespējams, nepieder norādītajai organizācijai."
- "Tik un tā turpināt, izmantojot pārlūkprogrammu"
- "Šo sertifikātu nav izsniegusi uzticama iestāde."
- "Vietnes nosaukums neatbilst nosaukumam sertifikātā."
- "Šī sertifikāta derīguma termiņš ir beidzies."
- "Šis sertifikāts vēl nav derīgs."
- "Šī sertifikāta datums nav derīgs."
- "Šis sertifikāts nav derīgs."
- "Nezināma sertifikāta kļūda."
- "Drošības brīdinājums"
- "Skatīt sertifikātu"
- "Labi"
- "Adrese:"
- "Lapas informācija"
-
diff --git a/packages/CaptivePortalLogin/res/values-mk/strings.xml b/packages/CaptivePortalLogin/res/values-mk/strings.xml
deleted file mode 100644
index 9a57fca47dabe..0000000000000
--- a/packages/CaptivePortalLogin/res/values-mk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Користи ја мрежата во оваа состојба"
- "Не ја користи мрежата"
- "Најавете се на мрежа"
- "Најавете се на %1$s"
- "Мрежата на која се обидувате да се придружите има проблеми со безбедноста."
- "На пример, страницата за најавување може да не припаѓа на организацијата што е прикажана."
- "Сепак продолжи преку прелистувач"
- "Сертификатов не е од доверлив орган."
- "Името на сајтот не се совпаѓа со името на сертификатот."
- "Сертификатов е истечен."
- "Сертификатов сѐ уште не е важечки."
- "Сертификатов има неважечки датум."
- "Сертификатов е неважечки."
- "Непозната грешка на сертификатот."
- "Предупредување за безбедност"
- "Прикажи го сертификатот"
- "Во ред"
- "Адреса:"
- "Информации за страницата"
-
diff --git a/packages/CaptivePortalLogin/res/values-ml/strings.xml b/packages/CaptivePortalLogin/res/values-ml/strings.xml
deleted file mode 100644
index 8fd74d1c28504..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ml/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "ഈ നെറ്റ്വർക്ക് മാറ്റമൊന്നും വരുത്താതെ ഉപയോഗിക്കുക"
- "ഈ നെറ്റ്വർക്ക് ഉപയോഗിക്കരുത്"
- "നെറ്റ്വർക്കിൽ സൈൻ ഇൻ ചെയ്യുക"
- "%1$s എന്നതിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"
- "നിങ്ങൾ ചേരാൻ ശ്രമിക്കുന്ന നെറ്റ്വർക്കിൽ സുരക്ഷാ പ്രശ്നങ്ങളുണ്ടായിരിക്കാം."
- "ഉദാഹരണത്തിന്, കാണിച്ചിരിക്കുന്ന ഓർഗനൈസേഷന്റേതായിരിക്കില്ല ലോഗിൻ പേജ്."
- "എന്തായാലും ബ്രൗസർ വഴി തുടരുക"
- "ഈ സർട്ടിഫിക്കറ്റ്, വിശ്വസനീയ അതോറിറ്റിയിൽ നിന്നുള്ളതല്ല."
- "സൈറ്റിന്റെ പേര്, സർട്ടിഫിക്കറ്റിലുള്ള പേരുമായി പൊരുത്തപ്പെടുന്നില്ല."
- "ഈ സർട്ടിഫിക്കറ്റ് കാലഹരണപ്പെട്ടു."
- "ഈ സർട്ടിഫിക്കറ്റിന് ഇപ്പോഴും സാധുതയില്ല."
- "ഈ സർട്ടിഫിക്കറ്റിൽ അസാധുവായ തീയതിയാണുള്ളത്."
- "ഈ സർട്ടിഫിക്കറ്റ് അസാധുവാണ്."
- "അജ്ഞാത സർട്ടിഫിക്കറ്റ് പിശക്."
- "സുരക്ഷാ മുന്നറിയിപ്പ്"
- "സർട്ടിഫിക്കറ്റ് കാണുക"
- "ശരി"
- "വിലാസം:"
- "പേജ് വിവരം"
-
diff --git a/packages/CaptivePortalLogin/res/values-mn/strings.xml b/packages/CaptivePortalLogin/res/values-mn/strings.xml
deleted file mode 100644
index f11f1d6d1a5f0..0000000000000
--- a/packages/CaptivePortalLogin/res/values-mn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Энэ сүлжээг ашиглана уу"
- "Энэ сүлжээг бүү ашиглана уу"
- "Сүлжээнд нэвтэрнэ үү"
- "%1$s-д нэвтрэх"
- "Таны нэгдэх гэж буй сүлжээ аюулгүй байдлын асуудалтай байна."
- "Жишээлбэл нэвтрэх хуудас нь харагдах байгууллагынх биш байж болзошгүй."
- "Ямартаа ч хөтчөөр үргэлжлүүлэх"
- "Энэ сертификатыг итгэмжлэгдсэн байгууллагаас олгоогүй байна."
- "Сайтын нэр сертификат дээрх нэртэй таарахгүй байна."
- "Энэ сертификатын хугацаа дууссан байна."
- "Энэ сертификат одоогоор хүчингүй байна."
- "Энэ сертификатын огноо хүчингүй байна."
- "Энэ сертификат хүчингүй байна."
- "Сертификатын үл мэдэгдэх алдаа гарлаа."
- "Аюулгүй байдлын анхааруулга"
- "Сертификатыг харах"
- "ЗА"
- "Хаяг:"
- "Хуудасны мэдээлэл"
-
diff --git a/packages/CaptivePortalLogin/res/values-mr/strings.xml b/packages/CaptivePortalLogin/res/values-mr/strings.xml
deleted file mode 100644
index 0adc92e601cea..0000000000000
--- a/packages/CaptivePortalLogin/res/values-mr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "हे नेटवर्क जसेच्या तसे वापरा"
- "हे नेटवर्क वापरू नका"
- "नेटवर्क मध्ये साइन इन करा"
- "%1$sमध्ये साइन इन करा"
- "ज्या नेटवर्कमध्ये तुम्ही सामील होण्याचा प्रयत्न करीत आहात त्यात सुरक्षितता समस्या आहेत."
- "उदाहरणार्थ, लॉगिन पृष्ठ कदाचित दर्शविलेल्या संस्थेच्या मालकीचे नसावे."
- "ब्राउझरद्वारे तरीही सुरु ठेवा"
- "हे प्रमाणपत्र विश्वसनीय संस्थेने दिलेले नाही"
- "साइटचे नाव प्रमाणपत्रावरील नावाशी जुळत नाही."
- "हे प्रमाणपत्र एक्स्पायर झाले आहे."
- "हे प्रमाणपत्र अजून योग्य नाही."
- "या प्रमाणपत्राची तारीख चुकीची आहे."
- "हे प्रमाणपत्र चुकीचे आहे."
- "अज्ञात प्रमाणपत्र एरर."
- "सुरक्षितता चेतावणी"
- "प्रमाणपत्र पाहा"
- "ठीक आहे"
- "पत्ता:"
- "पेज माहिती"
-
diff --git a/packages/CaptivePortalLogin/res/values-ms/strings.xml b/packages/CaptivePortalLogin/res/values-ms/strings.xml
deleted file mode 100644
index cc4957eca95ac..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ms/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Gunakan rangkaian ini"
- "Jangan gunakan rangkaian ini"
- "Log masuk ke rangkaian"
- "Log masuk ke %1$s"
- "Rangkaian yang anda cuba sertai mempunyai isu keselamatan."
- "Contohnya, halaman log masuk mungkin bukan milik organisasi yang ditunjukkan."
- "Teruskan juga melalui penyemak imbas"
- "Sijil ini bukan daripada pihak berkuasa yang dipercayai."
- "Nama tapak tidak sepadan dengan nama pada sijil."
- "Sijil ini telah tamat tempoh."
- "Sijil ini belum sah."
- "Sijil ini mempunyai tarikh yang tidak sah."
- "Sijil ini tidak sah."
- "Ralat sijil tidak diketahui."
- "Amaran keselamatan"
- "Lihat sijil"
- "OK"
- "Alamat:"
- "Maklumat halaman"
-
diff --git a/packages/CaptivePortalLogin/res/values-my/strings.xml b/packages/CaptivePortalLogin/res/values-my/strings.xml
deleted file mode 100644
index 04a07a457b575..0000000000000
--- a/packages/CaptivePortalLogin/res/values-my/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "ဒီကွန်ရက်ကို လက်ရှိအတိုင်း သုံးရန်"
- "ဒီကွန်ရက်ကို မသုံးပါနှင့်"
- "ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"
- "%1$s သို့ လက်မှတ်ထိုးဝင်ပါ"
- "သင်ချိတ်ဆက်ရန် ကြိုးစားနေသည့် ကွန်ရက်သည် လုံခြုံရေးပြဿနာ ရှိနေသည်။"
- "ဥပမာ၊ ဝင်ရောက်ရန် စာမျက်နှာသည် ပြသထားသည့် အဖွဲ့အစည်းနှင့် သက်ဆိုင်မှု မရှိနိုင်ပါ။"
- "ဘရောက်ဇာမှတစ်ဆင့် ဆက်လုပ်ရန်"
- "ဤအသိအမှတ်ပြုလက်မှတ်သည် ယုံကြည်ရသော စစ်ဆေးရေးအဖွဲ့ထံမှ မဟုတ်ပါ။"
- "ဝဘ်ဆိုက်အမည်သည် အသိအမှတ်ပြုလက်မှတ်ရှိ အမည်နှင့် မကိုက်ညီပါ။"
- "ဤအသိအမှတ်ပြုလက်မှတ် သက်တမ်းကုန်နေပါပြီ။"
- "ဤအသိအမှတ်ပြုလက်မှတ်သည် မမှန်ကန်သေးပါ။"
- "ဤအသိအမှတ်ပြုလက်မှတ်ရှိ ရက်စွဲ မမှန်ကန်ပါ။"
- "ဤအသိအမှတ်ပြုလက်မှတ် မမှန်ကန်ပါ။"
- "အသိအမှတ်ပြုလက်မှတ်ဆိုင်ရာ အမျိုးအမည်မသိ အမှား။"
- "လုံခြုံရေး သတိပေးချက်"
- "အသိအမှတ်ပြုလက်မှတ်ကို ကြည့်ရန်"
- "OK"
- "လိပ်စာ-"
- "စာမျက်နှာ အချက်အလက်"
-
diff --git a/packages/CaptivePortalLogin/res/values-nb/strings.xml b/packages/CaptivePortalLogin/res/values-nb/strings.xml
deleted file mode 100644
index 29ab4389dd0d6..0000000000000
--- a/packages/CaptivePortalLogin/res/values-nb/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Bruk dette nettverket som det er"
- "Ikke bruk dette nettverket"
- "Logg på nettverk"
- "Logg på %1$s"
- "Nettverket du prøver å logge på, har sikkerhetsproblemer."
- "Det er for eksempel mulig at påloggingssiden kanskje ikke tilhører organisasjonen som vises."
- "Fortsett likevel via nettleseren"
- "Sertifikatet er ikke fra en pålitelig myndighet."
- "Navnet på nettstedet samsvarer ikke med navnet på sertifikatet."
- "Sertifikatet er utløpt."
- "Sertifikatet er ikke gyldig ennå."
- "Dette sertifikatet har en ugyldig dato."
- "Dette sertifikatet er ugyldig."
- "Ukjent sertifikatfeil."
- "Sikkerhetsadvarsel"
- "Vis sertifikatet"
- "OK"
- "Adresse:"
- "Sideinformasjon"
-
diff --git a/packages/CaptivePortalLogin/res/values-ne/strings.xml b/packages/CaptivePortalLogin/res/values-ne/strings.xml
deleted file mode 100644
index 07d7231286419..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ne/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "यो सञ्जाल जस्तो छ प्रयोग गर्नुहोस्"
- "यो सञ्जाल प्रयोग नगर्नुहोस्"
- "सञ्जालमा साइन इन गर्नुहोस्"
- "%1$s मा साइन इन गर्नुहोस्"
- "तपाईँले सामेल हुन प्रयास गरिरहनु भएको नेटवर्कमा सुरक्षा मुद्दाहरू छन्।"
- "उदाहरणका लागि, लग इन पृष्ठ देखाइएको संस्थाको नहुन सक्छ।"
- "जे भए पनि ब्राउजर मार्फत जारी राख्नुहोस्"
- "यो विश्वसनीय प्राधिकरणबाट उपलब्ध गराइएको प्रमाणपत्र होइन।"
- "प्रमाणपत्रमा भएको नाम साइटमा भएको नामसँग मेल खाँदैन।"
- "यो प्रमाणपत्रको म्याद समाप्त भयो।"
- "यो प्रमाणपत्र अझै मान्य छैन।"
- "यो प्रमाणपत्रमा कुनै अमान्य मिति छ।"
- "यो प्रमाणपत्र अमान्य छ।"
- "प्रमाणपत्रसम्बन्धी अज्ञात त्रुटि।"
- "सुरक्षासम्बन्धी चेतावनी"
- "प्रमाणपत्र हेर्नुहोस्"
- "ठिक छ"
- "ठेगाना:"
- "पृष्ठको जानकारी"
-
diff --git a/packages/CaptivePortalLogin/res/values-nl/strings.xml b/packages/CaptivePortalLogin/res/values-nl/strings.xml
deleted file mode 100644
index a4df7b7059853..0000000000000
--- a/packages/CaptivePortalLogin/res/values-nl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Dit netwerk in de huidige staat gebruiken"
- "Dit netwerk niet gebruiken"
- "Inloggen bij netwerk"
- "Inloggen bij %1$s"
- "Het netwerk waarmee u verbinding probeert te maken, heeft beveiligingsproblemen."
- "Zo hoort de weergegeven inlogpagina misschien niet bij de weergegeven organisatie."
- "Toch doorgaan via browser"
- "Dit is geen certificaat van een vertrouwde autoriteit."
- "De naam van deze site komt niet overeen met de naam op het certificaat."
- "Dit certificaat is verlopen."
- "Dit certificaat is nog niet geldig."
- "Dit certificaat heeft een ongeldige datum."
- "Dit certificaat is ongeldig."
- "Onbekende certificaatfout."
- "Beveiligingswaarschuwing"
- "Certificaat weergeven"
- "OK"
- "Adres:"
- "Paginagegevens"
-
diff --git a/packages/CaptivePortalLogin/res/values-or/strings.xml b/packages/CaptivePortalLogin/res/values-or/strings.xml
deleted file mode 100644
index f4c5dac914728..0000000000000
--- a/packages/CaptivePortalLogin/res/values-or/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "ଏହି ନେଟ୍ୱର୍କ ଯେପରି ଅଛି, ସେହିପରି ବ୍ୟବହାର କରନ୍ତୁ"
- "ଏହି ନେଟ୍ୱର୍କକୁ ବ୍ୟବହାର କରନ୍ତୁ ନାହିଁ"
- "ନେଟ୍ୱର୍କରେ ସାଇନ୍ ଇନ୍ କରନ୍ତୁ"
- "%1$sରେ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ"
- "ଆପଣ ଯୋଗ ଦେବାକୁ ଚେଷ୍ଟା କରୁଥିବା ନେଟ୍ୱର୍କର ସୁରକ୍ଷା ସମସ୍ୟା ଅଛି।"
- "ଉଦାହରଣସ୍ୱରୂପ, ଲଗଇନ୍ ପୃଷ୍ଠା ଦେଖାଯାଇଥିବା ସଂସ୍ଥାର ନହୋଇଥାଇପାରେ।"
- "ବ୍ରାଉଜର୍ ଜରିଆରେ ଯେମିତିବି ହେଉ ଜାରି ରଖନ୍ତୁ"
- "ଏହି ସାର୍ଟିଫିକେଟ୍ ଏକ ବିଶ୍ବସ୍ତ କର୍ତ୍ତୃପକ୍ଷଙ୍କ ଠାରୁ ନୁହେଁ।"
- "ସାଇଟ୍ର ନାମ ସାର୍ଟିଫିକେଟ୍ର ନାମ ସହ ମିଶୁ ନାହିଁ।"
- "ଏହି ସାର୍ଟିଫିକେଟ୍ ସମୟସୀମା ଶେଷ ହୋଇଛି।"
- "ଏହି ସାର୍ଟିଫିକେଟ୍ ବର୍ତ୍ତମାନ ପର୍ଯ୍ୟନ୍ତ ବୈଧ ନୁହେଁ।"
- "ଏହି ସାର୍ଟିଫିକେଟ୍ରେ ଏକ ଅବୈଧ ତାରିଖ ରହିଛି।"
- "ଏହି ସାର୍ଟିଫିକେଟ୍ ବୈଧ ନୁହେଁ।"
- "ଅଜଣା ସାର୍ଟିଫିକେଟ୍ ତ୍ରୁଟି।"
- "ସୁରକ୍ଷା ଚେତାବନୀ"
- "ସାର୍ଟିଫିକେଟ୍ ଦେଖନ୍ତୁ"
- "ଠିକ୍ ଅଛି"
- "ଠିକଣା:"
- "ପୃଷ୍ଠା ସୂଚନା"
-
diff --git a/packages/CaptivePortalLogin/res/values-pa/strings.xml b/packages/CaptivePortalLogin/res/values-pa/strings.xml
deleted file mode 100644
index b0d6187ea873c..0000000000000
--- a/packages/CaptivePortalLogin/res/values-pa/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "ਇਸ ਨੈੱਟਵਰਕ ਨੂੰ ਉਵੇਂ ਵਰਤੋ ਜਿਵੇਂ ਇਹ ਹੈ"
- "ਇਹ ਨੈੱਟਵਰਕ ਨਾ ਵਰਤੋ"
- "ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"
- "%1$s \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"
- "ਤੁਹਾਡੇ ਦੁਆਰਾ ਸ਼ਾਮਿਲ ਹੋਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੇ ਜਾ ਰਹੇ ਨੈੱਟਵਰਕ ਵਿੱਚ ਸੁਰੱਖਿਆ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ।"
- "ਉਦਾਹਰਣ ਵੱਜੋਂ, ਲੌਗ-ਇਨ ਪੰਨਾ ਦਿਖਾਈ ਗਈ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਨਹੀਂ ਹੋ ਸਕਦਾ ਹੈ।"
- "ਬ੍ਰਾਊਜ਼ਰ ਰਾਹੀਂ ਫਿਰ ਵੀ ਜਾਰੀ ਰੱਖੋ"
- "ਇਹ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਕਿਸੇ ਭਰੋਸੇਯੋਗ ਅਥਾਰਿਟੀ ਦਾ ਨਹੀਂ ਹੈ।"
- "ਸਾਈਟ ਦਾ ਨਾਮ ਪ੍ਰਮਾਣ-ਪੱਤਰ \'ਤੇ ਦਿੱਤੇ ਨਾਮ ਨਾਲ ਮੇਲ ਨਹੀਂ ਖਾਂਦਾ ਹੈ।"
- "ਇਸ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਦੀ ਮਿਆਦ ਸਮਾਪਤ ਹੋ ਗਈ ਹੈ।"
- "ਇਹ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਹਾਲੇ ਵੈਧ ਨਹੀਂ ਹੈ।"
- "ਇਸ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਦੀ ਤਾਰੀਖ ਅਵੈਧ ਹੈ।"
- "ਇਹ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਵੈਧ ਹੈ।"
- "ਅਗਿਆਤ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਗੜਬੜ।"
- "ਸੁਰੱਖਿਆ ਚਿਤਾਵਨੀ"
- "ਪ੍ਰਮਾਣ-ਪੱਤਰ ਦੇਖੋ"
- "ਠੀਕ ਹੈ"
- "ਪਤਾ:"
- "ਪੰਨੇ ਦੀ ਜਾਣਕਾਰੀ"
-
diff --git a/packages/CaptivePortalLogin/res/values-pl/strings.xml b/packages/CaptivePortalLogin/res/values-pl/strings.xml
deleted file mode 100644
index 178734d4c00a4..0000000000000
--- a/packages/CaptivePortalLogin/res/values-pl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Używaj tej sieci tak jak jest"
- "Nie używaj tej sieci"
- "Zaloguj się do sieci"
- "Zaloguj się w aplikacji %1$s"
- "W sieci, z którą próbujesz się połączyć, występują problemy z zabezpieczeniami."
- "Na przykład strona logowania może nie należeć do wyświetlanej organizacji."
- "Kontynuuj mimo to w przeglądarce"
- "Certyfikat nie pochodzi od zaufanego urzędu."
- "Nazwa witryny nie pasuje do nazwy na certyfikacie."
- "Certyfikat wygasł."
- "Certyfikat nie jest jeszcze ważny."
- "Certyfikat ma nieprawidłową datę."
- "Certyfikat jest nieprawidłowy."
- "Nieznany błąd certyfikatu."
- "Ostrzeżenie dotyczące bezpieczeństwa"
- "Wyświetl certyfikat"
- "OK"
- "Adres:"
- "Informacje o stronie"
-
diff --git a/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml b/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 3aa82c4814dee..0000000000000
--- a/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Usar esta rede como está"
- "Não usar esta rede"
- "Fazer login na rede"
- "Fazer login em %1$s"
- "A rede à qual você está tentando se conectar tem problemas de segurança."
- "Por exemplo, a página de login pode não pertencer à organização mostrada."
- "Continuar mesmo assim pelo navegador"
- "Esse certificado não é de uma autoridade confiável."
- "O nome do site não corresponde ao nome no certificado."
- "Esse certificado expirou."
- "Esse certificado ainda não é válido."
- "A data desse certificado é inválida."
- "Esse certificado é inválido."
- "Erro de certificado desconhecido."
- "Aviso de segurança"
- "Ver certificado"
- "OK"
- "Endereço:"
- "Informações da página"
-
diff --git a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml b/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 72d62a3c23509..0000000000000
--- a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Utilizar esta rede como está"
- "Não utilizar esta rede"
- "Início de sessão na rede"
- "Iniciar sessão em %1$s"
- "A rede à qual está a tentar aceder tem problemas de segurança."
- "Por exemplo, a página de início de sessão pode não pertencer à entidade apresentada."
- "Continuar mesmo assim através do navegador"
- "Este certificado não pertence a uma autoridade fidedigna."
- "O nome do site não corresponde ao nome constante no certificado."
- "Este certificado expirou."
- "Este certificado ainda não é válido."
- "Este certificado tem uma data inválida."
- "Este certificado é inválido."
- "Erro de certificado desconhecido."
- "Aviso de segurança"
- "Ver certificado"
- "OK"
- "Endereço:"
- "Informações da página"
-
diff --git a/packages/CaptivePortalLogin/res/values-pt/strings.xml b/packages/CaptivePortalLogin/res/values-pt/strings.xml
deleted file mode 100644
index 3aa82c4814dee..0000000000000
--- a/packages/CaptivePortalLogin/res/values-pt/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Usar esta rede como está"
- "Não usar esta rede"
- "Fazer login na rede"
- "Fazer login em %1$s"
- "A rede à qual você está tentando se conectar tem problemas de segurança."
- "Por exemplo, a página de login pode não pertencer à organização mostrada."
- "Continuar mesmo assim pelo navegador"
- "Esse certificado não é de uma autoridade confiável."
- "O nome do site não corresponde ao nome no certificado."
- "Esse certificado expirou."
- "Esse certificado ainda não é válido."
- "A data desse certificado é inválida."
- "Esse certificado é inválido."
- "Erro de certificado desconhecido."
- "Aviso de segurança"
- "Ver certificado"
- "OK"
- "Endereço:"
- "Informações da página"
-
diff --git a/packages/CaptivePortalLogin/res/values-ro/strings.xml b/packages/CaptivePortalLogin/res/values-ro/strings.xml
deleted file mode 100644
index e3fc191a7b5e2..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ro/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Utilizați această rețea în starea actuală"
- "Nu utilizați această rețea"
- "Conectați-vă la rețea"
- "Conectați-vă la %1$s"
- "Rețeaua la care încercați să vă conectați are probleme de securitate."
- "De exemplu, este posibil ca pagina de conectare să nu aparțină organizației afișate."
- "Continuați oricum prin browser"
- "Acest certificat nu provine de la o autoritate de încredere."
- "Numele acestui site nu se potrivește cu numele de pe certificat."
- "Acest certificat a expirat."
- "Acest certificat nu este încă valid."
- "Acest certificat are o dată nevalidă."
- "Acest certificat este nevalid."
- "Eroare de certificat necunoscută."
- "Avertisment de securitate"
- "Vizualizați certificatul"
- "OK"
- "Adresă:"
- "Informații despre pagină"
-
diff --git a/packages/CaptivePortalLogin/res/values-ru/strings.xml b/packages/CaptivePortalLogin/res/values-ru/strings.xml
deleted file mode 100644
index a976f5760da36..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ru/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Использовать эту сеть"
- "Не использовать эту сеть"
- "Регистрация в сети"
- "Войти: %1$s"
- "Сеть, к которой вы хотите подключиться, небезопасна."
- "Например, страница входа в аккаунт может быть фиктивной."
- "Игнорировать и открыть браузер"
- "Этот сертификат получен из ненадежных источников."
- "Название сайта отличается от указанного в сертификате."
- "Срок действия сертификата истек."
- "Сертификат ещё не действителен."
- "Дата сертификата недействительна."
- "Сертификат недействителен."
- "Неизвестная ошибка, связанная с сертификатом."
- "Предупреждение об опасности"
- "Показать сертификат"
- "ОК"
- "Адрес:"
- "О странице"
-
diff --git a/packages/CaptivePortalLogin/res/values-si/strings.xml b/packages/CaptivePortalLogin/res/values-si/strings.xml
deleted file mode 100644
index edfe716126760..0000000000000
--- a/packages/CaptivePortalLogin/res/values-si/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "මෙම ජාලය ලෙසම භාවිතා කරන්න"
- "මෙම ජාලය භාවිතා කරන්න එපා"
- "ජාලයට පුරනය වන්න"
- "%1$s වෙත පුරන්න"
- "ඔබ සම්බන්ධ වීමට උත්සහ කරන ජාලයේ ආරක්ෂක ගැටළු ඇත."
- "උදාහරණයක් ලෙස, පුරනය වන පිටුව පෙන්වා ඇති සංවිධානයට අයිති නැති විය හැක."
- "කෙසේ වුවත් බ්රවුසරය හරහා ඉදිරියට යන්න"
- "මෙම සහතිකය විශ්වාසී අධිකාරියකින් නොවේ."
- "අඩවියේ නම සහතිකයේ නමට නොගැළපේ."
- "මෙම සහතිකය කල් ඉකුත් වී ඇත."
- "මෙම සහතිකය තවම වලංගු නැත."
- "මෙම සහතිකයට වලංගු නොවන දිනයක් ඇත."
- "මෙම සහතිකය වලංගු නොවේ."
- "නොදන්නා සහතික දෝෂයකි."
- "ආරක්ෂක අවවාදයයි"
- "සහතිකය බලන්න"
- "හරි"
- "ලිපිනය:"
- "පිටු තොරතුරු"
-
diff --git a/packages/CaptivePortalLogin/res/values-sk/strings.xml b/packages/CaptivePortalLogin/res/values-sk/strings.xml
deleted file mode 100644
index c1100b86c606f..0000000000000
--- a/packages/CaptivePortalLogin/res/values-sk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Použiť túto sieť tak, ako je"
- "Túto sieť nepoužívať"
- "Prihlásiť sa do siete"
- "Prihláste sa do služby %1$s"
- "Sieť, ku ktorej sa pokúšate pripojiť, má problémy so zabezpečením"
- "Napríklad prihlasovacia stránka nemusí patriť uvedenej organizácii."
- "Pokračovať pomocou prehliadača"
- "Tento certifikát nepochádza od dôveryhodnej autority."
- "Názov stránky sa nezhoduje s názvom uvedeným v certifikáte."
- "Platnosť certifikátu skončila."
- "Tento certifikát zatiaľ nie je platný."
- "Tento certifikát má neplatný dátum."
- "Tento certifikát je neplatný."
- "Neznáma chyba certifikátu."
- "Bezpečnostné upozornenie"
- "Zobraziť certifikát"
- "OK"
- "Adresa:"
- "Informácie o stránke"
-
diff --git a/packages/CaptivePortalLogin/res/values-sl/strings.xml b/packages/CaptivePortalLogin/res/values-sl/strings.xml
deleted file mode 100644
index b2542406b1fd6..0000000000000
--- a/packages/CaptivePortalLogin/res/values-sl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Uporabljajte to omrežje, »kakršno je«"
- "Ne uporabljajte tega omrežja"
- "Prijavite se v omrežje"
- "Prijava v %1$s"
- "Omrežje, ki se mu poskušate pridružiti, ima varnostne težave."
- "Stran za prijavo na primer morda ne pripada prikazani organizaciji."
- "Vseeno nadaljuj v brskalniku"
- "Potrdila ni izdal zaupanja vreden overitelj."
- "Ime spletnega mesta se ne ujema z imenom na potrdilu."
- "Potrdilo je poteklo."
- "To potrdilo še ni veljavno."
- "To potrdilo ima neveljaven datum."
- "To potrdilo ni veljavno."
- "Neznana napaka potrdila."
- "Varnostno opozorilo"
- "Prikaži potrdilo"
- "V redu"
- "Naslov:"
- "Podatki o strani"
-
diff --git a/packages/CaptivePortalLogin/res/values-sq/strings.xml b/packages/CaptivePortalLogin/res/values-sq/strings.xml
deleted file mode 100644
index a178c40322af3..0000000000000
--- a/packages/CaptivePortalLogin/res/values-sq/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Përdore këtë rrjet siç është"
- "Mos e përdor këtë rrjet"
- "Identifikohu në rrjet"
- "Identifikohu në %1$s"
- "Rrjeti në të cilin po përpiqesh të bashkohesh ka probleme sigurie."
- "për shembull, faqja e identifikimit mund të mos i përkasë organizatës së shfaqur."
- "Vazhdo gjithsesi nëpërmjet shfletuesit"
- "Kjo certifikatë nuk është nga një autoritet i besuar."
- "Emri i sajtit nuk përputhet me emrin në certifikatë."
- "Kjo certifikatë ka skaduar."
- "Kjo certifikatë nuk është ende e vlefshme."
- "Kjo certifikatë ka një datë të pavlefshme."
- "Kjo certifikatë është e pavlefshme."
- "Gabim i panjohur i certifikatës."
- "Paralajmërim për sigurinë"
- "Shiko certifikatën"
- "Në rregull"
- "Adresa:"
- "Informacionet e faqes"
-
diff --git a/packages/CaptivePortalLogin/res/values-sr/strings.xml b/packages/CaptivePortalLogin/res/values-sr/strings.xml
deleted file mode 100644
index 6b08369163dc0..0000000000000
--- a/packages/CaptivePortalLogin/res/values-sr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Користи ову мрежу такву каква је"
- "Не користи ову мрежу"
- "Пријави ме на мрежу"
- "Пријавите се у: %1$s"
- "Мрежа којој покушавате да се придружите има безбедносних проблема."
- "На пример, страница за пријављивање можда не припада приказаној организацији."
- "Ипак настави преко прегледача"
- "Овај сертификат не потиче из поузданог извора."
- "Назив сајта се не подудара са називом на сертификату."
- "Овај сертификат је истекао."
- "Овај сертификат још увек није важећи."
- "Датум овог сертификата је неважећи."
- "Овај сертификат је неважећи."
- "Непозната грешка сертификата."
- "Безбедносно упозорење"
- "Прикажи сертификат"
- "Потврди"
- "Адреса:"
- "Информације о страници"
-
diff --git a/packages/CaptivePortalLogin/res/values-sv/strings.xml b/packages/CaptivePortalLogin/res/values-sv/strings.xml
deleted file mode 100644
index 7fe54329d7b36..0000000000000
--- a/packages/CaptivePortalLogin/res/values-sv/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Använd det här nätverket som det är"
- "Använd inte det här nätverket"
- "Logga in på nätverket"
- "Logga in på %1$s"
- "Nätverket du försöker ansluta till har säkerhetsproblem."
- "Det kan t.ex. hända att inloggningssidan inte tillhör den organisation som visas."
- "Fortsätt ändå via webbläsaren"
- "Certifikatet kommer inte från en betrodd utfärdare."
- "Webbplatsens namn matchar inte namnet på certifikatet."
- "Det här certifikatet har löpt ut."
- "Det här certifikatet är inte giltigt än."
- "Datumet för det här certifikatet är ogiltigt."
- "Det här certifikatet är ogiltigt."
- "Okänt certifikatfel."
- "Säkerhetsvarning"
- "Visa certifikat"
- "OK"
- "Adress:"
- "Sidinformation"
-
diff --git a/packages/CaptivePortalLogin/res/values-sw/strings.xml b/packages/CaptivePortalLogin/res/values-sw/strings.xml
deleted file mode 100644
index 297b72d35f74c..0000000000000
--- a/packages/CaptivePortalLogin/res/values-sw/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Tumia mtandao huu jinsi ulivyo"
- "Usitumie mtandao huu"
- "Ingia katika mtandao"
- "Ingia katika akaunti ya %1$s"
- "Mtandao unaojaribu kujiunga nao una matatizo ya usalama."
- "Kwa mfano, ukurasa wa kuingia katika akaunti unaweza usiwe unamilikiwa na shirika lililoonyeshwa."
- "Endelea hata hivyo kupitia kivinjari"
- "Cheti hiki hakijatoka kwa mamlaka inayoaminika."
- "Jina la tovuti halilingani na jina lililo katika cheti."
- "Muda wa kutumia cheti hiki umeisha."
- "Cheti hiki bado si halali."
- "Tarehe ya cheti hiki si sahihi."
- "Cheti hiki si sahihi."
- "Hitilafu isiyojulikana ya cheti."
- "Ilani ya usalama"
- "Angalia cheti"
- "Sawa"
- "Anwani:"
- "Maelezo ya ukurasa"
-
diff --git a/packages/CaptivePortalLogin/res/values-ta/strings.xml b/packages/CaptivePortalLogin/res/values-ta/strings.xml
deleted file mode 100644
index 4517d15ab7fee..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ta/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "இந்த நெட்வொர்க்கைப் பயன்படுத்து"
- "இந்த நெட்வொர்க்கைப் பயன்படுத்த வேண்டாம்"
- "நெட்வொர்க்கில் உள்நுழையவும்"
- "%1$s இல் உள்நுழைக"
- "நீங்கள் சேர முயற்சிக்கும் நெட்வொர்க்கில் பாதுகாப்புச் சிக்கல்கள் உள்ளன."
- "எடுத்துக்காட்டாக, உள்நுழைவுப் பக்கமானது காட்டப்படும் அமைப்பிற்குச் சொந்தமானதாக இல்லாமல் இருக்கலாம்."
- "பரவாயில்லை, உலாவி வழியாகத் தொடரவும்"
- "இந்தச் சான்றிதழ் நம்பகமான நிறுவனத்தால் அங்கீகரிக்கப்படவில்லை."
- "சான்றிதழிலுள்ள பெயருடன் வலைதளத்தின் பெயர் பொருந்தவில்லை."
- "இந்தச் சான்றிதழ் காலாவதியாகிவிட்டது."
- "இந்தச் சான்றிதழ் இன்னும் செல்லாததாக உள்ளது."
- "இந்தச் சான்றிதழில் தவறான தேதி உள்ளது."
- "இந்தச் சான்றிதழ் செல்லாதது."
- "சான்றிதழில் அறியப்படாத பிழை உள்ளது."
- "பாதுகாப்பு எச்சரிக்கை"
- "சான்றிதழைக் காட்டு"
- "சரி"
- "முகவரி:"
- "பக்கத் தகவல்"
-
diff --git a/packages/CaptivePortalLogin/res/values-te/strings.xml b/packages/CaptivePortalLogin/res/values-te/strings.xml
deleted file mode 100644
index 9bb4c11be8eb8..0000000000000
--- a/packages/CaptivePortalLogin/res/values-te/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "ఈ నెట్వర్క్ని యథావిధిగా ఉపయోగించు"
- "ఈ నెట్వర్క్ని ఉపయోగించవద్దు"
- "నెట్వర్క్కి సైన్ ఇన్ చేయండి"
- "%1$sకి సైన్ ఇన్ చేయండి"
- "మీరు చేరడానికి ప్రయత్నిస్తున్న నెట్వర్క్ భద్రతా సమస్యలను కలిగి ఉంది."
- "ఉదాహరణకు, లాగిన్ పేజీ చూపిన సంస్థకు చెందినది కాకపోవచ్చు."
- "ఏదేమైనా బ్రౌజర్ ద్వారా కొనసాగించు"
- "ఈ సర్టిఫికెట్ విశ్వసనీయ అధికార సంస్థ నుండి కాదు."
- "సైట్ యొక్క పేరు సర్టిఫికెట్లోని పేరుతో సరిపోలలేదు."
- "ఈ సర్టిఫికెట్ గడువు ముగిసింది."
- "ఈ సర్టిఫికెట్ ఇప్పటికీ చెల్లదు."
- "ఈ సర్టిఫికెట్ చెల్లని తేదీని కలిగి ఉంది."
- "ఈ సర్టిఫికెట్ చెల్లుబాటు కాదు."
- "తెలియని సర్టిఫికెట్ ఎర్రర్."
- "భద్రతా హెచ్చరిక"
- "సర్టిఫికెట్ని చూడండి"
- "సరే"
- "చిరునామా:"
- "పేజీ సమాచారం"
-
diff --git a/packages/CaptivePortalLogin/res/values-th/strings.xml b/packages/CaptivePortalLogin/res/values-th/strings.xml
deleted file mode 100644
index 739b50500d20d..0000000000000
--- a/packages/CaptivePortalLogin/res/values-th/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "ใช้เครือข่ายนี้ตามที่เป็นอยู่"
- "ไม่ใช้เครือข่ายนี้"
- "ลงชื่อเข้าใช้เครือข่าย"
- "ลงชื่อเข้าใช้ %1$s"
- "เครือข่ายที่คุณพยายามเข้าร่วมมีปัญหาด้านความปลอดภัย"
- "ตัวอย่างเช่น หน้าเข้าสู่ระบบอาจไม่ใช่ขององค์กรที่แสดงไว้"
- "ดำเนินการต่อผ่านเบราว์เซอร์"
- "ใบรับรองนี้ไม่ได้มาจากผู้ออกที่เชื่อถือได้"
- "ชื่อเว็บไซต์ไม่ตรงกับในใบรับรอง"
- "ใบรับรองนี้หมดอายุแล้ว"
- "ใบรับรองนี้ยังใช้งานไม่ได้"
- "วันที่ในใบรับรองนี้ไม่ถูกต้อง"
- "ใบรับรองนี้ไม่ถูกต้อง"
- "ข้อผิดพลาดใบรับรองที่ไม่รู้จัก"
- "การแจ้งเตือนเกี่ยวกับความปลอดภัย"
- "ดูใบรับรอง"
- "ตกลง"
- "ที่อยู่:"
- "ข้อมูลหน้าเว็บ"
-
diff --git a/packages/CaptivePortalLogin/res/values-tl/strings.xml b/packages/CaptivePortalLogin/res/values-tl/strings.xml
deleted file mode 100644
index 483a589bb9763..0000000000000
--- a/packages/CaptivePortalLogin/res/values-tl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Gamitin ang network na ito nang walang pagbabago"
- "Huwag gamitin ang network na ito"
- "Mag-sign in sa network"
- "Mag-sign in sa %1$s"
- "May mga isyu sa seguridad ang network kung saan mo sinusubukang sumali."
- "Halimbawa, maaaring hindi sa organisasyong ipinapakita ang page sa pag-log in."
- "Magpatuloy pa rin sa pamamagitan ng browser"
- "Ang certificate na ito ay mula sa hindi pinagkakatiwalaang awtoridad."
- "Ang pangalan ng site ay hindi tumutugma sa pangalan sa certificate."
- "Nag-expire na ang certificate na ito."
- "Hindi pa valid ang certificate na ito."
- "May invalid na petsa ang certificate na ito."
- "Invalid ang certificate na ito."
- "Hindi kilalang error sa certificate."
- "Babala sa seguridad"
- "Tingnan ang certificate"
- "OK"
- "Address:"
- "Impormasyon ng page"
-
diff --git a/packages/CaptivePortalLogin/res/values-tr/strings.xml b/packages/CaptivePortalLogin/res/values-tr/strings.xml
deleted file mode 100644
index f51cfbdfec04b..0000000000000
--- a/packages/CaptivePortalLogin/res/values-tr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Bu ağı olduğu gibi kullan"
- "Bu ağı kullanma"
- "Ağda oturum açın"
- "%1$s üzerinde oturum açın"
- "Katılmaya çalıştığınız ağda güvenlik sorunları var."
- "Örneğin, giriş sayfası, gösterilen kuruluşa ait olmayabilir."
- "Yine de tarayıcıyla devam et"
- "Bu sertifika güvenilir bir yetkiliden değil."
- "Sitenin adı sertifika üzerindeki adla eşleşmiyor."
- "Bu sertifikanın süresi dolmuş."
- "Bu sertifika henüz geçerli değil."
- "Bu sertifikanın tarihi geçersiz."
- "Bu sertifika geçersiz."
- "Bilinmeyen sertifika hatası."
- "Güvenlik uyarısı"
- "Sertifikayı göster"
- "Tamam"
- "Adres:"
- "Sayfa bilgileri"
-
diff --git a/packages/CaptivePortalLogin/res/values-uk/strings.xml b/packages/CaptivePortalLogin/res/values-uk/strings.xml
deleted file mode 100644
index c1a5d05a090da..0000000000000
--- a/packages/CaptivePortalLogin/res/values-uk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Використовувати цю мережу як є"
- "Не використовувати цю мережу"
- "Увійти в мережу"
- "Увійти в обліковий запис %1$s"
- "У мережі, до якої ви намагаєтеся під’єднатись, є проблеми з безпекою."
- "Наприклад, сторінка входу може не належати вказаній організації."
- "Усе одно продовжити у веб-переглядачі"
- "Сертифікат видано ненадійним центром сертифікації."
- "Назва сайту не збігається з назвою в сертифікаті."
- "Термін дії сертифіката закінчився."
- "Цей сертифікат ще не дійсний."
- "Цей сертифікат має недійсну дату."
- "Цей сертифікат недійсний."
- "Помилка невідомого сертифіката."
- "Застереження про небезпеку"
- "Переглянути сертифікат"
- "OK"
- "Адреса:"
- "Інформація про сторінку"
-
diff --git a/packages/CaptivePortalLogin/res/values-ur/strings.xml b/packages/CaptivePortalLogin/res/values-ur/strings.xml
deleted file mode 100644
index e6f85393c2db6..0000000000000
--- a/packages/CaptivePortalLogin/res/values-ur/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "جوں کا توں اس نیٹ ورک کا استعمال کریں"
- "اس نیٹ ورک کا استعمال نہ کریں"
- "نیٹ ورک میں سائن ان کریں"
- "%1$s میں سائن ان کریں"
- "جس نیٹ ورک میں آپ شامل ہونے کی کوشش کر رہے ہیں اس میں سیکیورٹی کے مسائل ہیں۔"
- "مثال کے طور پر ہو سکتا ہے کہ لاگ ان صفحہ دکھائی گئی تنظیم سے تعلق نہ رکھتا ہو۔"
- "براؤزر کے ذریعے بہرحال جاری رکھیں"
- "یہ سرٹیفکیٹ قابل اعتماد اتھارٹی سے حاصل شدہ نہیں ہے۔"
- "سائٹ کا نام سرٹیفکیٹ پر موجود نام سے مماثل نہیں ہے۔"
- "اس سرٹیفکیٹ کی میعاد ختم ہو گئی ہے۔"
- "یہ سرٹیفکیٹ ابھی تک درست نہیں ہے۔"
- "اس سرٹیفکیٹ میں ایک غلط تاریخ ہے۔"
- "یہ سرٹیفیکیٹ غلط ہے۔"
- "سرٹیفکیٹ کی نامعلوم خرابی۔"
- "سیکیورٹی وارننگ"
- "سرٹیفکیٹ دیکھیں"
- "ٹھیک ہے"
- "پتہ:"
- "صفحہ کی معلومات"
-
diff --git a/packages/CaptivePortalLogin/res/values-uz/strings.xml b/packages/CaptivePortalLogin/res/values-uz/strings.xml
deleted file mode 100644
index 4009db6a5caea..0000000000000
--- a/packages/CaptivePortalLogin/res/values-uz/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Ushbu tarmoqdan o‘z holicha foydalanilsin"
- "Ushbu tarmoqdan foydalanilmasin"
- "Tarmoqqa kirish"
- "%1$s hisobiga kirish"
- "Siz ulanmoqchi bo‘lgan tarmoqda xavfsizlik bilan bog‘liq muammolar mavjud."
- "Masalan, tizimga kirish sahifasi ko‘rsatilgan tashkilotga tegishli bo‘lmasligi mumkin."
- "E’tiborsiz qoldirilsin va brauzer ochilsin"
- "Bu sertifikat ishonchsiz manbadan olingan."
- "Sayt nomi sertifikatdagi nomga mos emas."
- "Bu sertifikat muddati tugagan."
- "Bu sertifikat hali yaroqli emas."
- "Bu sertifikatdagi sana xato."
- "Bu sertifikat yaroqsiz."
- "Sertifikatga aloqador notanish xato."
- "Xavfsizlikka oid ogohlantirish"
- "Sertifikatni ochish"
- "OK"
- "Manzil:"
- "Sahifa haqida"
-
diff --git a/packages/CaptivePortalLogin/res/values-vi/strings.xml b/packages/CaptivePortalLogin/res/values-vi/strings.xml
deleted file mode 100644
index bd4e96aac3db4..0000000000000
--- a/packages/CaptivePortalLogin/res/values-vi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "Sử dụng mạng này"
- "Không sử dụng mạng này"
- "Đăng nhập vào mạng"
- "Đăng nhập vào %1$s"
- "Mạng mà bạn đang cố gắng tham gia có vấn đề về bảo mật."
- "Ví dụ, trang đăng nhập có thể không thuộc về tổ chức được hiển thị."
- "Vẫn tiếp tục qua trình duyệt"
- "Chứng chỉ này không xuất phát từ tổ chức phát hành đáng tin cậy."
- "Tên của trang web không khớp với tên trên chứng chỉ."
- "Chứng chỉ này đã hết hạn."
- "Chứng chỉ này chưa hợp lệ."
- "Chứng chỉ này có ngày không hợp lệ."
- "Chứng chỉ này không hợp lệ."
- "Lỗi chứng chỉ không xác định."
- "Cảnh báo bảo mật"
- "Xem chứng chỉ"
- "OK"
- "Địa chỉ:"
- "Thông tin trang"
-
diff --git a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 5beaa2e306556..0000000000000
--- a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "直接使用此网络"
- "不要使用此网络"
- "登录到网络"
- "登录%1$s"
- "您尝试加入的网络存在安全问题。"
- "例如,登录页面可能并不属于页面上显示的单位。"
- "仍然通过浏览器继续操作"
- "该证书并非来自可信的授权中心。"
- "网站的名称与证书上的名称不一致。"
- "该证书已过期。"
- "该证书尚未生效。"
- "该证书的日期无效。"
- "该证书无效。"
- "未知证书错误。"
- "安全警告"
- "查看证书"
- "确定"
- "地址:"
- "页面信息"
-
diff --git a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
deleted file mode 100644
index 7987023eaf48f..0000000000000
--- a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "依照現況使用這個網絡"
- "不要使用這個網絡"
- "登入網絡"
- "登入「%1$s」"
- "您正在嘗試加入的網絡有安全性問題。"
- "例如,登入頁面並不屬於所顯示的機構。"
- "透過瀏覽器繼續"
- "此憑證並非由信任的機構發出。"
- "網站名稱與憑證上的名稱不符。"
- "此憑證已過期。"
- "此憑證尚未生效。"
- "此憑證的日期無效。"
- "此憑證無效。"
- "不明的憑證錯誤。"
- "安全性警告"
- "查看憑證"
- "確定"
- "電郵地址:"
- "頁面資料"
-
diff --git a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 9f92f393e63dd..0000000000000
--- a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "CaptivePortalLogin"
- "依現況使用這個網路"
- "不使用這個網路"
- "登入網路"
- "登入 %1$s"
- "你嘗試加入的網路有安全問題。"
- "例如,登入網頁中顯示的機構可能並非該網頁實際隸屬的機構。"
- "透過瀏覽器繼續"
- "這個憑證並非來自信任的授權單位。"
- "網站名稱與憑證上的名稱不相符。"
- "這個憑證已過期。"
- "這個憑證尚未生效。"
- "這個憑證的日期無效。"
- "這個憑證無效。"
- "不明的憑證錯誤。"
- "安全性警告"
- "查看憑證"
- "確定"
- "地址:"
- "頁面資訊"
-
diff --git a/packages/CaptivePortalLogin/res/values-zu/strings.xml b/packages/CaptivePortalLogin/res/values-zu/strings.xml
deleted file mode 100644
index ebb0f54563e5e..0000000000000
--- a/packages/CaptivePortalLogin/res/values-zu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- "I-CaptivePortalLogin"
- "Sebenzisa le nethiwekhi njengoba injalo"
- "Ungasebenzisi le nethiwekhi"
- "Ngena ngemvume kunethiwekhi"
- "Ngena ngemvume ku-%1$s"
- "Inethiwekhi ozama ukuyijoyina inezinkinga zokuvikela."
- "Isibonelo, ikhasi lokungena ngemvume kungenzeka lingelenhlangano ebonisiwe."
- "Qhubeka noma kunjalo ngesiphequluli"
- "Lesi sitifiketi asisuki kusiphathimandla esithembekile."
- "Igama lesayithi alifani negama elikusitifiketi."
- "Lesi sitifiketi siphelelwe yisikhathi"
- "Lesi sitifiketi asivumelekile okwamanje."
- "Lesi sitifiketi sinosuku olungalungile."
- "Lesi sitifiketi asilungile."
- "Iphutha lesitifiketi elingaziwa."
- "Isexwayiso sokuvikeleka"
- "Buka isitifiketi"
- "KULUNGILE"
- "Ikheli:"
- "Ulwazi lekhasi"
-
diff --git a/packages/CaptivePortalLogin/res/values/dimens.xml b/packages/CaptivePortalLogin/res/values/dimens.xml
deleted file mode 100644
index 55c1e5908c7e0..0000000000000
--- a/packages/CaptivePortalLogin/res/values/dimens.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- 16dp
- 16dp
-
-
diff --git a/packages/CaptivePortalLogin/res/values/strings.xml b/packages/CaptivePortalLogin/res/values/strings.xml
deleted file mode 100644
index e9698dbbd784f..0000000000000
--- a/packages/CaptivePortalLogin/res/values/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
- CaptivePortalLogin
- Use this network as is
- Do not use this network
- Sign in to network
- Sign in to %1$s
- The network you’re trying to join has security issues.
- For example, the login page may not belong to the organization shown.
- Continue anyway via browser
- This certificate isn\'t from a trusted authority.
- The name of the site doesn\'t match the name on the certificate.
- This certificate has expired.
- This certificate isn\'t valid yet.
- This certificate has an invalid date.
- This certificate is invalid.
- Unknown certificate error.
- Security warning
- View certificate
- OK
- Address:
- Page info
-
-
diff --git a/packages/CaptivePortalLogin/res/values/styles.xml b/packages/CaptivePortalLogin/res/values/styles.xml
deleted file mode 100644
index f6c233954b52d..0000000000000
--- a/packages/CaptivePortalLogin/res/values/styles.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
deleted file mode 100644
index 23fb7b6c8ba21..0000000000000
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ /dev/null
@@ -1,710 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.captiveportallogin;
-
-import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Application;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.net.CaptivePortal;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.Proxy;
-import android.net.Uri;
-import android.net.captiveportal.CaptivePortalProbeSpec;
-import android.net.http.SslCertificate;
-import android.net.http.SslError;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.os.Bundle;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.webkit.CookieManager;
-import android.webkit.SslErrorHandler;
-import android.webkit.WebChromeClient;
-import android.webkit.WebResourceRequest;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Objects;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public class CaptivePortalLoginActivity extends Activity {
- private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName();
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
- private static final int SOCKET_TIMEOUT_MS = 10000;
- public static final String HTTP_LOCATION_HEADER_NAME = "Location";
-
- private enum Result {
- DISMISSED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED),
- UNWANTED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED),
- WANTED_AS_IS(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS);
-
- final int metricsEvent;
- Result(int metricsEvent) { this.metricsEvent = metricsEvent; }
- };
-
- private URL mUrl;
- private CaptivePortalProbeSpec mProbeSpec;
- private String mUserAgent;
- private Network mNetwork;
- private CaptivePortal mCaptivePortal;
- private NetworkCallback mNetworkCallback;
- private ConnectivityManager mCm;
- private WifiManager mWifiManager;
- private boolean mLaunchBrowser = false;
- private MyWebViewClient mWebViewClient;
- private SwipeRefreshLayout mSwipeRefreshLayout;
- // Ensures that done() happens once exactly, handling concurrent callers with atomic operations.
- private final AtomicBoolean isDone = new AtomicBoolean(false);
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
- logMetricsEvent(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY);
-
- mCm = getSystemService(ConnectivityManager.class);
- mWifiManager = getSystemService(WifiManager.class);
- mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
- mUserAgent =
- getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT);
- mUrl = getUrl();
- if (mUrl == null) {
- // getUrl() failed to parse the url provided in the intent: bail out in a way that
- // at least provides network access.
- done(Result.WANTED_AS_IS);
- return;
- }
- if (DBG) {
- Log.d(TAG, String.format("onCreate for %s", mUrl.toString()));
- }
-
- final String spec = getIntent().getStringExtra(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC);
- try {
- mProbeSpec = CaptivePortalProbeSpec.parseSpecOrNull(spec);
- } catch (Exception e) {
- // Make extra sure that invalid configurations do not cause crashes
- mProbeSpec = null;
- }
-
- mNetworkCallback = new NetworkCallback() {
- @Override
- public void onLost(Network lostNetwork) {
- // If the network disappears while the app is up, exit.
- if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
- }
- };
- mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), mNetworkCallback);
-
- // If the network has disappeared, exit.
- final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
- if (networkCapabilities == null) {
- finishAndRemoveTask();
- return;
- }
-
- // Also initializes proxy system properties.
- mNetwork = mNetwork.getPrivateDnsBypassingCopy();
- mCm.bindProcessToNetwork(mNetwork);
-
- // Proxy system properties must be initialized before setContentView is called because
- // setContentView initializes the WebView logic which in turn reads the system properties.
- setContentView(R.layout.activity_captive_portal_login);
-
- getActionBar().setDisplayShowHomeEnabled(false);
- getActionBar().setElevation(0); // remove shadow
- getActionBar().setTitle(getHeaderTitle());
- getActionBar().setSubtitle("");
-
- final WebView webview = getWebview();
- webview.clearCache(true);
- CookieManager.getInstance().setAcceptThirdPartyCookies(webview, true);
- WebSettings webSettings = webview.getSettings();
- webSettings.setJavaScriptEnabled(true);
- webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
- webSettings.setUseWideViewPort(true);
- webSettings.setLoadWithOverviewMode(true);
- webSettings.setSupportZoom(true);
- webSettings.setBuiltInZoomControls(true);
- webSettings.setDisplayZoomControls(false);
- webSettings.setDomStorageEnabled(true);
- mWebViewClient = new MyWebViewClient();
- webview.setWebViewClient(mWebViewClient);
- webview.setWebChromeClient(new MyWebChromeClient());
- // Start initial page load so WebView finishes loading proxy settings.
- // Actual load of mUrl is initiated by MyWebViewClient.
- webview.loadData("", "text/html", null);
-
- mSwipeRefreshLayout = findViewById(R.id.swipe_refresh);
- mSwipeRefreshLayout.setOnRefreshListener(() -> {
- webview.reload();
- mSwipeRefreshLayout.setRefreshing(true);
- });
-
- }
-
- // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties.
- private void setWebViewProxy() {
- // TODO: migrate to androidx WebView proxy setting API as soon as it is finalized
- try {
- final Field loadedApkField = Application.class.getDeclaredField("mLoadedApk");
- final Class> loadedApkClass = loadedApkField.getType();
- final Object loadedApk = loadedApkField.get(getApplication());
- Field receiversField = loadedApkClass.getDeclaredField("mReceivers");
- receiversField.setAccessible(true);
- ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
- for (Object receiverMap : receivers.values()) {
- for (Object rec : ((ArrayMap) receiverMap).keySet()) {
- Class clazz = rec.getClass();
- if (clazz.getName().contains("ProxyChangeListener")) {
- Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class,
- Intent.class);
- Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
- onReceiveMethod.invoke(rec, getApplicationContext(), intent);
- Log.v(TAG, "Prompting WebView proxy reload.");
- }
- }
- }
- } catch (Exception e) {
- Log.e(TAG, "Exception while setting WebView proxy: " + e);
- }
- }
-
- private void done(Result result) {
- if (isDone.getAndSet(true)) {
- // isDone was already true: done() already called
- return;
- }
- if (DBG) {
- Log.d(TAG, String.format("Result %s for %s", result.name(), mUrl.toString()));
- }
- logMetricsEvent(result.metricsEvent);
- switch (result) {
- case DISMISSED:
- mCaptivePortal.reportCaptivePortalDismissed();
- break;
- case UNWANTED:
- mCaptivePortal.ignoreNetwork();
- break;
- case WANTED_AS_IS:
- mCaptivePortal.useNetwork();
- break;
- }
- finishAndRemoveTask();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.captive_portal_login, menu);
- return true;
- }
-
- @Override
- public void onBackPressed() {
- WebView myWebView = findViewById(R.id.webview);
- if (myWebView.canGoBack() && mWebViewClient.allowBack()) {
- myWebView.goBack();
- } else {
- super.onBackPressed();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- final Result result;
- final String action;
- final int id = item.getItemId();
- switch (id) {
- case R.id.action_use_network:
- result = Result.WANTED_AS_IS;
- action = "USE_NETWORK";
- break;
- case R.id.action_do_not_use_network:
- result = Result.UNWANTED;
- action = "DO_NOT_USE_NETWORK";
- break;
- default:
- return super.onOptionsItemSelected(item);
- }
- if (DBG) {
- Log.d(TAG, String.format("onOptionsItemSelect %s for %s", action, mUrl.toString()));
- }
- done(result);
- return true;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- final WebView webview = (WebView) findViewById(R.id.webview);
- if (webview != null) {
- webview.stopLoading();
- webview.setWebViewClient(null);
- webview.setWebChromeClient(null);
- webview.destroy();
- }
- if (mNetworkCallback != null) {
- // mNetworkCallback is not null if mUrl is not null.
- mCm.unregisterNetworkCallback(mNetworkCallback);
- }
- if (mLaunchBrowser) {
- // Give time for this network to become default. After 500ms just proceed.
- for (int i = 0; i < 5; i++) {
- // TODO: This misses when mNetwork underlies a VPN.
- if (mNetwork.equals(mCm.getActiveNetwork())) break;
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- }
- }
- final String url = mUrl.toString();
- if (DBG) {
- Log.d(TAG, "starting activity with intent ACTION_VIEW for " + url);
- }
- startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
- }
- }
-
- private URL getUrl() {
- String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
- if (url == null) {
- url = mCm.getCaptivePortalServerUrl();
- }
- return makeURL(url);
- }
-
- private static URL makeURL(String url) {
- try {
- return new URL(url);
- } catch (MalformedURLException e) {
- Log.e(TAG, "Invalid URL " + url);
- }
- return null;
- }
-
- private static String host(URL url) {
- if (url == null) {
- return null;
- }
- return url.getHost();
- }
-
- private static String sanitizeURL(URL url) {
- // In non-Debug build, only show host to avoid leaking private info.
- return isDebuggable() ? Objects.toString(url) : host(url);
- }
-
- private static boolean isDebuggable() {
- return SystemProperties.getInt("ro.debuggable", 0) == 1;
- }
-
- private void testForCaptivePortal() {
- // TODO: reuse NetworkMonitor facilities for consistent captive portal detection.
- new Thread(new Runnable() {
- public void run() {
- // Give time for captive portal to open.
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- }
- HttpURLConnection urlConnection = null;
- int httpResponseCode = 500;
- String locationHeader = null;
- try {
- urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl);
- urlConnection.setInstanceFollowRedirects(false);
- urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
- urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
- urlConnection.setUseCaches(false);
- if (mUserAgent != null) {
- urlConnection.setRequestProperty("User-Agent", mUserAgent);
- }
- // cannot read request header after connection
- String requestHeader = urlConnection.getRequestProperties().toString();
-
- urlConnection.getInputStream();
- httpResponseCode = urlConnection.getResponseCode();
- locationHeader = urlConnection.getHeaderField(HTTP_LOCATION_HEADER_NAME);
- if (DBG) {
- Log.d(TAG, "probe at " + mUrl +
- " ret=" + httpResponseCode +
- " request=" + requestHeader +
- " headers=" + urlConnection.getHeaderFields());
- }
- } catch (IOException e) {
- } finally {
- if (urlConnection != null) urlConnection.disconnect();
- }
- if (isDismissed(httpResponseCode, locationHeader, mProbeSpec)) {
- done(Result.DISMISSED);
- }
- }
- }).start();
- }
-
- private static boolean isDismissed(
- int httpResponseCode, String locationHeader, CaptivePortalProbeSpec probeSpec) {
- return (probeSpec != null)
- ? probeSpec.getResult(httpResponseCode, locationHeader).isSuccessful()
- : (httpResponseCode == 204);
- }
-
- private class MyWebViewClient extends WebViewClient {
- private static final String INTERNAL_ASSETS = "file:///android_asset/";
-
- private final String mBrowserBailOutToken = Long.toString(new Random().nextLong());
- private final String mCertificateOutToken = Long.toString(new Random().nextLong());
- // How many Android device-independent-pixels per scaled-pixel
- // dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp)
- private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
- getResources().getDisplayMetrics()) /
- TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
- getResources().getDisplayMetrics());
- private int mPagesLoaded;
- private String mMainFrameUrl;
-
- // If we haven't finished cleaning up the history, don't allow going back.
- public boolean allowBack() {
- return mPagesLoaded > 1;
- }
-
- private String mSslErrorTitle = null;
- private SslErrorHandler mSslErrorHandler = null;
- private SslError mSslError = null;
-
- @Override
- public void onPageStarted(WebView view, String urlString, Bitmap favicon) {
- if (urlString.contains(mBrowserBailOutToken)) {
- mLaunchBrowser = true;
- done(Result.WANTED_AS_IS);
- return;
- }
- // The first page load is used only to cause the WebView to
- // fetch the proxy settings. Don't update the URL bar, and
- // don't check if the captive portal is still there.
- if (mPagesLoaded == 0) {
- return;
- }
- final URL url = makeURL(urlString);
- Log.d(TAG, "onPageStarted: " + sanitizeURL(url));
- // For internally generated pages, leave URL bar listing prior URL as this is the URL
- // the page refers to.
- if (!urlString.startsWith(INTERNAL_ASSETS)) {
- String subtitle = (url != null) ? getHeaderSubtitle(url) : urlString;
- getActionBar().setSubtitle(subtitle);
- }
- getProgressBar().setVisibility(View.VISIBLE);
- testForCaptivePortal();
- }
-
- @Override
- public void onPageFinished(WebView view, String url) {
- mPagesLoaded++;
- getProgressBar().setVisibility(View.INVISIBLE);
- mSwipeRefreshLayout.setRefreshing(false);
- if (mPagesLoaded == 1) {
- // Now that WebView has loaded at least one page we know it has read in the proxy
- // settings. Now prompt the WebView read the Network-specific proxy settings.
- setWebViewProxy();
- // Load the real page.
- view.loadUrl(mUrl.toString());
- return;
- } else if (mPagesLoaded == 2) {
- // Prevent going back to empty first page.
- // Fix for missing focus, see b/62449959 for details. Remove it once we get a
- // newer version of WebView (60.x.y).
- view.requestFocus();
- view.clearHistory();
- }
- testForCaptivePortal();
- }
-
- // Convert Android scaled-pixels (sp) to HTML size.
- private String sp(int sp) {
- // Convert sp to dp's.
- float dp = sp * mDpPerSp;
- // Apply a scale factor to make things look right.
- dp *= 1.3;
- // Convert dp's to HTML size.
- // HTML px's are scaled just like dp's, so just add "px" suffix.
- return Integer.toString((int)dp) + "px";
- }
-
- // Check if webview is trying to load the main frame and record its url.
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
- if (request.isForMainFrame()) {
- mMainFrameUrl = request.getUrl().toString();
- }
- // Be careful that two shouldOverrideUrlLoading methods are overridden, but
- // shouldOverrideUrlLoading(WebView view, String url) was deprecated in API level 24.
- // TODO: delete deprecated one ??
- return shouldOverrideUrlLoading(view, mMainFrameUrl);
- }
-
- // A web page consisting of a large broken lock icon to indicate SSL failure.
-
- @Override
- public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
- final URL errorUrl = makeURL(error.getUrl());
- final URL mainFrameUrl = makeURL(mMainFrameUrl);
- Log.d(TAG, String.format("SSL error: %s, url: %s, certificate: %s",
- sslErrorName(error), sanitizeURL(errorUrl), error.getCertificate()));
- if (errorUrl == null
- // Ignore SSL errors from resources by comparing the main frame url with SSL
- // error url.
- || !errorUrl.equals(mainFrameUrl)) {
- Log.d(TAG, "onReceivedSslError: mMainFrameUrl = " + mMainFrameUrl);
- handler.cancel();
- return;
- }
- logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR);
- final String sslErrorPage = makeSslErrorPage();
- view.loadDataWithBaseURL(INTERNAL_ASSETS, sslErrorPage, "text/HTML", "UTF-8", null);
- mSslErrorTitle = view.getTitle() == null ? "" : view.getTitle();
- mSslErrorHandler = handler;
- mSslError = error;
- }
-
- private String makeSslErrorPage() {
- final String warningMsg = getString(R.string.ssl_error_warning);
- final String exampleMsg = getString(R.string.ssl_error_example);
- final String continueMsg = getString(R.string.ssl_error_continue);
- final String certificateMsg = getString(R.string.ssl_error_view_certificate);
- return String.join("\n",
- "",
- "",
- " ",
- " ",
- "",
- "",
- "
",
- "
" + warningMsg + "
",
- "
" + exampleMsg + "
",
- " " + continueMsg + " ",
- " " + certificateMsg +
- "",
- "",
- "");
- }
-
- @Override
- public boolean shouldOverrideUrlLoading (WebView view, String url) {
- if (url.startsWith("tel:")) {
- startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
- return true;
- }
- if (url.contains(mCertificateOutToken) && mSslError != null) {
- showSslAlertDialog(mSslErrorHandler, mSslError, mSslErrorTitle);
- return true;
- }
- return false;
- }
- private void showSslAlertDialog(SslErrorHandler handler, SslError error, String title) {
- final LayoutInflater factory = LayoutInflater.from(CaptivePortalLoginActivity.this);
- final View sslWarningView = factory.inflate(R.layout.ssl_warning, null);
-
- // Set Security certificate
- setViewSecurityCertificate(sslWarningView.findViewById(R.id.certificate_layout), error);
- ((TextView) sslWarningView.findViewById(R.id.ssl_error_type))
- .setText(sslErrorName(error));
- ((TextView) sslWarningView.findViewById(R.id.title)).setText(mSslErrorTitle);
- ((TextView) sslWarningView.findViewById(R.id.address)).setText(error.getUrl());
-
- AlertDialog sslAlertDialog = new AlertDialog.Builder(CaptivePortalLoginActivity.this)
- .setTitle(R.string.ssl_security_warning_title)
- .setView(sslWarningView)
- .setPositiveButton(R.string.ok, (DialogInterface dialog, int whichButton) -> {
- // handler.cancel is called via OnCancelListener.
- dialog.cancel();
- })
- .setOnCancelListener((DialogInterface dialogInterface) -> handler.cancel())
- .create();
- sslAlertDialog.show();
- }
-
- private void setViewSecurityCertificate(LinearLayout certificateLayout, SslError error) {
- ((TextView) certificateLayout.findViewById(R.id.ssl_error_msg))
- .setText(sslErrorMessage(error));
- SslCertificate cert = error.getCertificate();
- // TODO: call the method directly once inflateCertificateView is @SystemApi
- try {
- final View certificateView = (View) SslCertificate.class.getMethod(
- "inflateCertificateView", Context.class)
- .invoke(cert, CaptivePortalLoginActivity.this);
- certificateLayout.addView(certificateView);
- } catch (ReflectiveOperationException | SecurityException e) {
- Log.e(TAG, "Could not create certificate view", e);
- }
- }
- }
-
- private class MyWebChromeClient extends WebChromeClient {
- @Override
- public void onProgressChanged(WebView view, int newProgress) {
- getProgressBar().setProgress(newProgress);
- }
- }
-
- private ProgressBar getProgressBar() {
- return findViewById(R.id.progress_bar);
- }
-
- private WebView getWebview() {
- return findViewById(R.id.webview);
- }
-
- private String getHeaderTitle() {
- NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork);
- final String ssid = getSsid();
- if (TextUtils.isEmpty(ssid)
- || nc == null || !nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
- return getString(R.string.action_bar_label);
- }
- return getString(R.string.action_bar_title, ssid);
- }
-
- // TODO: remove once SSID is obtained from NetworkCapabilities
- private String getSsid() {
- if (mWifiManager == null) {
- return null;
- }
- final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
- return removeDoubleQuotes(wifiInfo.getSSID());
- }
-
- private static String removeDoubleQuotes(String string) {
- if (string == null) return null;
- final int length = string.length();
- if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
- return string.substring(1, length - 1);
- }
- return string;
- }
-
- private String getHeaderSubtitle(URL url) {
- String host = host(url);
- final String https = "https";
- if (https.equals(url.getProtocol())) {
- return https + "://" + host;
- }
- return host;
- }
-
- private void logMetricsEvent(int event) {
- mCaptivePortal.logEvent(event, getPackageName());
- }
-
- private static final SparseArray SSL_ERRORS = new SparseArray<>();
- static {
- SSL_ERRORS.put(SslError.SSL_NOTYETVALID, "SSL_NOTYETVALID");
- SSL_ERRORS.put(SslError.SSL_EXPIRED, "SSL_EXPIRED");
- SSL_ERRORS.put(SslError.SSL_IDMISMATCH, "SSL_IDMISMATCH");
- SSL_ERRORS.put(SslError.SSL_UNTRUSTED, "SSL_UNTRUSTED");
- SSL_ERRORS.put(SslError.SSL_DATE_INVALID, "SSL_DATE_INVALID");
- SSL_ERRORS.put(SslError.SSL_INVALID, "SSL_INVALID");
- }
-
- private static String sslErrorName(SslError error) {
- return SSL_ERRORS.get(error.getPrimaryError(), "UNKNOWN");
- }
-
- private static final SparseArray SSL_ERROR_MSGS = new SparseArray<>();
- static {
- SSL_ERROR_MSGS.put(SslError.SSL_NOTYETVALID, R.string.ssl_error_not_yet_valid);
- SSL_ERROR_MSGS.put(SslError.SSL_EXPIRED, R.string.ssl_error_expired);
- SSL_ERROR_MSGS.put(SslError.SSL_IDMISMATCH, R.string.ssl_error_mismatch);
- SSL_ERROR_MSGS.put(SslError.SSL_UNTRUSTED, R.string.ssl_error_untrusted);
- SSL_ERROR_MSGS.put(SslError.SSL_DATE_INVALID, R.string.ssl_error_date_invalid);
- SSL_ERROR_MSGS.put(SslError.SSL_INVALID, R.string.ssl_error_invalid);
- }
-
- private static Integer sslErrorMessage(SslError error) {
- return SSL_ERROR_MSGS.get(error.getPrimaryError(), R.string.ssl_error_unknown);
- }
-}
diff --git a/packages/ExtServices/Android.bp b/packages/ExtServices/Android.bp
deleted file mode 100644
index 77972fedfb0d0..0000000000000
--- a/packages/ExtServices/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-android_library {
- name: "ExtServices-core",
- srcs: [
- "src/**/*.java",
- ],
- resource_dirs: [
- "res",
- ],
-
- manifest: "AndroidManifest.xml",
-}
\ No newline at end of file
diff --git a/packages/ExtServices/Android.mk b/packages/ExtServices/Android.mk
deleted file mode 100644
index a06dd86fe1886..0000000000000
--- a/packages/ExtServices/Android.mk
+++ /dev/null
@@ -1,40 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := ExtServices
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.proguard
-
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_MIN_SDK_VERSION := 28
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-ifeq ($(strip $(LOCAL_PACKAGE_OVERRIDES)),)
-include $(call all-makefiles-under, $(LOCAL_PATH))
-endif
-
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
deleted file mode 100644
index 8592fc9a7e91f..0000000000000
--- a/packages/ExtServices/AndroidManifest.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/ExtServices/MODULE_LICENSE_APACHE2 b/packages/ExtServices/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29bb2d1d..0000000000000
diff --git a/packages/ExtServices/NOTICE b/packages/ExtServices/NOTICE
deleted file mode 100644
index c5b1efa7aac76..0000000000000
--- a/packages/ExtServices/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
- Copyright (c) 2005-2008, The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
-
- 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.
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
diff --git a/packages/ExtServices/OWNERS b/packages/ExtServices/OWNERS
deleted file mode 100644
index bf52f35ee6e01..0000000000000
--- a/packages/ExtServices/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-set noparent
-
-baligh@google.com
-delphij@google.com
diff --git a/packages/ExtServices/proguard.proguard b/packages/ExtServices/proguard.proguard
deleted file mode 100644
index e5dfbe1c453d8..0000000000000
--- a/packages/ExtServices/proguard.proguard
+++ /dev/null
@@ -1,7 +0,0 @@
--keepparameternames
--keepattributes Exceptions,InnerClasses,Signature,Deprecated,
- SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-
--keep public class * {
- public protected *;
-}
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
deleted file mode 100644
index 58deb11eef740..0000000000000
--- a/packages/ExtServices/res/values/strings.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
- Android Services Library
-
- Android Adaptive Notifications
-
- EDIT_DISTANCE
-
- EDIT_DISTANCE
- EXACT_MATCH
-
-
-
- Copy \u201c%1$s\u201c
-
- Code copied
-
-
diff --git a/packages/ExtServices/src/android/ext/services/Version.java b/packages/ExtServices/src/android/ext/services/Version.java
deleted file mode 100644
index 026cccd37334e..0000000000000
--- a/packages/ExtServices/src/android/ext/services/Version.java
+++ /dev/null
@@ -1,36 +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.ext.services;
-
-/**
- * Class that provides the version of the library.
- */
-public final class Version {
-
- private Version() {
- /* do nothing - hide constructor */
- }
-
- /**
- * Gets the version of the library.
- *
- * @return The version.
- */
- public static int getVersionCode() {
- return 1;
- }
-}
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
deleted file mode 100644
index e379db842b3ba..0000000000000
--- a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
+++ /dev/null
@@ -1,95 +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.ext.services.autofill;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
-import android.service.autofill.AutofillFieldClassificationService;
-import android.util.Log;
-import android.view.autofill.AutofillValue;
-
-import com.android.internal.util.ArrayUtils;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService {
-
- private static final String TAG = "AutofillFieldClassificationServiceImpl";
-
- private static final String DEFAULT_ALGORITHM = REQUIRED_ALGORITHM_EDIT_DISTANCE;
-
- @Nullable
- @Override
- /** @hide */
- public float[][] onCalculateScores(@NonNull List actualValues,
- @NonNull List userDataValues, @NonNull List categoryIds,
- @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
- @Nullable Map algorithms, @Nullable Map args) {
- if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) {
- Log.w(TAG, "calculateScores(): empty currentvalues (" + actualValues
- + ") or userValues (" + userDataValues + ")");
- return null;
- }
-
- return calculateScores(actualValues, userDataValues, categoryIds, defaultAlgorithm,
- defaultArgs, (HashMap) algorithms,
- (HashMap) args);
- }
-
- /** @hide */
- public float[][] calculateScores(@NonNull List actualValues,
- @NonNull List userDataValues, @NonNull List categoryIds,
- @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
- @Nullable HashMap algorithms,
- @Nullable HashMap args) {
- final int actualValuesSize = actualValues.size();
- final int userDataValuesSize = userDataValues.size();
- final float[][] scores = new float[actualValuesSize][userDataValuesSize];
-
- for (int j = 0; j < userDataValuesSize; j++) {
- final String categoryId = categoryIds.get(j);
- String algorithmName = defaultAlgorithm;
- Bundle arg = defaultArgs;
- if (algorithms != null && algorithms.containsKey(categoryId)) {
- algorithmName = algorithms.get(categoryId);
- }
- if (args != null && args.containsKey(categoryId)) {
- arg = args.get(categoryId);
- }
-
- if (algorithmName == null || (!algorithmName.equals(DEFAULT_ALGORITHM)
- && !algorithmName.equals(REQUIRED_ALGORITHM_EXACT_MATCH))) {
- Log.w(TAG, "algorithmName is " + algorithmName + ", defaulting to "
- + DEFAULT_ALGORITHM);
- algorithmName = DEFAULT_ALGORITHM;
- }
-
- for (int i = 0; i < actualValuesSize; i++) {
- if (algorithmName.equals(DEFAULT_ALGORITHM)) {
- scores[i][j] = EditDistanceScorer.calculateScore(actualValues.get(i),
- userDataValues.get(j));
- } else {
- scores[i][j] = ExactMatch.calculateScore(actualValues.get(i),
- userDataValues.get(j), arg);
- }
- }
- }
- return scores;
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java
deleted file mode 100644
index 196257162058b..0000000000000
--- a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java
+++ /dev/null
@@ -1,123 +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.ext.services.autofill;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.view.autofill.AutofillValue;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-final class EditDistanceScorer {
-
- private static final String TAG = "EditDistanceScorer";
-
- // TODO(b/70291841): STOPSHIP - set to false before launching
- private static final boolean DEBUG = true;
-
- /**
- * Gets the field classification score of 2 values based on the edit distance between them.
- *
- *
The score is defined as: @(max_length - edit_distance) / max_length
- */
- @VisibleForTesting
- static float calculateScore(@Nullable AutofillValue actualValue,
- @Nullable String userDataValue) {
- if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0;
-
- final String actualValueText = actualValue.getTextValue().toString();
- final int actualValueLength = actualValueText.length();
- final int userDatalength = userDataValue.length();
- if (userDatalength == 0) {
- return (actualValueLength == 0) ? 1 : 0;
- }
-
- final int distance = editDistance(actualValueText.toLowerCase(),
- userDataValue.toLowerCase());
- final int maxLength = Math.max(actualValueLength, userDatalength);
- return ((float) maxLength - distance) / maxLength;
- }
-
- /**
- * Computes the edit distance (number of insertions, deletions or substitutions to edit one
- * string into the other) between two strings. In particular, this will compute the Levenshtein
- * distance.
- *
- *
See http://en.wikipedia.org/wiki/Levenshtein_distance for details.
- *
- * @param s the first string to compare
- * @param t the second string to compare
- * @return the edit distance between the two strings
- */
- // Note: copied verbatim from com.android.tools.lint.detector.api.LintUtils.java
- public static int editDistance(@NonNull String s, @NonNull String t) {
- return editDistance(s, t, Integer.MAX_VALUE);
- }
-
- /**
- * Computes the edit distance (number of insertions, deletions or substitutions to edit one
- * string into the other) between two strings. In particular, this will compute the Levenshtein
- * distance.
- *
- *
See http://en.wikipedia.org/wiki/Levenshtein_distance for details.
- *
- * @param s the first string to compare
- * @param t the second string to compare
- * @param max the maximum edit distance that we care about; if for example the string length
- * delta is greater than this we don't bother computing the exact edit distance since the
- * caller has indicated they're not interested in the result
- * @return the edit distance between the two strings, or some other value greater than that if
- * the edit distance is at least as big as the {@code max} parameter
- */
- // Note: copied verbatim from com.android.tools.lint.detector.api.LintUtils.java
- public static int editDistance(@NonNull String s, @NonNull String t, int max) {
- if (s.equals(t)) {
- return 0;
- }
-
- if (Math.abs(s.length() - t.length()) > max) {
- // The string lengths differ more than the allowed edit distance;
- // no point in even attempting to compute the edit distance (requires
- // O(n*m) storage and O(n*m) speed, where n and m are the string lengths)
- return Integer.MAX_VALUE;
- }
-
- int m = s.length();
- int n = t.length();
- int[][] d = new int[m + 1][n + 1];
- for (int i = 0; i <= m; i++) {
- d[i][0] = i;
- }
- for (int j = 0; j <= n; j++) {
- d[0][j] = j;
- }
- for (int j = 1; j <= n; j++) {
- for (int i = 1; i <= m; i++) {
- if (s.charAt(i - 1) == t.charAt(j - 1)) {
- d[i][j] = d[i - 1][j - 1];
- } else {
- int deletion = d[i - 1][j] + 1;
- int insertion = d[i][j - 1] + 1;
- int substitution = d[i - 1][j - 1] + 1;
- d[i][j] = Math.min(deletion, Math.min(insertion, substitution));
- }
- }
- }
-
- return d[m][n];
- }
-
-}
diff --git a/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java b/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java
deleted file mode 100644
index 3e55c5c59e024..0000000000000
--- a/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java
+++ /dev/null
@@ -1,67 +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.ext.services.autofill;
-
-import android.annotation.Nullable;
-import android.os.Bundle;
-import android.view.autofill.AutofillValue;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-final class ExactMatch {
-
- /**
- * Gets the field classification score of 2 values based on whether they are an exact match
- *
- * @return {@code 1.0} if the two values are an exact match, {@code 0.0} otherwise.
- */
- @VisibleForTesting
- static float calculateScore(@Nullable AutofillValue actualValue,
- @Nullable String userDataValue, @Nullable Bundle args) {
- if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0;
-
- final String actualValueText = actualValue.getTextValue().toString();
-
- final int suffixLength;
- if (args != null) {
- suffixLength = args.getInt("suffix", -1);
-
- if (suffixLength < 0) {
- throw new IllegalArgumentException("suffix argument is invalid");
- }
-
- final String actualValueSuffix;
- if (suffixLength < actualValueText.length()) {
- actualValueSuffix = actualValueText.substring(actualValueText.length()
- - suffixLength);
- } else {
- actualValueSuffix = actualValueText;
- }
-
- final String userDataValueSuffix;
- if (suffixLength < userDataValue.length()) {
- userDataValueSuffix = userDataValue.substring(userDataValue.length()
- - suffixLength);
- } else {
- userDataValueSuffix = userDataValue;
- }
-
- return (actualValueSuffix.equalsIgnoreCase(userDataValueSuffix)) ? 1 : 0;
- } else {
- return actualValueText.equalsIgnoreCase(userDataValue) ? 1 : 0;
- }
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java b/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java
deleted file mode 100644
index 31c9224a8489b..0000000000000
--- a/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java
+++ /dev/null
@@ -1,172 +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.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.ext.services.notification.NotificationCategorizer.Category;
-import android.net.Uri;
-import android.util.ArraySet;
-import android.util.Slog;
-
-import java.util.Set;
-
-public class AgingHelper {
- private final static String TAG = "AgingHelper";
- private final boolean DEBUG = false;
-
- private static final String AGING_ACTION = AgingHelper.class.getSimpleName() + ".EVALUATE";
- private static final int REQUEST_CODE_AGING = 1;
- private static final String AGING_SCHEME = "aging";
- private static final String EXTRA_KEY = "key";
- private static final String EXTRA_CATEGORY = "category";
-
- private static final int HOUR_MS = 1000 * 60 * 60;
- private static final int TWO_HOURS_MS = 2 * HOUR_MS;
-
- private Context mContext;
- private NotificationCategorizer mNotificationCategorizer;
- private AlarmManager mAm;
- private Callback mCallback;
-
- // The set of keys we've scheduled alarms for
- private Set mAging = new ArraySet<>();
-
- public AgingHelper(Context context, NotificationCategorizer categorizer, Callback callback) {
- mNotificationCategorizer = categorizer;
- mContext = context;
- mAm = mContext.getSystemService(AlarmManager.class);
- mCallback = callback;
-
- IntentFilter filter = new IntentFilter(AGING_ACTION);
- filter.addDataScheme(AGING_SCHEME);
- mContext.registerReceiver(mBroadcastReceiver, filter);
- }
-
- // NAS lifecycle methods
-
- public void onNotificationSeen(NotificationEntry entry) {
- // user has strong opinions about this notification. we can't down rank it, so don't bother.
- if (entry.getChannel().hasUserSetImportance()) {
- return;
- }
-
- @Category int category = mNotificationCategorizer.getCategory(entry);
-
- // already very low
- if (category == NotificationCategorizer.CATEGORY_MIN) {
- return;
- }
-
- if (entry.hasSeen()) {
- if (category == NotificationCategorizer.CATEGORY_ONGOING
- || category > NotificationCategorizer.CATEGORY_REMINDER) {
- scheduleAging(entry.getSbn().getKey(), category, TWO_HOURS_MS);
- } else {
- scheduleAging(entry.getSbn().getKey(), category, HOUR_MS);
- }
-
- mAging.add(entry.getSbn().getKey());
- }
- }
-
- public void onNotificationPosted(NotificationEntry entry) {
- cancelAging(entry.getSbn().getKey());
- }
-
- public void onNotificationRemoved(String key) {
- cancelAging(key);
- }
-
- public void onDestroy() {
- mContext.unregisterReceiver(mBroadcastReceiver);
- }
-
- // Aging
-
- private void scheduleAging(String key, @Category int category, long duration) {
- if (mAging.contains(key)) {
- // already scheduled. Don't reset aging just because the user saw the noti again.
- return;
- }
- final PendingIntent pi = createPendingIntent(key, category);
- long time = System.currentTimeMillis() + duration;
- if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + key + " in ms: " + duration);
- mAm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pi);
- }
-
- private void cancelAging(String key) {
- final PendingIntent pi = createPendingIntent(key);
- mAm.cancel(pi);
- mAging.remove(key);
- }
-
- private Intent createBaseIntent(String key) {
- return new Intent(AGING_ACTION)
- .setData(new Uri.Builder().scheme(AGING_SCHEME).appendPath(key).build());
- }
-
- private Intent createAgingIntent(String key, @Category int category) {
- Intent intent = createBaseIntent(key);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(EXTRA_CATEGORY, category)
- .putExtra(EXTRA_KEY, key);
- return intent;
- }
-
- private PendingIntent createPendingIntent(String key, @Category int category) {
- return PendingIntent.getBroadcast(mContext,
- REQUEST_CODE_AGING,
- createAgingIntent(key, category),
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private PendingIntent createPendingIntent(String key) {
- return PendingIntent.getBroadcast(mContext,
- REQUEST_CODE_AGING,
- createBaseIntent(key),
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private void demote(String key, @Category int category) {
- int newImportance = IMPORTANCE_MIN;
- // TODO: Change "aged" importance based on category
- mCallback.sendAdjustment(key, newImportance);
- }
-
- protected interface Callback {
- void sendAdjustment(String key, int newImportance);
- }
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) {
- Slog.d(TAG, "Reposting notification");
- }
- if (AGING_ACTION.equals(intent.getAction())) {
- demote(intent.getStringExtra(EXTRA_KEY), intent.getIntExtra(EXTRA_CATEGORY,
- NotificationCategorizer.CATEGORY_EVERYTHING_ELSE));
- }
- }
- };
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
deleted file mode 100644
index d01878aef37d9..0000000000000
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ /dev/null
@@ -1,514 +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.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.service.notification.Adjustment.KEY_IMPORTANCE;
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.app.ActivityThread;
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.UserHandle;
-import android.os.storage.StorageManager;
-import android.service.notification.Adjustment;
-import android.service.notification.NotificationAssistantService;
-import android.service.notification.NotificationStats;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-import android.util.AtomicFile;
-import android.util.Log;
-import android.util.Slog;
-import android.util.Xml;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-
-import libcore.io.IoUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * Notification assistant that provides guidance on notification channel blocking
- */
-@SuppressLint("OverrideAbstract")
-public class Assistant extends NotificationAssistantService {
- private static final String TAG = "ExtAssistant";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- private static final String TAG_ASSISTANT = "assistant";
- private static final String TAG_IMPRESSION = "impression-set";
- private static final String ATT_KEY = "key";
- private static final int DB_VERSION = 1;
- private static final String ATTR_VERSION = "version";
- private final ExecutorService mSingleThreadExecutor = Executors.newSingleThreadExecutor();
-
- private static final ArrayList PREJUDICAL_DISMISSALS = new ArrayList<>();
- static {
- PREJUDICAL_DISMISSALS.add(REASON_CANCEL);
- PREJUDICAL_DISMISSALS.add(REASON_LISTENER_CANCEL);
- }
-
- private SmartActionsHelper mSmartActionsHelper;
- private NotificationCategorizer mNotificationCategorizer;
-
- // key : impressions tracker
- // TODO: prune deleted channels and apps
- private final ArrayMap mkeyToImpressions = new ArrayMap<>();
- // SBN key : entry
- protected ArrayMap mLiveNotifications = new ArrayMap<>();
-
- private Ranking mFakeRanking = null;
- private AtomicFile mFile = null;
- private IPackageManager mPackageManager;
-
- @VisibleForTesting
- protected AssistantSettings.Factory mSettingsFactory = AssistantSettings.FACTORY;
- @VisibleForTesting
- protected AssistantSettings mSettings;
- private SmsHelper mSmsHelper;
-
- public Assistant() {
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- // Contexts are correctly hooked up by the creation step, which is required for the observer
- // to be hooked up/initialized.
- mPackageManager = ActivityThread.getPackageManager();
- mSettings = mSettingsFactory.createAndRegister(mHandler,
- getApplicationContext().getContentResolver(), getUserId(), this::updateThresholds);
- mSmartActionsHelper = new SmartActionsHelper(getContext(), mSettings);
- mNotificationCategorizer = new NotificationCategorizer();
- mSmsHelper = new SmsHelper(this);
- mSmsHelper.initialize();
- }
-
- @Override
- public void onDestroy() {
- // This null check is only for the unit tests as ServiceTestCase.tearDown calls onDestroy
- // without having first called onCreate.
- if (mSmsHelper != null) {
- mSmsHelper.destroy();
- }
- super.onDestroy();
- }
-
- private void loadFile() {
- if (DEBUG) Slog.d(TAG, "loadFile");
- AsyncTask.execute(() -> {
- InputStream infile = null;
- try {
- infile = mFile.openRead();
- readXml(infile);
- } catch (FileNotFoundException e) {
- Log.d(TAG, "File doesn't exist or isn't readable yet");
- } catch (IOException e) {
- Log.e(TAG, "Unable to read channel impressions", e);
- } catch (NumberFormatException | XmlPullParserException e) {
- Log.e(TAG, "Unable to parse channel impressions", e);
- } finally {
- IoUtils.closeQuietly(infile);
- }
- });
- }
-
- protected void readXml(InputStream stream)
- throws XmlPullParserException, NumberFormatException, IOException {
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
- final int outerDepth = parser.getDepth();
- while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (!TAG_ASSISTANT.equals(parser.getName())) {
- continue;
- }
- final int impressionOuterDepth = parser.getDepth();
- while (XmlUtils.nextElementWithin(parser, impressionOuterDepth)) {
- if (!TAG_IMPRESSION.equals(parser.getName())) {
- continue;
- }
- String key = parser.getAttributeValue(null, ATT_KEY);
- ChannelImpressions ci = createChannelImpressionsWithThresholds();
- ci.populateFromXml(parser);
- synchronized (mkeyToImpressions) {
- ci.append(mkeyToImpressions.get(key));
- mkeyToImpressions.put(key, ci);
- }
- }
- }
- }
-
- private void saveFile() {
- AsyncTask.execute(() -> {
- final FileOutputStream stream;
- try {
- stream = mFile.startWrite();
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save policy file", e);
- return;
- }
- try {
- final XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
- writeXml(out);
- mFile.finishWrite(stream);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save impressions file, restoring backup", e);
- mFile.failWrite(stream);
- }
- });
- }
-
- protected void writeXml(XmlSerializer out) throws IOException {
- out.startDocument(null, true);
- out.startTag(null, TAG_ASSISTANT);
- out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
- synchronized (mkeyToImpressions) {
- for (Map.Entry entry
- : mkeyToImpressions.entrySet()) {
- // TODO: ensure channel still exists
- out.startTag(null, TAG_IMPRESSION);
- out.attribute(null, ATT_KEY, entry.getKey());
- entry.getValue().writeXml(out);
- out.endTag(null, TAG_IMPRESSION);
- }
- }
- out.endTag(null, TAG_ASSISTANT);
- out.endDocument();
- }
-
- @Override
- public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
- // we use the version with channel, so this is never called.
- return null;
- }
-
- @Override
- public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
- NotificationChannel channel) {
- if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey() + " on " + channel.getId());
- if (!isForCurrentUser(sbn)) {
- return null;
- }
- mSingleThreadExecutor.submit(() -> {
- NotificationEntry entry =
- new NotificationEntry(getContext(), mPackageManager, sbn, channel, mSmsHelper);
- SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(entry);
- if (DEBUG) {
- Log.d(TAG, String.format(
- "Creating Adjustment for %s, with %d actions, and %d replies.",
- sbn.getKey(), suggestions.actions.size(), suggestions.replies.size()));
- }
- Adjustment adjustment = createEnqueuedNotificationAdjustment(
- entry, suggestions.actions, suggestions.replies);
- adjustNotification(adjustment);
- });
- return null;
- }
-
- /** A convenience helper for creating an adjustment for an SBN. */
- @VisibleForTesting
- @Nullable
- Adjustment createEnqueuedNotificationAdjustment(
- @NonNull NotificationEntry entry,
- @NonNull ArrayList smartActions,
- @NonNull ArrayList smartReplies) {
- Bundle signals = new Bundle();
-
- if (!smartActions.isEmpty()) {
- signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, smartActions);
- }
- if (!smartReplies.isEmpty()) {
- signals.putCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES, smartReplies);
- }
- if (mSettings.mNewInterruptionModel) {
- if (mNotificationCategorizer.shouldSilence(entry)) {
- final int importance = entry.getImportance() < IMPORTANCE_LOW
- ? entry.getImportance() : IMPORTANCE_LOW;
- signals.putInt(KEY_IMPORTANCE, importance);
- } else {
- // Even if no change is made, send an identity adjustment for metric logging.
- signals.putInt(KEY_IMPORTANCE, entry.getImportance());
- }
- }
-
- return new Adjustment(
- entry.getSbn().getPackageName(),
- entry.getSbn().getKey(),
- signals,
- "",
- entry.getSbn().getUserId());
- }
-
- @Override
- public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
- if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
- try {
- if (!isForCurrentUser(sbn)) {
- return;
- }
- Ranking ranking = getRanking(sbn.getKey(), rankingMap);
- if (ranking != null && ranking.getChannel() != null) {
- NotificationEntry entry = new NotificationEntry(getContext(), mPackageManager,
- sbn, ranking.getChannel(), mSmsHelper);
- String key = getKey(
- sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId());
- boolean shouldTriggerBlock;
- synchronized (mkeyToImpressions) {
- ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
- createChannelImpressionsWithThresholds());
- mkeyToImpressions.put(key, ci);
- shouldTriggerBlock = ci.shouldTriggerBlock();
- }
- if (ranking.getImportance() > IMPORTANCE_MIN && shouldTriggerBlock) {
- adjustNotification(createNegativeAdjustment(
- sbn.getPackageName(), sbn.getKey(), sbn.getUserId()));
- }
- mLiveNotifications.put(sbn.getKey(), entry);
- }
- } catch (Throwable e) {
- Log.e(TAG, "Error occurred processing post", e);
- }
- }
-
- @Override
- public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
- NotificationStats stats, int reason) {
- try {
- if (!isForCurrentUser(sbn)) {
- return;
- }
-
- boolean updatedImpressions = false;
- String channelId = mLiveNotifications.remove(sbn.getKey()).getChannel().getId();
- String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId);
- synchronized (mkeyToImpressions) {
- ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
- createChannelImpressionsWithThresholds());
- if (stats != null && stats.hasSeen()) {
- ci.incrementViews();
- updatedImpressions = true;
- }
- if (PREJUDICAL_DISMISSALS.contains(reason)) {
- if ((!sbn.isAppGroup() || sbn.getNotification().isGroupChild())
- && !stats.hasInteracted()
- && stats.getDismissalSurface() != NotificationStats.DISMISSAL_AOD
- && stats.getDismissalSurface() != NotificationStats.DISMISSAL_PEEK
- && stats.getDismissalSurface() != NotificationStats.DISMISSAL_OTHER) {
- if (DEBUG) Log.i(TAG, "increment dismissals " + key);
- ci.incrementDismissals();
- updatedImpressions = true;
- } else {
- if (DEBUG) Slog.i(TAG, "reset streak " + key);
- if (ci.getStreak() > 0) {
- updatedImpressions = true;
- }
- ci.resetStreak();
- }
- }
- mkeyToImpressions.put(key, ci);
- }
- if (updatedImpressions) {
- saveFile();
- }
- } catch (Throwable e) {
- Slog.e(TAG, "Error occurred processing removal of " + sbn, e);
- }
- }
-
- @Override
- public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
- String snoozeCriterionId) {
- }
-
- @Override
- public void onNotificationsSeen(List keys) {
- }
-
- @Override
- public void onNotificationExpansionChanged(@NonNull String key, boolean isUserAction,
- boolean isExpanded) {
- if (DEBUG) {
- Log.d(TAG, "onNotificationExpansionChanged() called with: key = [" + key
- + "], isUserAction = [" + isUserAction + "], isExpanded = [" + isExpanded
- + "]");
- }
- NotificationEntry entry = mLiveNotifications.get(key);
-
- if (entry != null) {
- mSingleThreadExecutor.submit(
- () -> mSmartActionsHelper.onNotificationExpansionChanged(entry, isExpanded));
- }
- }
-
- @Override
- public void onNotificationDirectReplied(@NonNull String key) {
- if (DEBUG) Log.i(TAG, "onNotificationDirectReplied " + key);
- mSingleThreadExecutor.submit(() -> mSmartActionsHelper.onNotificationDirectReplied(key));
- }
-
- @Override
- public void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply,
- @Source int source) {
- if (DEBUG) {
- Log.d(TAG, "onSuggestedReplySent() called with: key = [" + key + "], reply = [" + reply
- + "], source = [" + source + "]");
- }
- mSingleThreadExecutor.submit(
- () -> mSmartActionsHelper.onSuggestedReplySent(key, reply, source));
- }
-
- @Override
- public void onActionInvoked(@NonNull String key, @NonNull Notification.Action action,
- @Source int source) {
- if (DEBUG) {
- Log.d(TAG,
- "onActionInvoked() called with: key = [" + key + "], action = [" + action.title
- + "], source = [" + source + "]");
- }
- mSingleThreadExecutor.submit(
- () -> mSmartActionsHelper.onActionClicked(key, action, source));
- }
-
- @Override
- public void onListenerConnected() {
- if (DEBUG) Log.i(TAG, "CONNECTED");
- try {
- mFile = new AtomicFile(new File(new File(
- Environment.getDataUserCePackageDirectory(
- StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()),
- "assistant"), "blocking_helper_stats.xml"));
- loadFile();
- for (StatusBarNotification sbn : getActiveNotifications()) {
- onNotificationPosted(sbn);
- }
- } catch (Throwable e) {
- Log.e(TAG, "Error occurred on connection", e);
- }
- }
-
- @Override
- public void onListenerDisconnected() {
- }
-
- private boolean isForCurrentUser(StatusBarNotification sbn) {
- return sbn != null && sbn.getUserId() == UserHandle.myUserId();
- }
-
- protected String getKey(String pkg, int userId, String channelId) {
- return pkg + "|" + userId + "|" + channelId;
- }
-
- private Ranking getRanking(String key, RankingMap rankingMap) {
- if (mFakeRanking != null) {
- return mFakeRanking;
- }
- Ranking ranking = new Ranking();
- rankingMap.getRanking(key, ranking);
- return ranking;
- }
-
- private Adjustment createNegativeAdjustment(String packageName, String key, int user) {
- if (DEBUG) Log.d(TAG, "User probably doesn't want " + key);
- Bundle signals = new Bundle();
- signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
- return new Adjustment(packageName, key, signals, "", user);
- }
-
- // for testing
-
- @VisibleForTesting
- public void setFile(AtomicFile file) {
- mFile = file;
- }
-
- @VisibleForTesting
- public void setFakeRanking(Ranking ranking) {
- mFakeRanking = ranking;
- }
-
- @VisibleForTesting
- public void setNoMan(INotificationManager noMan) {
- mNoMan = noMan;
- }
-
- @VisibleForTesting
- public void setContext(Context context) {
- mSystemContext = context;
- }
-
- @VisibleForTesting
- public void setPackageManager(IPackageManager pm) {
- mPackageManager = pm;
- }
-
- @VisibleForTesting
- public ChannelImpressions getImpressions(String key) {
- synchronized (mkeyToImpressions) {
- return mkeyToImpressions.get(key);
- }
- }
-
- @VisibleForTesting
- public void insertImpressions(String key, ChannelImpressions ci) {
- synchronized (mkeyToImpressions) {
- mkeyToImpressions.put(key, ci);
- }
- }
-
- private ChannelImpressions createChannelImpressionsWithThresholds() {
- ChannelImpressions impressions = new ChannelImpressions();
- impressions.updateThresholds(mSettings.mDismissToViewRatioLimit, mSettings.mStreakLimit);
- return impressions;
- }
-
- private void updateThresholds() {
- // Update all existing channel impression objects with any new limits/thresholds.
- synchronized (mkeyToImpressions) {
- for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) {
- channelImpressions.updateThresholds(
- mSettings.mDismissToViewRatioLimit, mSettings.mStreakLimit);
- }
- }
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
deleted file mode 100644
index 296db46d2624a..0000000000000
--- a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
+++ /dev/null
@@ -1,176 +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.ext.services.notification;
-
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-
-/**
- * Observes the settings for {@link Assistant}.
- */
-final class AssistantSettings extends ContentObserver {
- private static final String LOG_TAG = "AssistantSettings";
- public static Factory FACTORY = AssistantSettings::createAndRegister;
- private static final boolean DEFAULT_GENERATE_REPLIES = true;
- private static final boolean DEFAULT_GENERATE_ACTIONS = true;
- private static final int DEFAULT_NEW_INTERRUPTION_MODEL_INT = 1;
- private static final int DEFAULT_MAX_MESSAGES_TO_EXTRACT = 5;
- @VisibleForTesting
- static final int DEFAULT_MAX_SUGGESTIONS = 3;
-
- private static final Uri STREAK_LIMIT_URI =
- Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT);
- private static final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI =
- Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT);
- private static final Uri NOTIFICATION_NEW_INTERRUPTION_MODEL_URI =
- Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL);
-
- private final ContentResolver mResolver;
- private final int mUserId;
-
- private final Handler mHandler;
-
- @VisibleForTesting
- protected final Runnable mOnUpdateRunnable;
-
- // Actual configuration settings.
- float mDismissToViewRatioLimit;
- int mStreakLimit;
- boolean mGenerateReplies = DEFAULT_GENERATE_REPLIES;
- boolean mGenerateActions = DEFAULT_GENERATE_ACTIONS;
- boolean mNewInterruptionModel;
- int mMaxMessagesToExtract = DEFAULT_MAX_MESSAGES_TO_EXTRACT;
- int mMaxSuggestions = DEFAULT_MAX_SUGGESTIONS;
-
- private AssistantSettings(Handler handler, ContentResolver resolver, int userId,
- Runnable onUpdateRunnable) {
- super(handler);
- mHandler = handler;
- mResolver = resolver;
- mUserId = userId;
- mOnUpdateRunnable = onUpdateRunnable;
- }
-
- private static AssistantSettings createAndRegister(
- Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
- AssistantSettings assistantSettings =
- new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
- assistantSettings.register();
- assistantSettings.registerDeviceConfigs();
- return assistantSettings;
- }
-
- /**
- * Creates an instance but doesn't register it as an observer.
- */
- @VisibleForTesting
- protected static AssistantSettings createForTesting(
- Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
- return new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
- }
-
- private void register() {
- mResolver.registerContentObserver(
- DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, mUserId);
- mResolver.registerContentObserver(STREAK_LIMIT_URI, false, this, mUserId);
-
- // Update all uris on creation.
- update(null);
- }
-
- private void registerDeviceConfigs() {
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- this::postToHandler,
- (properties) -> onDeviceConfigPropertiesChanged(properties.getNamespace()));
-
- // Update the fields in this class from the current state of the device config.
- updateFromDeviceConfigFlags();
- }
-
- private void postToHandler(Runnable r) {
- this.mHandler.post(r);
- }
-
- @VisibleForTesting
- void onDeviceConfigPropertiesChanged(String namespace) {
- if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
- Log.e(LOG_TAG, "Received update from DeviceConfig for unrelated namespace: "
- + namespace);
- return;
- }
-
- updateFromDeviceConfigFlags();
- }
-
- private void updateFromDeviceConfigFlags() {
- mGenerateReplies = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, DEFAULT_GENERATE_REPLIES);
-
- mGenerateActions = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, DEFAULT_GENERATE_ACTIONS);
-
- mMaxMessagesToExtract = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT,
- DEFAULT_MAX_MESSAGES_TO_EXTRACT);
-
- mMaxSuggestions = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS, DEFAULT_MAX_SUGGESTIONS);
-
- mOnUpdateRunnable.run();
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- update(uri);
- }
-
- private void update(Uri uri) {
- if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) {
- mDismissToViewRatioLimit = Settings.Global.getFloat(
- mResolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
- ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT);
- }
- if (uri == null || STREAK_LIMIT_URI.equals(uri)) {
- mStreakLimit = Settings.Global.getInt(
- mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
- ChannelImpressions.DEFAULT_STREAK_LIMIT);
- }
- if (uri == null || NOTIFICATION_NEW_INTERRUPTION_MODEL_URI.equals(uri)) {
- int mNewInterruptionModelInt = Settings.Secure.getInt(
- mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL,
- DEFAULT_NEW_INTERRUPTION_MODEL_INT);
- mNewInterruptionModel = mNewInterruptionModelInt == 1;
- }
-
- mOnUpdateRunnable.run();
- }
-
- public interface Factory {
- AssistantSettings createAndRegister(Handler handler, ContentResolver resolver, int userId,
- Runnable onUpdateRunnable);
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
deleted file mode 100644
index ca3daad8d1090..0000000000000
--- a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
+++ /dev/null
@@ -1,211 +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.ext.services.notification;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-
-public final class ChannelImpressions implements Parcelable {
- private static final String TAG = "ExtAssistant.CI";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- static final float DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT = .8f;
- static final int DEFAULT_STREAK_LIMIT = 2;
- static final String ATT_DISMISSALS = "dismisses";
- static final String ATT_VIEWS = "views";
- static final String ATT_STREAK = "streak";
- static final String ATT_SENT = "sent";
- static final String ATT_INTERRUPTIVE = "interruptive";
-
- private int mDismissals = 0;
- private int mViews = 0;
- private int mStreak = 0;
-
- private float mDismissToViewRatioLimit;
- private int mStreakLimit;
-
- public ChannelImpressions() {
- mDismissToViewRatioLimit = DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT;
- mStreakLimit = DEFAULT_STREAK_LIMIT;
- }
-
- protected ChannelImpressions(Parcel in) {
- mDismissals = in.readInt();
- mViews = in.readInt();
- mStreak = in.readInt();
- mDismissToViewRatioLimit = in.readFloat();
- mStreakLimit = in.readInt();
- }
-
- public int getStreak() {
- return mStreak;
- }
-
- public int getDismissals() {
- return mDismissals;
- }
-
- public int getViews() {
- return mViews;
- }
-
- public void incrementDismissals() {
- mDismissals++;
- mStreak++;
- }
-
- void updateThresholds(float dismissToViewRatioLimit, int streakLimit) {
- mDismissToViewRatioLimit = dismissToViewRatioLimit;
- mStreakLimit = streakLimit;
- }
-
- @VisibleForTesting
- float getDismissToViewRatioLimit() {
- return mDismissToViewRatioLimit;
- }
-
- @VisibleForTesting
- int getStreakLimit() {
- return mStreakLimit;
- }
-
- public void append(ChannelImpressions additionalImpressions) {
- if (additionalImpressions != null) {
- mViews += additionalImpressions.getViews();
- mStreak += additionalImpressions.getStreak();
- mDismissals += additionalImpressions.getDismissals();
- }
- }
-
- public void incrementViews() {
- mViews++;
- }
-
- public void resetStreak() {
- mStreak = 0;
- }
-
- public boolean shouldTriggerBlock() {
- if (getViews() == 0) {
- return false;
- }
- if (DEBUG) {
- Log.d(TAG, "should trigger? " + getDismissals() + " " + getViews() + " " + getStreak());
- }
- return ((float) getDismissals() / getViews()) > mDismissToViewRatioLimit
- && getStreak() > mStreakLimit;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mDismissals);
- dest.writeInt(mViews);
- dest.writeInt(mStreak);
- dest.writeFloat(mDismissToViewRatioLimit);
- dest.writeInt(mStreakLimit);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public static final Creator CREATOR = new Creator() {
- @Override
- public ChannelImpressions createFromParcel(Parcel in) {
- return new ChannelImpressions(in);
- }
-
- @Override
- public ChannelImpressions[] newArray(int size) {
- return new ChannelImpressions[size];
- }
- };
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- ChannelImpressions that = (ChannelImpressions) o;
-
- if (mDismissals != that.mDismissals) return false;
- if (mViews != that.mViews) return false;
- return mStreak == that.mStreak;
- }
-
- @Override
- public int hashCode() {
- int result = mDismissals;
- result = 31 * result + mViews;
- result = 31 * result + mStreak;
- return result;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("ChannelImpressions{");
- sb.append("mDismissals=").append(mDismissals);
- sb.append(", mViews=").append(mViews);
- sb.append(", mStreak=").append(mStreak);
- sb.append(", thresholds=(").append(mDismissToViewRatioLimit);
- sb.append(",").append(mStreakLimit);
- sb.append(")}");
- return sb.toString();
- }
-
- protected void populateFromXml(XmlPullParser parser) {
- mDismissals = safeInt(parser, ATT_DISMISSALS, 0);
- mStreak = safeInt(parser, ATT_STREAK, 0);
- mViews = safeInt(parser, ATT_VIEWS, 0);
- }
-
- protected void writeXml(XmlSerializer out) throws IOException {
- if (mDismissals != 0) {
- out.attribute(null, ATT_DISMISSALS, String.valueOf(mDismissals));
- }
- if (mStreak != 0) {
- out.attribute(null, ATT_STREAK, String.valueOf(mStreak));
- }
- if (mViews != 0) {
- out.attribute(null, ATT_VIEWS, String.valueOf(mViews));
- }
- }
-
- private static int safeInt(XmlPullParser parser, String att, int defValue) {
- final String val = parser.getAttributeValue(null, att);
- return tryParseInt(val, defValue);
- }
-
- private static int tryParseInt(String value, int defValue) {
- if (TextUtils.isEmpty(value)) return defValue;
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
- return defValue;
- }
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/CopyCodeActivity.java b/packages/ExtServices/src/android/ext/services/notification/CopyCodeActivity.java
deleted file mode 100644
index 6708d4d695ec4..0000000000000
--- a/packages/ExtServices/src/android/ext/services/notification/CopyCodeActivity.java
+++ /dev/null
@@ -1,54 +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.ext.services.notification;
-
-import android.app.Activity;
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Intent;
-import android.ext.services.R;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.widget.Toast;
-
-/**
- * An activity that copies text in the Bundle.
- */
-public class CopyCodeActivity extends Activity {
- private static final String TAG = "CopyCodeActivity";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- handleIntent();
- finish();
- }
-
- private void handleIntent() {
- String code = getIntent().getStringExtra(Intent.EXTRA_TEXT);
- if (TextUtils.isEmpty(code)) {
- Log.w(TAG, "handleIntent: empty code");
- return;
- }
- ClipboardManager clipboardManager = getSystemService(ClipboardManager.class);
- ClipData clipData = ClipData.newPlainText(null, code);
- clipboardManager.setPrimaryClip(clipData);
- Toast.makeText(getApplicationContext(), R.string.code_copied_to_clipboard,
- Toast.LENGTH_SHORT).show();
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java b/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java
deleted file mode 100644
index 50cb0abd63297..0000000000000
--- a/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java
+++ /dev/null
@@ -1,73 +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.ext.services.notification;
-
-import android.annotation.NonNull;
-import android.util.ArrayMap;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextLinks;
-
-/**
- * Counts the entity types for smart actions. Some entity types are considered the same
- * type, like {@link TextClassifier#TYPE_DATE} and {@link TextClassifier#TYPE_DATE_TIME}.
- */
-class EntityTypeCounter {
-
- private static final ArrayMap ENTITY_TYPE_MAPPING = new ArrayMap<>();
-
- static {
- ENTITY_TYPE_MAPPING.put(TextClassifier.TYPE_DATE_TIME, TextClassifier.TYPE_DATE);
- }
-
- private final ArrayMap mEntityTypeCount = new ArrayMap<>();
-
-
- void increment(@NonNull String entityType) {
- entityType = convertToBaseEntityType(entityType);
- if (mEntityTypeCount.containsKey(entityType)) {
- mEntityTypeCount.put(entityType, mEntityTypeCount.get(entityType) + 1);
- } else {
- mEntityTypeCount.put(entityType, 1);
- }
- }
-
- int getCount(@NonNull String entityType) {
- entityType = convertToBaseEntityType(entityType);
- return mEntityTypeCount.getOrDefault(entityType, 0);
- }
-
- @NonNull
- private String convertToBaseEntityType(@NonNull String entityType) {
- return ENTITY_TYPE_MAPPING.getOrDefault(entityType, entityType);
- }
-
- /**
- * Given the links extracted from a piece of text, returns the frequency of each entity
- * type.
- */
- @NonNull
- static EntityTypeCounter fromTextLinks(@NonNull TextLinks links) {
- EntityTypeCounter counter = new EntityTypeCounter();
- for (TextLinks.TextLink link : links.getLinks()) {
- if (link.getEntityCount() == 0) {
- continue;
- }
- String entityType = link.getEntity(0);
- counter.increment(entityType);
- }
- return counter;
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java b/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java
deleted file mode 100644
index f95891cc89629..0000000000000
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java
+++ /dev/null
@@ -1,109 +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.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import android.annotation.IntDef;
-import android.app.Notification;
-import android.media.AudioAttributes;
-import android.os.Process;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Default categorizer for incoming notifications; used to determine what notifications
- * should be silenced.
- */
-// TODO: stop using @hide methods
-public class NotificationCategorizer {
-
- protected static final int CATEGORY_MIN = -3;
- protected static final int CATEGORY_EVERYTHING_ELSE = -2;
- protected static final int CATEGORY_ONGOING = -1;
- protected static final int CATEGORY_SYSTEM_LOW = 0;
- protected static final int CATEGORY_EVENT = 1;
- protected static final int CATEGORY_REMINDER = 2;
- protected static final int CATEGORY_SYSTEM = 3;
- protected static final int CATEGORY_PEOPLE = 4;
- protected static final int CATEGORY_ALARM = 5;
- protected static final int CATEGORY_CALL = 6;
- protected static final int CATEGORY_HIGH = 7;
-
- /** @hide */
- @IntDef(prefix = { "CATEGORY_" }, value = {
- CATEGORY_MIN, CATEGORY_EVERYTHING_ELSE, CATEGORY_ONGOING, CATEGORY_CALL,
- CATEGORY_SYSTEM_LOW, CATEGORY_EVENT, CATEGORY_REMINDER, CATEGORY_SYSTEM,
- CATEGORY_PEOPLE, CATEGORY_ALARM, CATEGORY_HIGH
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Category {}
-
- public boolean shouldSilence(NotificationEntry entry) {
- return shouldSilence(getCategory(entry));
- }
-
- @VisibleForTesting
- boolean shouldSilence(int category) {
- return category < CATEGORY_EVENT;
- }
-
- public int getCategory(NotificationEntry entry) {
- if (entry.getChannel() == null) {
- return CATEGORY_EVERYTHING_ELSE;
- }
- if (entry.getChannel().getImportance() == IMPORTANCE_MIN) {
- return CATEGORY_MIN;
- }
- if (entry.isCategory(Notification.CATEGORY_REMINDER)) {
- return CATEGORY_REMINDER;
- }
- if (entry.isCategory(Notification.CATEGORY_EVENT)) {
- return CATEGORY_EVENT;
- }
- if (entry.isCategory(Notification.CATEGORY_ALARM)
- || entry.isAudioAttributesUsage(AudioAttributes.USAGE_ALARM)) {
- return CATEGORY_ALARM;
- }
- // TODO: check for default phone app
- if (entry.isCategory(Notification.CATEGORY_CALL)) {
- return CATEGORY_CALL;
- }
- if (entry.involvesPeople()) {
- return CATEGORY_PEOPLE;
- }
- // TODO: is from signature app
- if (entry.getSbn().getUid() < Process.FIRST_APPLICATION_UID) {
- if (entry.getImportance() >= IMPORTANCE_DEFAULT) {
- return CATEGORY_SYSTEM;
- } else {
- return CATEGORY_SYSTEM_LOW;
- }
- }
- if (entry.getChannel().getImportance() == IMPORTANCE_HIGH) {
- return CATEGORY_HIGH;
- }
- if (entry.isOngoing()) {
- return CATEGORY_ONGOING;
- }
- return CATEGORY_EVERYTHING_ELSE;
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
deleted file mode 100644
index 1ffbac941dc33..0000000000000
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
+++ /dev/null
@@ -1,349 +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.ext.services.notification;
-
-import static android.app.Notification.CATEGORY_MESSAGE;
-import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.Person;
-import android.app.RemoteInput;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Icon;
-import android.media.AudioAttributes;
-import android.media.AudioSystem;
-import android.os.Build;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.util.ArrayList;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Holds data about notifications.
- */
-public class NotificationEntry {
- static final String TAG = "NotificationEntry";
-
- // Copied from hidden definitions in Notification.TvExtender
- private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
-
- private final Context mContext;
- private final StatusBarNotification mSbn;
- private final IPackageManager mPackageManager;
- private int mTargetSdkVersion = Build.VERSION_CODES.N_MR1;
- private final boolean mPreChannelsNotification;
- private final AudioAttributes mAttributes;
- private final NotificationChannel mChannel;
- private final int mImportance;
- private boolean mSeen;
- private boolean mIsShowActionEventLogged;
- private final SmsHelper mSmsHelper;
-
- private final Object mLock = new Object();
-
- public NotificationEntry(Context applicationContext, IPackageManager packageManager,
- StatusBarNotification sbn, NotificationChannel channel, SmsHelper smsHelper) {
- mContext = applicationContext;
- mSbn = cloneStatusBarNotificationLight(sbn);
- mChannel = channel;
- mPackageManager = packageManager;
- mPreChannelsNotification = isPreChannelsNotification();
- mAttributes = calculateAudioAttributes();
- mImportance = calculateInitialImportance();
- mSmsHelper = smsHelper;
- }
-
- /** Adapted from {@code Notification.lightenPayload}. */
- @SuppressWarnings("nullness")
- private static void lightenNotificationPayload(Notification notification) {
- notification.tickerView = null;
- notification.contentView = null;
- notification.bigContentView = null;
- notification.headsUpContentView = null;
- notification.largeIcon = null;
- if (notification.extras != null && !notification.extras.isEmpty()) {
- final Set keyset = notification.extras.keySet();
- final int keysetSize = keyset.size();
- final String[] keys = keyset.toArray(new String[keysetSize]);
- for (int i = 0; i < keysetSize; i++) {
- final String key = keys[i];
- if (EXTRA_TV_EXTENDER.equals(key)
- || Notification.EXTRA_MESSAGES.equals(key)
- || Notification.EXTRA_MESSAGING_PERSON.equals(key)
- || Notification.EXTRA_PEOPLE_LIST.equals(key)) {
- continue;
- }
- final Object obj = notification.extras.get(key);
- if (obj != null
- && (obj instanceof Parcelable
- || obj instanceof Parcelable[]
- || obj instanceof SparseArray
- || obj instanceof ArrayList)) {
- notification.extras.remove(key);
- }
- }
- }
- }
-
- /** An interpretation of {@code Notification.cloneInto} with heavy=false. */
- private Notification cloneNotificationLight(Notification notification) {
- // We can't just use clone() here because the only way to remove the icons is with the
- // builder, which we can only create with a Context.
- Notification lightNotification =
- Notification.Builder.recoverBuilder(mContext, notification)
- .setSmallIcon(0)
- .setLargeIcon((Icon) null)
- .build();
- lightenNotificationPayload(lightNotification);
- return lightNotification;
- }
-
- /** Adapted from {@code StatusBarNotification.cloneLight}. */
- public StatusBarNotification cloneStatusBarNotificationLight(StatusBarNotification sbn) {
- return new StatusBarNotification(
- sbn.getPackageName(),
- sbn.getOpPkg(),
- sbn.getId(),
- sbn.getTag(),
- sbn.getUid(),
- /*initialPid=*/ 0,
- /*score=*/ 0,
- cloneNotificationLight(sbn.getNotification()),
- sbn.getUser(),
- sbn.getPostTime());
- }
-
- private boolean isPreChannelsNotification() {
- try {
- ApplicationInfo info = mPackageManager.getApplicationInfo(
- mSbn.getPackageName(), PackageManager.MATCH_ALL,
- mSbn.getUserId());
- if (info != null) {
- mTargetSdkVersion = info.targetSdkVersion;
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Couldn't look up " + mSbn.getPackageName());
- }
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
- if (mTargetSdkVersion < Build.VERSION_CODES.O) {
- return true;
- }
- }
- return false;
- }
-
- private AudioAttributes calculateAudioAttributes() {
- final Notification n = getNotification();
- AudioAttributes attributes = getChannel().getAudioAttributes();
- if (attributes == null) {
- attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
- }
-
- if (mPreChannelsNotification
- && (getChannel().getUserLockedFields()
- & NotificationChannel.USER_LOCKED_SOUND) == 0) {
- if (n.audioAttributes != null) {
- // prefer audio attributes to stream type
- attributes = n.audioAttributes;
- } else if (n.audioStreamType >= 0
- && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
- // the stream type is valid, use it
- attributes = new AudioAttributes.Builder()
- .setInternalLegacyStreamType(n.audioStreamType)
- .build();
- } else if (n.audioStreamType != AudioSystem.STREAM_DEFAULT) {
- Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
- }
- }
- return attributes;
- }
-
- private int calculateInitialImportance() {
- final Notification n = getNotification();
- int importance = getChannel().getImportance();
- int requestedImportance = IMPORTANCE_DEFAULT;
-
- // Migrate notification flags to scores
- if ((n.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
- n.priority = Notification.PRIORITY_MAX;
- }
-
- n.priority = clamp(n.priority, Notification.PRIORITY_MIN,
- Notification.PRIORITY_MAX);
- switch (n.priority) {
- case Notification.PRIORITY_MIN:
- requestedImportance = IMPORTANCE_MIN;
- break;
- case Notification.PRIORITY_LOW:
- requestedImportance = IMPORTANCE_LOW;
- break;
- case Notification.PRIORITY_DEFAULT:
- requestedImportance = IMPORTANCE_DEFAULT;
- break;
- case Notification.PRIORITY_HIGH:
- case Notification.PRIORITY_MAX:
- requestedImportance = IMPORTANCE_HIGH;
- break;
- }
-
- if (mPreChannelsNotification
- && (importance == IMPORTANCE_UNSPECIFIED
- || (getChannel().getUserLockedFields()
- & USER_LOCKED_IMPORTANCE) == 0)) {
- if (n.fullScreenIntent != null) {
- requestedImportance = IMPORTANCE_HIGH;
- }
- importance = requestedImportance;
- }
-
- return importance;
- }
-
- public boolean isCategory(String category) {
- return Objects.equals(getNotification().category, category);
- }
-
- /**
- * Similar to {@link #isCategory(String)}, but checking the public version of the notification,
- * if available.
- */
- public boolean isPublicVersionCategory(String category) {
- Notification publicVersion = getNotification().publicVersion;
- if (publicVersion == null) {
- return false;
- }
- return Objects.equals(publicVersion.category, category);
- }
-
- public boolean isAudioAttributesUsage(int usage) {
- return mAttributes != null && mAttributes.getUsage() == usage;
- }
-
- private boolean hasPerson() {
- // TODO: cache favorite and recent contacts to check contact affinity
- ArrayList people = getNotification().extras.getParcelableArrayList(
- Notification.EXTRA_PEOPLE_LIST);
- return people != null && !people.isEmpty();
- }
-
- protected boolean hasStyle(Class targetStyle) {
- Class extends Notification.Style> style = getNotification().getNotificationStyle();
- return targetStyle.equals(style);
- }
-
- protected boolean isOngoing() {
- return (getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
- }
-
- protected boolean involvesPeople() {
- return isMessaging()
- || hasStyle(Notification.InboxStyle.class)
- || hasPerson()
- || isDefaultSmsApp();
- }
-
- private boolean isDefaultSmsApp() {
- ComponentName defaultSmsApp = mSmsHelper.getDefaultSmsApplication();
- if (defaultSmsApp == null) {
- return false;
- }
- return mSbn.getPackageName().equals(defaultSmsApp.getPackageName());
- }
-
- protected boolean isMessaging() {
- return isCategory(CATEGORY_MESSAGE)
- || isPublicVersionCategory(CATEGORY_MESSAGE)
- || hasStyle(Notification.MessagingStyle.class);
- }
-
- public boolean hasInlineReply() {
- Notification.Action[] actions = getNotification().actions;
- if (actions == null) {
- return false;
- }
- for (Notification.Action action : actions) {
- RemoteInput[] remoteInputs = action.getRemoteInputs();
- if (remoteInputs == null) {
- continue;
- }
- for (RemoteInput remoteInput : remoteInputs) {
- if (remoteInput.getAllowFreeFormInput()) {
- return true;
- }
- }
- }
- return false;
- }
-
- public void setSeen() {
- synchronized (mLock) {
- mSeen = true;
- }
- }
-
- public void setShowActionEventLogged() {
- synchronized (mLock) {
- mIsShowActionEventLogged = true;
- }
- }
-
- public boolean hasSeen() {
- synchronized (mLock) {
- return mSeen;
- }
- }
-
- public boolean isShowActionEventLogged() {
- synchronized (mLock) {
- return mIsShowActionEventLogged;
- }
- }
-
- public StatusBarNotification getSbn() {
- return mSbn;
- }
-
- public Notification getNotification() {
- return getSbn().getNotification();
- }
-
- public NotificationChannel getChannel() {
- return mChannel;
- }
-
- public int getImportance() {
- return mImportance;
- }
-
- private int clamp(int x, int low, int high) {
- return (x < low) ? low : ((x > high) ? high : x);
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
deleted file mode 100644
index 93c522e8cec85..0000000000000
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ /dev/null
@@ -1,502 +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.ext.services.notification;
-
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Person;
-import android.app.RemoteAction;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.content.Intent;
-import android.ext.services.R;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.os.Process;
-import android.service.notification.NotificationAssistantService;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.LruCache;
-import android.util.Pair;
-import android.view.textclassifier.ConversationAction;
-import android.view.textclassifier.ConversationActions;
-import android.view.textclassifier.TextClassificationContext;
-import android.view.textclassifier.TextClassificationManager;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextClassifierEvent;
-
-import com.android.internal.util.ArrayUtils;
-
-import java.time.Instant;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Generates suggestions from incoming notifications.
- *
- * Methods in this class should be called in a single worker thread.
- */
-public class SmartActionsHelper {
- static final String ENTITIES_EXTRAS = "entities-extras";
- static final String KEY_ACTION_TYPE = "action_type";
- static final String KEY_ACTION_SCORE = "action_score";
- static final String KEY_TEXT = "text";
- // If a notification has any of these flags set, it's inelgibile for actions being added.
- private static final int FLAG_MASK_INELGIBILE_FOR_ACTIONS =
- Notification.FLAG_ONGOING_EVENT
- | Notification.FLAG_FOREGROUND_SERVICE
- | Notification.FLAG_GROUP_SUMMARY
- | Notification.FLAG_NO_CLEAR;
- private static final int MAX_RESULT_ID_TO_CACHE = 20;
-
- private static final List HINTS =
- Collections.singletonList(ConversationActions.Request.HINT_FOR_NOTIFICATION);
- private static final ConversationActions EMPTY_CONVERSATION_ACTIONS =
- new ConversationActions(Collections.emptyList(), null);
-
- private Context mContext;
- private TextClassificationManager mTextClassificationManager;
- private AssistantSettings mSettings;
- private LruCache mSessionCache = new LruCache<>(MAX_RESULT_ID_TO_CACHE);
-
- SmartActionsHelper(Context context, AssistantSettings settings) {
- mContext = context;
- mTextClassificationManager = mContext.getSystemService(TextClassificationManager.class);
- mSettings = settings;
- }
-
- SmartSuggestions suggest(NotificationEntry entry) {
- // Whenever suggest() is called on a notification, its previous session is ended.
- mSessionCache.remove(entry.getSbn().getKey());
-
- boolean eligibleForReplyAdjustment =
- mSettings.mGenerateReplies && isEligibleForReplyAdjustment(entry);
- boolean eligibleForActionAdjustment =
- mSettings.mGenerateActions && isEligibleForActionAdjustment(entry);
-
- ConversationActions conversationActionsResult =
- suggestConversationActions(
- entry,
- eligibleForReplyAdjustment,
- eligibleForActionAdjustment);
-
- String resultId = conversationActionsResult.getId();
- List conversationActions =
- conversationActionsResult.getConversationActions();
-
- ArrayList replies = new ArrayList<>();
- Map repliesScore = new ArrayMap<>();
- for (ConversationAction conversationAction : conversationActions) {
- CharSequence textReply = conversationAction.getTextReply();
- if (TextUtils.isEmpty(textReply)) {
- continue;
- }
- replies.add(textReply);
- repliesScore.put(textReply, conversationAction.getConfidenceScore());
- }
-
- ArrayList actions = new ArrayList<>();
- for (ConversationAction conversationAction : conversationActions) {
- if (!TextUtils.isEmpty(conversationAction.getTextReply())) {
- continue;
- }
- Notification.Action notificationAction;
- if (conversationAction.getAction() == null) {
- notificationAction =
- createNotificationActionWithoutRemoteAction(conversationAction);
- } else {
- notificationAction = createNotificationActionFromRemoteAction(
- conversationAction.getAction(),
- conversationAction.getType(),
- conversationAction.getConfidenceScore());
- }
- if (notificationAction != null) {
- actions.add(notificationAction);
- }
- }
-
- // Start a new session for logging if necessary.
- if (!TextUtils.isEmpty(resultId)
- && !conversationActions.isEmpty()
- && suggestionsMightBeUsedInNotification(
- entry, !actions.isEmpty(), !replies.isEmpty())) {
- mSessionCache.put(entry.getSbn().getKey(), new Session(resultId, repliesScore));
- }
-
- return new SmartSuggestions(replies, actions);
- }
-
- /**
- * Creates notification action from ConversationAction that does not come up a RemoteAction.
- * It could happen because we don't have common intents for some actions, like copying text.
- */
- @Nullable
- private Notification.Action createNotificationActionWithoutRemoteAction(
- ConversationAction conversationAction) {
- if (ConversationAction.TYPE_COPY.equals(conversationAction.getType())) {
- return createCopyCodeAction(conversationAction);
- }
- return null;
- }
-
- @Nullable
- private Notification.Action createCopyCodeAction(ConversationAction conversationAction) {
- Bundle extras = conversationAction.getExtras();
- if (extras == null) {
- return null;
- }
- Bundle entitiesExtas = extras.getParcelable(ENTITIES_EXTRAS);
- if (entitiesExtas == null) {
- return null;
- }
- String code = entitiesExtas.getString(KEY_TEXT);
- if (TextUtils.isEmpty(code)) {
- return null;
- }
- String contentDescription = mContext.getString(R.string.copy_code_desc, code);
- Intent intent = new Intent(mContext, CopyCodeActivity.class);
- intent.putExtra(Intent.EXTRA_TEXT, code);
-
- RemoteAction remoteAction = new RemoteAction(Icon.createWithResource(
- mContext.getResources(),
- com.android.internal.R.drawable.ic_menu_copy_material),
- code,
- contentDescription,
- PendingIntent.getActivity(
- mContext,
- code.hashCode(),
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT
- ));
-
- return createNotificationActionFromRemoteAction(
- remoteAction,
- ConversationAction.TYPE_COPY,
- conversationAction.getConfidenceScore());
- }
-
- /**
- * Returns whether the suggestion might be used in the notifications in SysUI.
- *
- * Currently, NAS has no idea if suggestions will actually be used in the notification, and thus
- * this function tries to make a heuristic. This function tries to optimize the precision,
- * that means when it is unsure, it will return false. The objective is to avoid false positive,
- * which could pollute the log and CTR as we are logging click rate of suggestions that could
- * be never visible to users. On the other hand, it is fine to have false negative because
- * it would be just like sampling.
- */
- private boolean suggestionsMightBeUsedInNotification(
- NotificationEntry notificationEntry, boolean hasSmartAction, boolean hasSmartReply) {
- Notification notification = notificationEntry.getNotification();
- boolean hasAppGeneratedContextualActions = !notification.getContextualActions().isEmpty();
-
- Pair freeformRemoteInputAndAction =
- notification.findRemoteInputActionPair(/* requiresFreeform */ true);
- boolean hasAppGeneratedReplies = false;
- boolean allowGeneratedReplies = false;
- if (freeformRemoteInputAndAction != null) {
- RemoteInput freeformRemoteInput = freeformRemoteInputAndAction.first;
- Notification.Action actionWithFreeformRemoteInput = freeformRemoteInputAndAction.second;
- hasAppGeneratedReplies = !ArrayUtils.isEmpty(freeformRemoteInput.getChoices());
- allowGeneratedReplies = actionWithFreeformRemoteInput.getAllowGeneratedReplies();
- }
-
- if (hasAppGeneratedReplies || hasAppGeneratedContextualActions) {
- return false;
- }
- return hasSmartAction && notification.getAllowSystemGeneratedContextualActions()
- || hasSmartReply && allowGeneratedReplies;
- }
-
- private void reportActionsGenerated(
- String resultId, List conversationActions) {
- if (TextUtils.isEmpty(resultId)) {
- return;
- }
- TextClassifierEvent textClassifierEvent =
- createTextClassifierEventBuilder(
- TextClassifierEvent.TYPE_ACTIONS_GENERATED, resultId)
- .setEntityTypes(conversationActions.stream()
- .map(ConversationAction::getType)
- .toArray(String[]::new))
- .build();
- getTextClassifier().onTextClassifierEvent(textClassifierEvent);
- }
-
- /**
- * Adds action adjustments based on the notification contents.
- */
- private ConversationActions suggestConversationActions(
- NotificationEntry entry,
- boolean includeReplies,
- boolean includeActions) {
- if (!includeReplies && !includeActions) {
- return EMPTY_CONVERSATION_ACTIONS;
- }
- List messages = extractMessages(entry.getNotification());
- if (messages.isEmpty()) {
- return EMPTY_CONVERSATION_ACTIONS;
- }
- // Do not generate smart actions if the last message is from the local user.
- ConversationActions.Message lastMessage = messages.get(messages.size() - 1);
- if (arePersonsEqual(
- ConversationActions.Message.PERSON_USER_SELF, lastMessage.getAuthor())) {
- return EMPTY_CONVERSATION_ACTIONS;
- }
-
- TextClassifier.EntityConfig.Builder typeConfigBuilder =
- new TextClassifier.EntityConfig.Builder();
- if (!includeReplies) {
- typeConfigBuilder.setExcludedTypes(
- Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY));
- } else if (!includeActions) {
- typeConfigBuilder
- .setIncludedTypes(
- Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
- .includeTypesFromTextClassifier(false);
- }
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(messages)
- .setMaxSuggestions(mSettings.mMaxSuggestions)
- .setHints(HINTS)
- .setTypeConfig(typeConfigBuilder.build())
- .build();
- ConversationActions conversationActions =
- getTextClassifier().suggestConversationActions(request);
- reportActionsGenerated(
- conversationActions.getId(), conversationActions.getConversationActions());
- return conversationActions;
- }
-
- void onNotificationExpansionChanged(NotificationEntry entry, boolean isExpanded) {
- if (!isExpanded) {
- return;
- }
- Session session = mSessionCache.get(entry.getSbn().getKey());
- if (session == null) {
- return;
- }
- // Only report if this is the first time the user sees these suggestions.
- if (entry.isShowActionEventLogged()) {
- return;
- }
- entry.setShowActionEventLogged();
- TextClassifierEvent textClassifierEvent =
- createTextClassifierEventBuilder(
- TextClassifierEvent.TYPE_ACTIONS_SHOWN, session.resultId)
- .build();
- // TODO: If possible, report which replies / actions are actually seen by user.
- getTextClassifier().onTextClassifierEvent(textClassifierEvent);
- }
-
- void onNotificationDirectReplied(String key) {
- Session session = mSessionCache.get(key);
- if (session == null) {
- return;
- }
- TextClassifierEvent textClassifierEvent =
- createTextClassifierEventBuilder(
- TextClassifierEvent.TYPE_MANUAL_REPLY, session.resultId)
- .build();
- getTextClassifier().onTextClassifierEvent(textClassifierEvent);
- }
-
- void onSuggestedReplySent(String key, CharSequence reply,
- @NotificationAssistantService.Source int source) {
- if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) {
- return;
- }
- Session session = mSessionCache.get(key);
- if (session == null) {
- return;
- }
- TextClassifierEvent textClassifierEvent =
- createTextClassifierEventBuilder(
- TextClassifierEvent.TYPE_SMART_ACTION, session.resultId)
- .setEntityTypes(ConversationAction.TYPE_TEXT_REPLY)
- .setScores(session.repliesScores.getOrDefault(reply, 0f))
- .build();
- getTextClassifier().onTextClassifierEvent(textClassifierEvent);
- }
-
- void onActionClicked(String key, Notification.Action action,
- @NotificationAssistantService.Source int source) {
- if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) {
- return;
- }
- Session session = mSessionCache.get(key);
- if (session == null) {
- return;
- }
- String actionType = action.getExtras().getString(KEY_ACTION_TYPE);
- if (actionType == null) {
- return;
- }
- TextClassifierEvent textClassifierEvent =
- createTextClassifierEventBuilder(
- TextClassifierEvent.TYPE_SMART_ACTION, session.resultId)
- .setEntityTypes(actionType)
- .build();
- getTextClassifier().onTextClassifierEvent(textClassifierEvent);
- }
-
- private Notification.Action createNotificationActionFromRemoteAction(
- RemoteAction remoteAction, String actionType, float score) {
- Icon icon = remoteAction.shouldShowIcon()
- ? remoteAction.getIcon()
- : Icon.createWithResource(mContext, com.android.internal.R.drawable.ic_action_open);
- Bundle extras = new Bundle();
- extras.putString(KEY_ACTION_TYPE, actionType);
- extras.putFloat(KEY_ACTION_SCORE, score);
- return new Notification.Action.Builder(
- icon,
- remoteAction.getTitle(),
- remoteAction.getActionIntent())
- .setContextual(true)
- .addExtras(extras)
- .build();
- }
-
- private TextClassifierEvent.ConversationActionsEvent.Builder createTextClassifierEventBuilder(
- int eventType, String resultId) {
- return new TextClassifierEvent.ConversationActionsEvent.Builder(eventType)
- .setEventContext(
- new TextClassificationContext.Builder(
- mContext.getPackageName(), TextClassifier.WIDGET_TYPE_NOTIFICATION)
- .build())
- .setResultId(resultId);
- }
-
- /**
- * Returns whether a notification is eligible for action adjustments.
- *
- *
We exclude system notifications, those that get refreshed frequently, or ones that relate
- * to fundamental phone functionality where any error would result in a very negative user
- * experience.
- */
- private boolean isEligibleForActionAdjustment(NotificationEntry entry) {
- Notification notification = entry.getNotification();
- String pkg = entry.getSbn().getPackageName();
- if (!Process.myUserHandle().equals(entry.getSbn().getUser())) {
- return false;
- }
- if ((notification.flags & FLAG_MASK_INELGIBILE_FOR_ACTIONS) != 0) {
- return false;
- }
- if (TextUtils.isEmpty(pkg) || pkg.equals("android")) {
- return false;
- }
- // For now, we are only interested in messages.
- return entry.isMessaging();
- }
-
- private boolean isEligibleForReplyAdjustment(NotificationEntry entry) {
- if (!Process.myUserHandle().equals(entry.getSbn().getUser())) {
- return false;
- }
- String pkg = entry.getSbn().getPackageName();
- if (TextUtils.isEmpty(pkg) || pkg.equals("android")) {
- return false;
- }
- // For now, we are only interested in messages.
- if (!entry.isMessaging()) {
- return false;
- }
- // Does not make sense to provide suggested replies if it is not something that can be
- // replied.
- if (!entry.hasInlineReply()) {
- return false;
- }
- return true;
- }
-
- /** Returns the text most salient for action extraction in a notification. */
- private List extractMessages(Notification notification) {
- Parcelable[] messages = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
- if (messages == null || messages.length == 0) {
- return Collections.singletonList(new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText(notification.extras.getCharSequence(Notification.EXTRA_TEXT))
- .build());
- }
- Person localUser = notification.extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
- Deque extractMessages = new ArrayDeque<>();
- for (int i = messages.length - 1; i >= 0; i--) {
- Notification.MessagingStyle.Message message =
- Notification.MessagingStyle.Message.getMessageFromBundle((Bundle) messages[i]);
- if (message == null) {
- continue;
- }
- // As per the javadoc of Notification.addMessage, null means local user.
- Person senderPerson = message.getSenderPerson();
- if (senderPerson == null) {
- senderPerson = localUser;
- }
- Person author = localUser != null && arePersonsEqual(localUser, senderPerson)
- ? ConversationActions.Message.PERSON_USER_SELF : senderPerson;
- extractMessages.push(new ConversationActions.Message.Builder(author)
- .setText(message.getText())
- .setReferenceTime(
- ZonedDateTime.ofInstant(Instant.ofEpochMilli(message.getTimestamp()),
- ZoneOffset.systemDefault()))
- .build());
- if (extractMessages.size() >= mSettings.mMaxMessagesToExtract) {
- break;
- }
- }
- return new ArrayList<>(extractMessages);
- }
-
- private TextClassifier getTextClassifier() {
- return mTextClassificationManager.getTextClassifier();
- }
-
- private static boolean arePersonsEqual(Person left, Person right) {
- return Objects.equals(left.getKey(), right.getKey())
- && Objects.equals(left.getName(), right.getName())
- && Objects.equals(left.getUri(), right.getUri());
- }
-
- static class SmartSuggestions {
- public final ArrayList replies;
- public final ArrayList actions;
-
- SmartSuggestions(
- ArrayList replies, ArrayList actions) {
- this.replies = replies;
- this.actions = actions;
- }
- }
-
- private static class Session {
- public final String resultId;
- public final Map repliesScores;
-
- Session(String resultId, Map repliesScores) {
- this.resultId = resultId;
- this.repliesScores = repliesScores;
- }
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmsHelper.java
deleted file mode 100644
index 07be0b8e5aa9d..0000000000000
--- a/packages/ExtServices/src/android/ext/services/notification/SmsHelper.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.ext.services.notification;
-
-import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL;
-
-import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.util.Log;
-
-import com.android.internal.telephony.SmsApplication;
-
-/**
- * A helper class for storing and retrieving the default SMS application.
- */
-public class SmsHelper {
- private static final String TAG = "SmsHelper";
-
- private final Context mContext;
- private ComponentName mDefaultSmsApplication;
- private BroadcastReceiver mBroadcastReceiver;
-
- SmsHelper(Context context) {
- mContext = context.getApplicationContext();
- }
-
- void initialize() {
- if (mBroadcastReceiver == null) {
- mDefaultSmsApplication = SmsApplication.getDefaultSmsApplication(mContext, false);
- mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) {
- mDefaultSmsApplication =
- SmsApplication.getDefaultSmsApplication(mContext, false);
- } else {
- Log.w(TAG, "Unknown broadcast received: " + intent.getAction());
- }
- }
- };
- mContext.registerReceiver(
- mBroadcastReceiver,
- new IntentFilter(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL));
- }
- }
-
- void destroy() {
- if (mBroadcastReceiver != null) {
- mContext.unregisterReceiver(mBroadcastReceiver);
- mBroadcastReceiver = null;
- }
- }
-
- @Nullable
- public ComponentName getDefaultSmsApplication() {
- return mDefaultSmsApplication;
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java b/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java
deleted file mode 100644
index 9d7a5689dcd1a..0000000000000
--- a/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java
+++ /dev/null
@@ -1,199 +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.ext.services.resolver;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Environment;
-import android.os.IBinder;
-import android.os.storage.StorageManager;
-import android.service.resolver.ResolverRankerService;
-import android.service.resolver.ResolverTarget;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A Logistic Regression based {@link android.service.resolver.ResolverRankerService}, to be used
- * in {@link ResolverComparator}.
- */
-public final class LRResolverRankerService extends ResolverRankerService {
- private static final String TAG = "LRResolverRankerService";
-
- private static final boolean DEBUG = false;
-
- private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
- private static final String BIAS_PREF_KEY = "bias";
- private static final String VERSION_PREF_KEY = "version";
-
- private static final String LAUNCH_SCORE = "launch";
- private static final String TIME_SPENT_SCORE = "timeSpent";
- private static final String RECENCY_SCORE = "recency";
- private static final String CHOOSER_SCORE = "chooser";
-
- // parameters for a pre-trained model, to initialize the app ranker. When updating the
- // pre-trained model, please update these params, as well as initModel().
- private static final int CURRENT_VERSION = 1;
- private static final float LEARNING_RATE = 0.0001f;
- private static final float REGULARIZER_PARAM = 0.0001f;
-
- private SharedPreferences mParamSharedPref;
- private ArrayMap mFeatureWeights;
- private float mBias;
-
- @Override
- public IBinder onBind(Intent intent) {
- initModel();
- return super.onBind(intent);
- }
-
- @Override
- public void onPredictSharingProbabilities(List targets) {
- final int size = targets.size();
- for (int i = 0; i < size; ++i) {
- ResolverTarget target = targets.get(i);
- ArrayMap features = getFeatures(target);
- target.setSelectProbability(predict(features));
- }
- }
-
- @Override
- public void onTrainRankingModel(List targets, int selectedPosition) {
- final int size = targets.size();
- if (selectedPosition < 0 || selectedPosition >= size) {
- if (DEBUG) {
- Log.d(TAG, "Invalid Position of Selected App " + selectedPosition);
- }
- return;
- }
- final ArrayMap positive = getFeatures(targets.get(selectedPosition));
- final float positiveProbability = targets.get(selectedPosition).getSelectProbability();
- final int targetSize = targets.size();
- for (int i = 0; i < targetSize; ++i) {
- if (i == selectedPosition) {
- continue;
- }
- final ArrayMap negative = getFeatures(targets.get(i));
- final float negativeProbability = targets.get(i).getSelectProbability();
- if (negativeProbability > positiveProbability) {
- update(negative, negativeProbability, false);
- update(positive, positiveProbability, true);
- }
- }
- commitUpdate();
- }
-
- private void initModel() {
- mParamSharedPref = getParamSharedPref();
- mFeatureWeights = new ArrayMap<>(4);
- if (mParamSharedPref == null ||
- mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) {
- // Initializing the app ranker to a pre-trained model. When updating the pre-trained
- // model, please increment CURRENT_VERSION, and update LEARNING_RATE and
- // REGULARIZER_PARAM.
- mBias = -1.6568f;
- mFeatureWeights.put(LAUNCH_SCORE, 2.5543f);
- mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f);
- mFeatureWeights.put(RECENCY_SCORE, 0.269f);
- mFeatureWeights.put(CHOOSER_SCORE, 4.2222f);
- } else {
- mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
- mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f));
- mFeatureWeights.put(
- TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f));
- mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f));
- mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f));
- }
- }
-
- private ArrayMap getFeatures(ResolverTarget target) {
- ArrayMap features = new ArrayMap<>(4);
- features.put(RECENCY_SCORE, target.getRecencyScore());
- features.put(TIME_SPENT_SCORE, target.getTimeSpentScore());
- features.put(LAUNCH_SCORE, target.getLaunchScore());
- features.put(CHOOSER_SCORE, target.getChooserScore());
- return features;
- }
-
- private float predict(ArrayMap target) {
- if (target == null) {
- return 0.0f;
- }
- final int featureSize = target.size();
- float sum = 0.0f;
- for (int i = 0; i < featureSize; i++) {
- String featureName = target.keyAt(i);
- float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
- sum += weight * target.valueAt(i);
- }
- return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
- }
-
- private void update(ArrayMap target, float predict, boolean isSelected) {
- if (target == null) {
- return;
- }
- final int featureSize = target.size();
- float error = isSelected ? 1.0f - predict : -predict;
- for (int i = 0; i < featureSize; i++) {
- String featureName = target.keyAt(i);
- float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f);
- mBias += LEARNING_RATE * error;
- currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight +
- LEARNING_RATE * error * target.valueAt(i);
- mFeatureWeights.put(featureName, currentWeight);
- }
- if (DEBUG) {
- Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias);
- }
- }
-
- private void commitUpdate() {
- try {
- SharedPreferences.Editor editor = mParamSharedPref.edit();
- editor.putFloat(BIAS_PREF_KEY, mBias);
- final int size = mFeatureWeights.size();
- for (int i = 0; i < size; i++) {
- editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
- }
- editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION);
- editor.apply();
- } catch (Exception e) {
- Log.e(TAG, "Failed to commit update" + e);
- }
- }
-
- private SharedPreferences getParamSharedPref() {
- // The package info in the context isn't initialized in the way it is for normal apps,
- // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
- // build the path manually below using the same policy that appears in ContextImpl.
- if (DEBUG) {
- Log.d(TAG, "Context Package Name: " + getPackageName());
- }
- final File prefsFile = new File(new File(
- Environment.getDataUserCePackageDirectory(
- StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()),
- "shared_prefs"),
- PARAM_SHARED_PREF_NAME + ".xml");
- return getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
- }
-}
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
deleted file mode 100644
index 81a63dd80a638..0000000000000
--- a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
+++ /dev/null
@@ -1,37 +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.ext.services.sms;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.database.CursorWindow;
-import android.os.Bundle;
-import android.service.sms.FinancialSmsService;
-
-/**
- * Service to provide financial apps access to sms messages.
- */
-public class FinancialSmsServiceImpl extends FinancialSmsService {
-
- private static final String TAG = "FinancialSmsServiceImpl";
- private static final String KEY_COLUMN_NAMES = "column_names";
-
- @Nullable
- @Override
- public CursorWindow onGetSmsMessages(@NonNull Bundle params) {
- return null;
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java b/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java
deleted file mode 100644
index 862f50b2b627c..0000000000000
--- a/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java
+++ /dev/null
@@ -1,143 +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.ext.services.storage;
-
-import android.app.usage.CacheQuotaHint;
-import android.app.usage.CacheQuotaService;
-import android.os.Environment;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.util.ArrayMap;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-/**
- * CacheQuotaServiceImpl implements the CacheQuotaService with a strategy for populating the quota
- * of {@link CacheQuotaHint}.
- */
-public class CacheQuotaServiceImpl extends CacheQuotaService {
- private static final double CACHE_RESERVE_RATIO = 0.15;
-
- @Override
- public List onComputeCacheQuotaHints(List requests) {
- ArrayMap> byUuid = new ArrayMap<>();
- final int requestCount = requests.size();
- for (int i = 0; i < requestCount; i++) {
- CacheQuotaHint request = requests.get(i);
- String uuid = request.getVolumeUuid();
- List listForUuid = byUuid.get(uuid);
- if (listForUuid == null) {
- listForUuid = new ArrayList<>();
- byUuid.put(uuid, listForUuid);
- }
- listForUuid.add(request);
- }
-
- List processed = new ArrayList<>();
- byUuid.entrySet().forEach(
- requestListEntry -> {
- // Collapse all usage stats to the same uid.
- Map> byUid = requestListEntry.getValue()
- .stream()
- .collect(Collectors.groupingBy(CacheQuotaHint::getUid));
- byUid.values().forEach(uidGroupedList -> {
- int size = uidGroupedList.size();
- if (size < 2) {
- return;
- }
- CacheQuotaHint first = uidGroupedList.get(0);
- for (int i = 1; i < size; i++) {
- /* Note: We can't use the UsageStats built-in addition function because
- UIDs may span multiple packages and usage stats adding has
- matching package names as a precondition. */
- first.getUsageStats().mTotalTimeInForeground +=
- uidGroupedList.get(i).getUsageStats().mTotalTimeInForeground;
- }
- });
-
- // Because the foreground stats have been added to the first element, we need
- // a list of only the first values (which contain the merged foreground time).
- List flattenedRequests =
- byUid.values()
- .stream()
- .map(entryList -> entryList.get(0))
- .filter(entry -> entry.getUsageStats().mTotalTimeInForeground != 0)
- .sorted(sCacheQuotaRequestComparator)
- .collect(Collectors.toList());
-
- // Because the elements are sorted, we can use the index to also be the sorted
- // index for cache quota calculation.
- double sum = getSumOfFairShares(flattenedRequests.size());
- String uuid = requestListEntry.getKey();
- long reservedSize = getReservedCacheSize(uuid);
- for (int count = 0; count < flattenedRequests.size(); count++) {
- double share = getFairShareForPosition(count) / sum;
- CacheQuotaHint entry = flattenedRequests.get(count);
- CacheQuotaHint.Builder builder = new CacheQuotaHint.Builder(entry);
- builder.setQuota(Math.round(share * reservedSize));
- processed.add(builder.build());
- }
- }
- );
-
- return processed.stream()
- .filter(request -> request.getQuota() > 0).collect(Collectors.toList());
- }
-
- private double getFairShareForPosition(int position) {
- double value = 1.0 / Math.log(position + 3) - 0.285;
- return (value > 0.01) ? value : 0.01;
- }
-
- private double getSumOfFairShares(int size) {
- double sum = 0;
- for (int i = 0; i < size; i++) {
- sum += getFairShareForPosition(i);
- }
- return sum;
- }
-
- private long getReservedCacheSize(String uuid) {
- // TODO: Revisit the cache size after running more storage tests.
- // TODO: Figure out how to ensure ExtServices has the permissions to call
- // StorageStatsManager, because this is ignoring the cache...
- StorageManager storageManager = getSystemService(StorageManager.class);
- long freeBytes = 0;
- if (uuid == StorageManager.UUID_PRIVATE_INTERNAL) { // regular equals because of null
- freeBytes = Environment.getDataDirectory().getUsableSpace();
- } else {
- final VolumeInfo vol = storageManager.findVolumeByUuid(uuid);
- freeBytes = vol.getPath().getUsableSpace();
- }
- return Math.round(freeBytes * CACHE_RESERVE_RATIO);
- }
-
- // Compares based upon foreground time.
- private static Comparator sCacheQuotaRequestComparator =
- new Comparator() {
- @Override
- public int compare(CacheQuotaHint o, CacheQuotaHint t1) {
- long x = t1.getUsageStats().getTotalTimeInForeground();
- long y = o.getUsageStats().getTotalTimeInForeground();
- return (x < y) ? -1 : ((x == y) ? 0 : 1);
- }
- };
-}
diff --git a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java b/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java
deleted file mode 100644
index 670b419a8aa31..0000000000000
--- a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java
+++ /dev/null
@@ -1,125 +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.ext.services.watchdog;
-
-import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.provider.DeviceConfig;
-import android.service.watchdog.ExplicitHealthCheckService;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Routes explicit health check requests to the appropriate {@link ExplicitHealthChecker}.
- */
-public final class ExplicitHealthCheckServiceImpl extends ExplicitHealthCheckService {
- private static final String TAG = "ExplicitHealthCheckServiceImpl";
- // TODO: Add build dependency on NetworkStack stable AIDL so we can stop hard coding class name
- private static final String NETWORK_STACK_CONNECTOR_CLASS =
- "android.net.INetworkStackConnector";
- public static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
- "watchdog_request_timeout_millis";
- public static final long DEFAULT_REQUEST_TIMEOUT_MILLIS =
- TimeUnit.HOURS.toMillis(1);
- // Modified only #onCreate, using concurrent collection to ensure thread visibility
- private final Map mSupportedCheckers = new ConcurrentHashMap<>();
-
- @Override
- public void onCreate() {
- super.onCreate();
- initHealthCheckers();
- }
-
- @Override
- public void onRequestHealthCheck(String packageName) {
- ExplicitHealthChecker checker = mSupportedCheckers.get(packageName);
- if (checker != null) {
- checker.request();
- } else {
- Log.w(TAG, "Ignoring request for explicit health check for unsupported package "
- + packageName);
- }
- }
-
- @Override
- public void onCancelHealthCheck(String packageName) {
- ExplicitHealthChecker checker = mSupportedCheckers.get(packageName);
- if (checker != null) {
- checker.cancel();
- } else {
- Log.w(TAG, "Ignoring request to cancel explicit health check for unsupported package "
- + packageName);
- }
- }
-
- @Override
- public List onGetSupportedPackages() {
- List packages = new ArrayList<>();
- long requestTimeoutMillis = DeviceConfig.getLong(
- DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
- DEFAULT_REQUEST_TIMEOUT_MILLIS);
- if (requestTimeoutMillis <= 0) {
- requestTimeoutMillis = DEFAULT_REQUEST_TIMEOUT_MILLIS;
- }
- for (ExplicitHealthChecker checker : mSupportedCheckers.values()) {
- PackageConfig pkg = new PackageConfig(checker.getSupportedPackageName(),
- requestTimeoutMillis);
- packages.add(pkg);
- }
- return packages;
- }
-
- @Override
- public List onGetRequestedPackages() {
- List packages = new ArrayList<>();
- Iterator it = mSupportedCheckers.values().iterator();
- // Could potentially race, where we read a checker#isPending and it changes before we
- // return list. However, if it races and it is in the list, the caller might call #cancel
- // which would fail, but that is fine. If it races and it ends up *not* in the list, it was
- // already cancelled, so there's no need for the caller to cancel it
- while (it.hasNext()) {
- ExplicitHealthChecker checker = it.next();
- if (checker.isPending()) {
- packages.add(checker.getSupportedPackageName());
- }
- }
- return packages;
- }
-
- private void initHealthCheckers() {
- Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
- ComponentName comp = intent.resolveSystemService(getPackageManager(), 0);
- if (comp != null) {
- String networkStackPackageName = comp.getPackageName();
- mSupportedCheckers.put(networkStackPackageName,
- new NetworkChecker(this, networkStackPackageName));
- } else {
- // On Go devices, or any device that does not ship the network stack module.
- // The network stack will live in system_server process, so no need to monitor.
- Log.i(TAG, "Network stack module not found");
- }
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthChecker.java b/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthChecker.java
deleted file mode 100644
index a982d52e59af7..0000000000000
--- a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthChecker.java
+++ /dev/null
@@ -1,44 +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.ext.services.watchdog;
-
-/**
- * A type of explicit health check that can be performed on a device, e.g network health check
- */
-interface ExplicitHealthChecker {
- /**
- * Requests a checker to listen to explicit health checks for {@link #getPackageName}.
- * {@link #isPending} will now return {@code true}.
- */
- void request();
-
- /**
- * Cancels a pending explicit health check request for {@link #getPackageName}.
- * {@link #isPending} will now return {@code false}.
- */
- void cancel();
-
- /**
- * Returns {@code true} if a request is pending, {@code false} otherwise.
- */
- boolean isPending();
-
- /**
- * Returns the name of the package this checker can make requests for.
- */
- String getSupportedPackageName();
-}
diff --git a/packages/ExtServices/src/android/ext/services/watchdog/NetworkChecker.java b/packages/ExtServices/src/android/ext/services/watchdog/NetworkChecker.java
deleted file mode 100644
index 5722e096dff05..0000000000000
--- a/packages/ExtServices/src/android/ext/services/watchdog/NetworkChecker.java
+++ /dev/null
@@ -1,92 +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.ext.services.watchdog;
-
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.service.watchdog.ExplicitHealthCheckService;
-
-import com.android.internal.annotations.GuardedBy;
-
-/**
- * Observes the network stack via the ConnectivityManager.
- */
-final class NetworkChecker extends ConnectivityManager.NetworkCallback
- implements ExplicitHealthChecker {
- private static final String TAG = "NetworkChecker";
-
- private final Object mLock = new Object();
- private final ExplicitHealthCheckService mService;
- private final String mPackageName;
- @GuardedBy("mLock")
- private boolean mIsPending;
-
- NetworkChecker(ExplicitHealthCheckService service, String packageName) {
- mService = service;
- mPackageName = packageName;
- }
-
- @Override
- public void request() {
- synchronized (mLock) {
- if (mIsPending) {
- return;
- }
- mService.getSystemService(ConnectivityManager.class).registerNetworkCallback(
- new NetworkRequest.Builder().build(), this);
- mIsPending = true;
- }
- }
-
- @Override
- public void cancel() {
- synchronized (mLock) {
- if (!mIsPending) {
- return;
- }
- mService.getSystemService(ConnectivityManager.class).unregisterNetworkCallback(this);
- mIsPending = false;
- }
- }
-
- @Override
- public boolean isPending() {
- synchronized (mLock) {
- return mIsPending;
- }
- }
-
- @Override
- public String getSupportedPackageName() {
- return mPackageName;
- }
-
- // TODO(b/120598832): Also monitor NetworkCallback#onAvailable to see if we have any
- // available networks that may be unusable. This could be additional signal to our heuristics
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities capabilities) {
- synchronized (mLock) {
- if (mIsPending
- && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
- mService.notifyHealthCheckPassed(mPackageName);
- cancel();
- }
- }
- }
-}
diff --git a/packages/ExtServices/tests/Android.bp b/packages/ExtServices/tests/Android.bp
deleted file mode 100644
index ae60d4e32c041..0000000000000
--- a/packages/ExtServices/tests/Android.bp
+++ /dev/null
@@ -1,27 +0,0 @@
-android_test {
- name: "ExtServicesUnitTests",
-
- // Include all test java files.
- srcs: ["src/**/*.java"],
-
- // We only want this apk build for tests.
- certificate: "platform",
-
- libs: [
- "android.test.runner",
- "android.test.base",
- ],
-
- static_libs: [
- "ExtServices-core",
- "androidx.test.rules",
- "compatibility-device-util-axt",
- "mockito-target-minus-junit4",
- "androidx.test.espresso.core",
- "truth-prebuilt",
- "testables",
- "testng",
- ],
-
- platform_apis: true,
-}
diff --git a/packages/ExtServices/tests/AndroidManifest.xml b/packages/ExtServices/tests/AndroidManifest.xml
deleted file mode 100644
index a08a10e5b5dfb..0000000000000
--- a/packages/ExtServices/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/packages/ExtServices/tests/AndroidTest.xml b/packages/ExtServices/tests/AndroidTest.xml
deleted file mode 100644
index cd26ebc407c14..0000000000000
--- a/packages/ExtServices/tests/AndroidTest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java
deleted file mode 100644
index 6fda4c73792b2..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java
+++ /dev/null
@@ -1,118 +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.ext.services.autofill;
-
-import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_EXACT_MATCH;
-import static android.view.autofill.AutofillValue.forText;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.os.Bundle;
-import android.view.autofill.AutofillValue;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Contains the base tests that does not rely on the specific algorithm implementation.
- */
-public class AutofillFieldClassificationServiceImplTest {
-
- private final AutofillFieldClassificationServiceImpl mService =
- new AutofillFieldClassificationServiceImpl();
-
- @Test
- public void testOnCalculateScores_nullActualValues() {
- assertThat(mService.onCalculateScores(null, null, null, null, null, null, null)).isNull();
- }
-
- @Test
- public void testOnCalculateScores_emptyActualValues() {
- assertThat(mService.onCalculateScores(Collections.emptyList(), Arrays.asList("whatever"),
- null, null, null, null, null)).isNull();
- }
-
- @Test
- public void testOnCalculateScores_nullUserDataValues() {
- assertThat(mService.onCalculateScores(Arrays.asList(AutofillValue.forText("whatever")),
- null, null, null, null, null, null)).isNull();
- }
-
- @Test
- public void testOnCalculateScores_emptyUserDataValues() {
- assertThat(mService.onCalculateScores(Arrays.asList(AutofillValue.forText("whatever")),
- Collections.emptyList(), null, null, null, null, null))
- .isNull();
- }
-
- @Test
- public void testCalculateScores() {
- final List actualValues = Arrays.asList(forText("A"), forText("b"),
- forText("dude"));
- final List userDataValues = Arrays.asList("a", "b", "B", "ab", "c", "dude",
- "sweet_dude", "dude_sweet");
- final List categoryIds = Arrays.asList("cat", "cat", "cat", "cat", "cat", "last4",
- "last4", "last4");
- final HashMap algorithms = new HashMap<>(1);
- algorithms.put("last4", REQUIRED_ALGORITHM_EXACT_MATCH);
-
- final Bundle last4Bundle = new Bundle();
- last4Bundle.putInt("suffix", 4);
-
- final HashMap args = new HashMap<>(1);
- args.put("last4", last4Bundle);
-
- final float[][] expectedScores = new float[][] {
- new float[] { 1F, 0F, 0F, 0.5F, 0F, 0F, 0F, 0F },
- new float[] { 0F, 1F, 1F, 0.5F, 0F, 0F, 0F, 0F },
- new float[] { 0F, 0F, 0F, 0F , 0F, 1F, 1F, 0F }
- };
- final float[][] actualScores = mService.onCalculateScores(actualValues, userDataValues,
- categoryIds, null, null, algorithms, args);
-
- // Unfortunately, Truth does not have an easy way to compare float matrices and show useful
- // messages in case of error, so we need to check.
- assertWithMessage("actual=%s, expected=%s", toString(actualScores),
- toString(expectedScores)).that(actualScores.length).isEqualTo(3);
- for (int i = 0; i < 3; i++) {
- assertWithMessage("actual=%s, expected=%s", toString(actualScores),
- toString(expectedScores)).that(actualScores[i].length).isEqualTo(8);
- }
-
- for (int i = 0; i < actualScores.length; i++) {
- final float[] line = actualScores[i];
- for (int j = 0; j < line.length; j++) {
- float cell = line[j];
- assertWithMessage("wrong score at [%s, %s]", i, j).that(cell).isWithin(0.01F)
- .of(expectedScores[i][j]);
- }
- }
- }
-
- public static String toString(float[][] matrix) {
- final StringBuilder string = new StringBuilder("[ ");
- for (int i = 0; i < matrix.length; i++) {
- string.append(Arrays.toString(matrix[i])).append(" ");
- }
- return string.append(" ]").toString();
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java
deleted file mode 100644
index 3d754f7fa9d92..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java
+++ /dev/null
@@ -1,85 +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.ext.services.autofill;
-
-import static android.ext.services.autofill.EditDistanceScorer.calculateScore;
-import static android.ext.services.autofill.EditDistanceScorer.editDistance;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.view.autofill.AutofillValue;
-
-import org.junit.Test;
-
-public class EditDistanceScorerTest {
-
- @Test
- public void testCalculateScore_nullValue() {
- assertFloat(calculateScore(null, "D'OH!"), 0);
- }
-
- @Test
- public void testCalculateScore_nonTextValue() {
- assertFloat(calculateScore(AutofillValue.forToggle(true), "D'OH!"), 0);
- }
-
- @Test
- public void testCalculateScore_nullUserData() {
- assertFloat(calculateScore(AutofillValue.forText("D'OH!"), null), 0);
- }
-
- @Test
- public void testCalculateScore_fullMatch() {
- assertFloat(calculateScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1);
- assertFloat(calculateScore(AutofillValue.forText(""), ""), 1);
- }
-
- @Test
- public void testCalculateScore_fullMatchMixedCase() {
- assertFloat(calculateScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1);
- }
-
- @Test
- public void testCalculateScore_mismatchDifferentSizes() {
- assertFloat(calculateScore(AutofillValue.forText("X"), "Xy"), 0.50F);
- assertFloat(calculateScore(AutofillValue.forText("Xy"), "X"), 0.50F);
- assertFloat(calculateScore(AutofillValue.forText("One"), "MoreThanOne"), 0.27F);
- assertFloat(calculateScore(AutofillValue.forText("MoreThanOne"), "One"), 0.27F);
- assertFloat(calculateScore(AutofillValue.forText("1600 Amphitheatre Parkway"),
- "1600 Amphitheatre Pkwy"), 0.88F);
- assertFloat(calculateScore(AutofillValue.forText("1600 Amphitheatre Pkwy"),
- "1600 Amphitheatre Parkway"), 0.88F);
- }
-
- @Test
- public void testCalculateScore_partialMatch() {
- assertFloat(calculateScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F);
- assertFloat(calculateScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F);
- assertFloat(calculateScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F);
- assertFloat(calculateScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F);
- assertFloat(calculateScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F);
- assertFloat(calculateScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F);
- }
-
- @Test
- public void testEditDistance_maxDistance() {
- assertFloat(editDistance("testing", "b", 4), Integer.MAX_VALUE);
- }
-
- public static void assertFloat(float actualValue, float expectedValue) {
- assertThat(actualValue).isWithin(0.01F).of(expectedValue);
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java
deleted file mode 100644
index bf5e1609fa8bd..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java
+++ /dev/null
@@ -1,98 +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.ext.services.autofill;
-
-import static android.ext.services.autofill.ExactMatch.calculateScore;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.os.Bundle;
-import android.view.autofill.AutofillValue;
-
-import org.junit.Test;
-
-public class ExactMatchTest {
-
- private Bundle last4Bundle() {
- final Bundle bundle = new Bundle();
- bundle.putInt("suffix", 4);
- return bundle;
- }
-
- @Test
- public void testCalculateScore_nullValue() {
- assertFloat(calculateScore(null, "TEST", null), 0);
- }
-
- @Test
- public void testCalculateScore_nonTextValue() {
- assertFloat(calculateScore(AutofillValue.forToggle(true), "TEST", null), 0);
- }
-
- @Test
- public void testCalculateScore_nullUserData() {
- assertFloat(calculateScore(AutofillValue.forText("TEST"), null, null), 0);
- }
-
- @Test
- public void testCalculateScore_succeedMatchMixedCases_last4() {
- final Bundle last4 = last4Bundle();
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "1234 test", last4), 1);
- assertFloat(calculateScore(AutofillValue.forText("test"), "1234 TEST", last4), 1);
- }
-
- @Test
- public void testCalculateScore_mismatchDifferentSizes_last4() {
- final Bundle last4 = last4Bundle();
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST1", last4), 0);
- assertFloat(calculateScore(AutofillValue.forText(""), "TEST", last4), 0);
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "", last4), 0);
- }
-
- @Test
- public void testCalculateScore_match() {
- final Bundle last4 = last4Bundle();
- assertFloat(calculateScore(AutofillValue.forText("1234 1234 1234 1234"),
- "xxxx xxxx xxxx 1234", last4), 1);
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST", null), 1);
- assertFloat(calculateScore(AutofillValue.forText("TEST 1234"), "1234", last4), 1);
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "test", null), 1);
- }
-
- @Test
- public void testCalculateScore_badBundle() {
- final Bundle bundle = new Bundle();
- bundle.putInt("suffix", -2);
- assertThrows(IllegalArgumentException.class, () -> calculateScore(
- AutofillValue.forText("TEST"), "TEST", bundle));
-
- final Bundle largeBundle = new Bundle();
- largeBundle.putInt("suffix", 10);
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST", largeBundle), 1);
-
- final Bundle stringBundle = new Bundle();
- stringBundle.putString("suffix", "value");
- assertThrows(IllegalArgumentException.class, () -> calculateScore(
- AutofillValue.forText("TEST"), "TEST", stringBundle));
-
- }
-
- public static void assertFloat(float actualValue, float expectedValue) {
- assertThat(actualValue).isWithin(0.01F).of(expectedValue);
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java
deleted file mode 100644
index a87d57cf872b0..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java
+++ /dev/null
@@ -1,160 +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.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AlarmManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.PendingIntent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.os.Build;
-import android.os.Process;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import android.testing.TestableContext;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-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)
-public class AgingHelperTest {
- private String mPkg = "pkg";
- private int mUid = 2018;
-
- @Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getTargetContext(), null);
-
- @Mock
- private NotificationCategorizer mCategorizer;
- @Mock
- private AlarmManager mAlarmManager;
- @Mock
- private IPackageManager mPackageManager;
- @Mock
- private AgingHelper.Callback mCallback;
- @Mock
- private SmsHelper mSmsHelper;
-
- private AgingHelper mAgingHelper;
-
- private StatusBarNotification generateSbn(String channelId) {
- Notification n = new Notification.Builder(mContext, channelId)
- .setContentTitle("foo")
- .build();
-
- return new StatusBarNotification(mPkg, mPkg, 0, "tag", mUid, mUid, n,
- UserHandle.SYSTEM, null, 0);
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mPkg = mContext.getPackageName();
- mUid = Process.myUid();
-
- ApplicationInfo info = mock(ApplicationInfo.class);
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
- .thenReturn(info);
- info.targetSdkVersion = Build.VERSION_CODES.P;
-
- mContext.addMockSystemService(AlarmManager.class, mAlarmManager);
-
- mAgingHelper = new AgingHelper(mContext, mCategorizer, mCallback);
- }
-
- @Test
- public void testNoSnoozingOnPost() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
-
-
- mAgingHelper.onNotificationPosted(entry);
- verify(mAlarmManager, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
- }
-
- @Test
- public void testPostResetsSnooze() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
-
-
- mAgingHelper.onNotificationPosted(entry);
- verify(mAlarmManager, times(1)).cancel(any(PendingIntent.class));
- }
-
- @Test
- public void testSnoozingOnSeen() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- entry.setSeen();
- when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE);
-
- mAgingHelper.onNotificationSeen(entry);
- verify(mAlarmManager, times(1)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
- }
-
- @Test
- public void testNoSnoozingOnSeenUserLocked() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE);
-
- mAgingHelper.onNotificationSeen(entry);
- verify(mAlarmManager, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
- }
-
- @Test
- public void testNoSnoozingOnSeenAlreadyLow() {
- NotificationEntry entry = mock(NotificationEntry.class);
- when(entry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_HIGH));
- when(entry.getImportance()).thenReturn(IMPORTANCE_MIN);
-
- mAgingHelper.onNotificationSeen(entry);
- verify(mAlarmManager, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
deleted file mode 100644
index 5c877de238168..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
+++ /dev/null
@@ -1,263 +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.ext.services.notification;
-
-import static android.ext.services.notification.AssistantSettings.DEFAULT_MAX_SUGGESTIONS;
-import static android.provider.DeviceConfig.setProperty;
-
-import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.ContentResolver;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.support.test.uiautomator.UiDevice;
-import android.testing.TestableContext;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-
-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;
-
-import java.io.IOException;
-
-@RunWith(AndroidJUnit4.class)
-public class AssistantSettingsTest {
- private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
- "device_config delete " + DeviceConfig.NAMESPACE_SYSTEMUI;
-
- private static final int USER_ID = 5;
-
- @Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getContext(), null);
-
- @Mock Runnable mOnUpdateRunnable;
-
- private ContentResolver mResolver;
- private AssistantSettings mAssistantSettings;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mResolver = mContext.getContentResolver();
- Handler handler = new Handler(Looper.getMainLooper());
-
- // To bypass real calls to global settings values, set the Settings values here.
- Settings.Global.putFloat(mResolver,
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, 0.8f);
- Settings.Global.putInt(mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, 2);
- Settings.Secure.putInt(mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
-
- mAssistantSettings = AssistantSettings.createForTesting(
- handler, mResolver, USER_ID, mOnUpdateRunnable);
- }
-
- @After
- public void tearDown() throws IOException {
- clearDeviceConfig();
- }
-
- @Test
- public void testGenerateRepliesDisabled() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
- "false",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertFalse(mAssistantSettings.mGenerateReplies);
- }
-
- @Test
- public void testGenerateRepliesEnabled() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
- "true",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertTrue(mAssistantSettings.mGenerateReplies);
- }
-
- @Test
- public void testGenerateRepliesNullFlag() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
- "false",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertFalse(mAssistantSettings.mGenerateReplies);
-
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
- null,
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- // Go back to the default value.
- assertTrue(mAssistantSettings.mGenerateReplies);
- }
-
- @Test
- public void testGenerateActionsDisabled() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
- "false",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertFalse(mAssistantSettings.mGenerateActions);
- }
-
- @Test
- public void testGenerateActionsEnabled() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
- "true",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertTrue(mAssistantSettings.mGenerateActions);
- }
-
- @Test
- public void testGenerateActionsNullFlag() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
- "false",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertFalse(mAssistantSettings.mGenerateActions);
-
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
- null,
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- // Go back to the default value.
- assertTrue(mAssistantSettings.mGenerateActions);
- }
-
- @Test
- public void testMaxMessagesToExtract() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT,
- "10",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertEquals(10, mAssistantSettings.mMaxMessagesToExtract);
- }
-
- @Test
- public void testMaxSuggestions() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS,
- "5",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertEquals(5, mAssistantSettings.mMaxSuggestions);
- }
-
- @Test
- public void testMaxSuggestionsEmpty() {
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertEquals(DEFAULT_MAX_SUGGESTIONS, mAssistantSettings.mMaxSuggestions);
- }
-
- @Test
- public void testStreakLimit() {
- verify(mOnUpdateRunnable, never()).run();
-
- // Update settings value.
- int newStreakLimit = 4;
- Settings.Global.putInt(mResolver,
- Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, newStreakLimit);
-
- // Notify for the settings value we updated.
- mAssistantSettings.onChange(false, Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_STREAK_LIMIT));
-
- assertEquals(newStreakLimit, mAssistantSettings.mStreakLimit);
- verify(mOnUpdateRunnable).run();
- }
-
- @Test
- public void testDismissToViewRatioLimit() {
- verify(mOnUpdateRunnable, never()).run();
-
- // Update settings value.
- float newDismissToViewRatioLimit = 3f;
- Settings.Global.putFloat(mResolver,
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
- newDismissToViewRatioLimit);
-
- // Notify for the settings value we updated.
- mAssistantSettings.onChange(false, Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT));
-
- assertEquals(newDismissToViewRatioLimit, mAssistantSettings.mDismissToViewRatioLimit, 1e-6);
- verify(mOnUpdateRunnable).run();
- }
-
- private static void clearDeviceConfig() throws IOException {
- UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " "
- + SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS);
- }
-
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
deleted file mode 100644
index 012dcc01fe6f2..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ /dev/null
@@ -1,477 +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.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Application;
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.os.Build;
-import android.os.UserHandle;
-import android.service.notification.Adjustment;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.NotificationStats;
-import android.service.notification.StatusBarNotification;
-import android.test.ServiceTestCase;
-import android.testing.TestableContext;
-import android.util.AtomicFile;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.internal.util.FastXmlSerializer;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileOutputStream;
-import java.util.ArrayList;
-
-public class AssistantTest extends ServiceTestCase {
-
- private static final String PKG1 = "pkg1";
- private static final int UID1 = 1;
- private static final NotificationChannel P1C1 =
- new NotificationChannel("one", "", IMPORTANCE_LOW);
- private static final NotificationChannel P1C2 =
- new NotificationChannel("p1c2", "", IMPORTANCE_DEFAULT);
- private static final NotificationChannel P1C3 =
- new NotificationChannel("p1c3", "", IMPORTANCE_MIN);
- private static final String PKG2 = "pkg2";
-
- private static final int UID2 = 2;
- private static final NotificationChannel P2C1 =
- new NotificationChannel("one", "", IMPORTANCE_LOW);
-
- @Mock INotificationManager mNoMan;
- @Mock AtomicFile mFile;
- @Mock IPackageManager mPackageManager;
- @Mock SmsHelper mSmsHelper;
-
- Assistant mAssistant;
- Application mApplication;
-
- @Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getContext(), null);
-
- public AssistantTest() {
- super(Assistant.class);
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- Intent startIntent =
- new Intent("android.service.notification.NotificationAssistantService");
- startIntent.setPackage("android.ext.services");
-
- mApplication = (Application) InstrumentationRegistry.getInstrumentation().
- getTargetContext().getApplicationContext();
- // Force the test to use the correct application instead of trying to use a mock application
- setApplication(mApplication);
-
- setupService();
- mAssistant = getService();
-
- // Override the AssistantSettings factory.
- mAssistant.mSettingsFactory = AssistantSettings::createForTesting;
-
- bindService(startIntent);
-
- mAssistant.mSettings.mDismissToViewRatioLimit = 0.8f;
- mAssistant.mSettings.mStreakLimit = 2;
- mAssistant.mSettings.mNewInterruptionModel = true;
- mAssistant.setNoMan(mNoMan);
- mAssistant.setFile(mFile);
- mAssistant.setPackageManager(mPackageManager);
-
- ApplicationInfo info = mock(ApplicationInfo.class);
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
- .thenReturn(info);
- info.targetSdkVersion = Build.VERSION_CODES.P;
- when(mFile.startWrite()).thenReturn(mock(FileOutputStream.class));
- }
-
- private StatusBarNotification generateSbn(String pkg, int uid, NotificationChannel channel,
- String tag, String groupKey) {
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setContentTitle("foo")
- .setGroup(groupKey)
- .build();
-
- StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 0, tag, uid, uid, n,
- UserHandle.SYSTEM, null, 0);
-
- return sbn;
- }
-
- private Ranking generateRanking(StatusBarNotification sbn, NotificationChannel channel) {
- Ranking mockRanking = mock(Ranking.class);
- when(mockRanking.getChannel()).thenReturn(channel);
- when(mockRanking.getImportance()).thenReturn(channel.getImportance());
- when(mockRanking.getKey()).thenReturn(sbn.getKey());
- when(mockRanking.getOverrideGroupKey()).thenReturn(null);
- return mockRanking;
- }
-
- private void almostBlockChannel(String pkg, int uid, NotificationChannel channel) {
- for (int i = 0; i < ChannelImpressions.DEFAULT_STREAK_LIMIT; i++) {
- dismissBadNotification(pkg, uid, channel, String.valueOf(i));
- }
- }
-
- private void dismissBadNotification(String pkg, int uid, NotificationChannel channel,
- String tag) {
- StatusBarNotification sbn = generateSbn(pkg, uid, channel, tag, null);
- mAssistant.setFakeRanking(generateRanking(sbn, channel));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.setFakeRanking(mock(Ranking.class));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
- stats.setSeen();
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
- }
-
- @Test
- public void testNoAdjustmentForInitialPost() throws Exception {
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, null, null);
-
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
- dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- ArgumentCaptor captor = ArgumentCaptor.forClass(Adjustment.class);
- verify(mNoMan, times(1)).applyEnqueuedAdjustmentFromAssistant(any(), captor.capture());
- assertEquals(sbn.getKey(), captor.getValue().getKey());
- assertEquals(Ranking.USER_SENTIMENT_NEGATIVE,
- captor.getValue().getSignals().getInt(Adjustment.KEY_USER_SENTIMENT));
- }
-
- @Test
- public void testMinCannotTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C3);
- dismissBadNotification(PKG1, UID1, P1C3, "trigger!");
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "new one!", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C3));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testGroupChildCanTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP");
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
- stats.setSeen();
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
-
- sbn = generateSbn(PKG1, UID1, P1C1, "new one!", "group");
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- ArgumentCaptor captor = ArgumentCaptor.forClass(Adjustment.class);
- verify(mNoMan, times(1)).applyEnqueuedAdjustmentFromAssistant(any(), captor.capture());
- assertEquals(sbn.getKey(), captor.getValue().getKey());
- assertEquals(Ranking.USER_SENTIMENT_NEGATIVE,
- captor.getValue().getSignals().getInt(Adjustment.KEY_USER_SENTIMENT));
- }
-
- @Test
- public void testGroupSummaryCannotTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP");
- sbn.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
- stats.setSeen();
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
-
- sbn = generateSbn(PKG1, UID1, P1C1, "new one!", "group");
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testAodCannotTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_AOD);
- stats.setSeen();
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
-
- sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testInteractedCannotTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
- stats.setSeen();
- stats.setExpanded();
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
-
- sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testAppDismissedCannotTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
- stats.setSeen();
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_APP_CANCEL);
-
- sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testAppSeparation() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
- dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
-
- StatusBarNotification sbn = generateSbn(PKG2, UID2, P2C1, "new app!", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P2C1));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testChannelSeparation() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
- dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C2, "new app!", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C2));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testReadXml() throws Exception {
- String key1 = mAssistant.getKey("pkg1", 1, "channel1");
- int streak1 = 2;
- int views1 = 5;
- int dismiss1 = 9;
-
- int streak1a = 3;
- int views1a = 10;
- int dismiss1a = 99;
- String key1a = mAssistant.getKey("pkg1", 1, "channel1a");
-
- int streak2 = 7;
- int views2 = 77;
- int dismiss2 = 777;
- String key2 = mAssistant.getKey("pkg2", 2, "channel2");
-
- String xml = ""
- + "\n"
- + "\n"
- + "\n"
- + "\n"
- + "\n";
- mAssistant.readXml(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())));
-
- ChannelImpressions c1 = mAssistant.getImpressions(key1);
- assertEquals(2, c1.getStreak());
- assertEquals(5, c1.getViews());
- assertEquals(9, c1.getDismissals());
-
- ChannelImpressions c1a = mAssistant.getImpressions(key1a);
- assertEquals(3, c1a.getStreak());
- assertEquals(10, c1a.getViews());
- assertEquals(99, c1a.getDismissals());
-
- ChannelImpressions c2 = mAssistant.getImpressions(key2);
- assertEquals(7, c2.getStreak());
- assertEquals(77, c2.getViews());
- assertEquals(777, c2.getDismissals());
- }
-
- @Test
- public void testRoundTripXml() throws Exception {
- String key1 = mAssistant.getKey("pkg1", 1, "channel1");
- ChannelImpressions ci1 = new ChannelImpressions();
- String key2 = mAssistant.getKey("pkg1", 1, "channel2");
- ChannelImpressions ci2 = new ChannelImpressions();
- for (int i = 0; i < 3; i++) {
- ci2.incrementViews();
- ci2.incrementDismissals();
- }
- ChannelImpressions ci3 = new ChannelImpressions();
- String key3 = mAssistant.getKey("pkg3", 3, "channel2");
- for (int i = 0; i < 9; i++) {
- ci3.incrementViews();
- if (i % 3 == 0) {
- ci3.incrementDismissals();
- }
- }
-
- mAssistant.insertImpressions(key1, ci1);
- mAssistant.insertImpressions(key2, ci2);
- mAssistant.insertImpressions(key3, ci3);
-
- XmlSerializer serializer = new FastXmlSerializer();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
- mAssistant.writeXml(serializer);
-
- Assistant assistant = new Assistant();
- // onCreate is not invoked, so settings won't be initialised, unless we do it here.
- assistant.mSettings = mAssistant.mSettings;
- assistant.readXml(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())));
-
- assertEquals(ci1, assistant.getImpressions(key1));
- assertEquals(ci2, assistant.getImpressions(key2));
- assertEquals(ci3, assistant.getImpressions(key3));
- }
-
- @Test
- public void testSettingsProviderUpdate() {
- // Set up channels
- String key = mAssistant.getKey("pkg1", 1, "channel1");
- ChannelImpressions ci = new ChannelImpressions();
- for (int i = 0; i < 3; i++) {
- ci.incrementViews();
- if (i % 2 == 0) {
- ci.incrementDismissals();
- }
- }
-
- mAssistant.insertImpressions(key, ci);
-
- // With default values, the blocking helper shouldn't be triggered.
- assertEquals(false, ci.shouldTriggerBlock());
-
- // Update settings values.
- mAssistant.mSettings.mDismissToViewRatioLimit = 0f;
- mAssistant.mSettings.mStreakLimit = 0;
-
- // Notify for the settings values we updated.
- mAssistant.mSettings.mOnUpdateRunnable.run();
-
- // With the new threshold, the blocking helper should be triggered.
- assertEquals(true, ci.shouldTriggerBlock());
- }
-
- @Test
- public void testTrimLiveNotifications() {
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
-
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- assertTrue(mAssistant.mLiveNotifications.containsKey(sbn.getKey()));
-
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), new NotificationStats(), 0);
-
- assertFalse(mAssistant.mLiveNotifications.containsKey(sbn.getKey()));
- }
-
- @Test
- public void testAssistantNeverIncreasesImportanceWhenSuggestingSilent() throws Exception {
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "min notif!", null);
- Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(
- new NotificationEntry(mContext, mPackageManager, sbn, P1C3, mSmsHelper),
- new ArrayList<>(),
- new ArrayList<>());
- assertEquals(IMPORTANCE_MIN, adjust.getSignals().getInt(Adjustment.KEY_IMPORTANCE));
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java
deleted file mode 100644
index 3253802bec039..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java
+++ /dev/null
@@ -1,161 +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.ext.services.notification;
-
-import static android.ext.services.notification.ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT;
-import static android.ext.services.notification.ChannelImpressions.DEFAULT_STREAK_LIMIT;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-public class ChannelImpressionsTest {
-
- @Test
- public void testNoResultNoBlock() {
- ChannelImpressions ci = new ChannelImpressions();
- assertFalse(ci.shouldTriggerBlock());
- }
-
- @Test
- public void testNoStreakNoBlock() {
- ChannelImpressions ci = new ChannelImpressions();
-
- for (int i = 0; i < DEFAULT_STREAK_LIMIT - 1; i++) {
- ci.incrementViews();
- ci.incrementDismissals();
- }
-
- assertFalse(ci.shouldTriggerBlock());
- }
-
- @Test
- public void testNoStreakNoBlock_breakStreak() {
- ChannelImpressions ci = new ChannelImpressions();
-
- for (int i = 0; i < DEFAULT_STREAK_LIMIT; i++) {
- ci.incrementViews();
- ci.incrementDismissals();
- if (i == DEFAULT_STREAK_LIMIT - 1) {
- ci.resetStreak();
- }
- }
-
- assertFalse(ci.shouldTriggerBlock());
- }
-
- @Test
- public void testStreakBlock() {
- ChannelImpressions ci = new ChannelImpressions();
-
- for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) {
- ci.incrementViews();
- ci.incrementDismissals();
- }
-
- assertTrue(ci.shouldTriggerBlock());
- }
-
- @Test
- public void testRatio_NoBlockEvenWithStreak() {
- ChannelImpressions ci = new ChannelImpressions();
-
- for (int i = 0; i < DEFAULT_STREAK_LIMIT; i++) {
- ci.incrementViews();
- ci.incrementDismissals();
- ci.incrementViews();
- }
-
- assertFalse(ci.shouldTriggerBlock());
- }
-
- @Test
- public void testAppend() {
- ChannelImpressions ci = new ChannelImpressions();
- ci.incrementViews();
- ci.incrementDismissals();
-
- ChannelImpressions ci2 = new ChannelImpressions();
- ci2.incrementViews();
- ci2.incrementDismissals();
- ci2.incrementViews();
-
- ci.append(ci2);
- assertEquals(3, ci.getViews());
- assertEquals(2, ci.getDismissals());
- assertEquals(2, ci.getStreak());
-
- assertEquals(2, ci2.getViews());
- assertEquals(1, ci2.getDismissals());
- assertEquals(1, ci2.getStreak());
-
- // no crash
- ci.append(null);
- }
-
- @Test
- public void testUpdateThresholds_streakLimitsCorrectlyApplied() {
- int updatedStreakLimit = DEFAULT_STREAK_LIMIT + 3;
- ChannelImpressions ci = new ChannelImpressions();
- ci.updateThresholds(DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT, updatedStreakLimit);
-
- for (int i = 0; i <= updatedStreakLimit; i++) {
- ci.incrementViews();
- ci.incrementDismissals();
- }
-
- ChannelImpressions ci2 = new ChannelImpressions();
- ci2.updateThresholds(DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT, updatedStreakLimit);
-
- for (int i = 0; i < updatedStreakLimit; i++) {
- ci2.incrementViews();
- ci2.incrementDismissals();
- }
-
- assertTrue(ci.shouldTriggerBlock());
- assertFalse(ci2.shouldTriggerBlock());
- }
-
- @Test
- public void testUpdateThresholds_ratioLimitsCorrectlyApplied() {
- float updatedDismissRatio = .99f;
- ChannelImpressions ci = new ChannelImpressions();
- ci.updateThresholds(updatedDismissRatio, DEFAULT_STREAK_LIMIT);
-
- // N views, N-1 dismissals, which doesn't satisfy the ratio = 1 criteria.
- for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) {
- ci.incrementViews();
- if (i != DEFAULT_STREAK_LIMIT) {
- ci.incrementDismissals();
- }
- }
-
- ChannelImpressions ci2 = new ChannelImpressions();
- ci2.updateThresholds(updatedDismissRatio, DEFAULT_STREAK_LIMIT);
-
- for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) {
- ci2.incrementViews();
- ci2.incrementDismissals();
- }
-
- assertFalse(ci.shouldTriggerBlock());
- assertTrue(ci2.shouldTriggerBlock());
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java
deleted file mode 100644
index ada61d0488c9f..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java
+++ /dev/null
@@ -1,59 +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.ext.services.notification;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.view.textclassifier.TextClassifier;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class EntityTypeCounterTest {
- private EntityTypeCounter mCounter;
-
- @Before
- public void setup() {
- mCounter = new EntityTypeCounter();
- }
-
- @Test
- public void testIncrementAndGetCount() {
- mCounter.increment(TextClassifier.TYPE_URL);
- mCounter.increment(TextClassifier.TYPE_URL);
- mCounter.increment(TextClassifier.TYPE_URL);
-
- mCounter.increment(TextClassifier.TYPE_PHONE);
- mCounter.increment(TextClassifier.TYPE_PHONE);
-
- assertThat(mCounter.getCount(TextClassifier.TYPE_URL)).isEqualTo(3);
- assertThat(mCounter.getCount(TextClassifier.TYPE_PHONE)).isEqualTo(2);
- assertThat(mCounter.getCount(TextClassifier.TYPE_DATE_TIME)).isEqualTo(0);
- }
-
- @Test
- public void testIncrementAndGetCount_typeDateAndDateTime() {
- mCounter.increment(TextClassifier.TYPE_DATE_TIME);
- mCounter.increment(TextClassifier.TYPE_DATE);
-
- assertThat(mCounter.getCount(TextClassifier.TYPE_DATE_TIME)).isEqualTo(2);
- assertThat(mCounter.getCount(TextClassifier.TYPE_DATE)).isEqualTo(2);
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java
deleted file mode 100644
index ea77d31f69695..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java
+++ /dev/null
@@ -1,202 +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.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.os.Process.FIRST_APPLICATION_UID;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.os.Process;
-import android.service.notification.StatusBarNotification;
-import android.testing.TestableContext;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-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)
-public class NotificationCategorizerTest {
- @Mock
- private NotificationEntry mEntry;
- @Mock
- private StatusBarNotification mSbn;
-
- @Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getContext(), null);
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- when(mEntry.getSbn()).thenReturn(mSbn);
- when(mSbn.getUid()).thenReturn(Process.myUid());
- when(mSbn.getPackageName()).thenReturn(mContext.getPackageName());
- }
-
- @Test
- public void testPeopleCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.involvesPeople()).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_PEOPLE, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_PEOPLE));
- }
-
- @Test
- public void testMin() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_MIN));
- when(mEntry.involvesPeople()).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_MIN, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_MIN));
- }
-
- @Test
- public void testHigh() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_HIGH));
-
- assertEquals(NotificationCategorizer.CATEGORY_HIGH, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_HIGH));
- }
-
- @Test
- public void testOngoingCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.isOngoing()).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_ONGOING, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_ONGOING));
-
- when(mEntry.isOngoing()).thenReturn(false);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE));
- }
-
- @Test
- public void testAlarmCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.isCategory(Notification.CATEGORY_ALARM)).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_ALARM, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_ALARM));
-
- when(mEntry.isCategory(Notification.CATEGORY_ALARM)).thenReturn(false);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE));
- }
-
- @Test
- public void testCallCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.isCategory(Notification.CATEGORY_CALL)).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_CALL, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_CALL));
-
- when(mEntry.isCategory(Notification.CATEGORY_CALL)).thenReturn(false);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE));
- }
-
- @Test
- public void testReminderCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.isCategory(Notification.CATEGORY_REMINDER)).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_REMINDER, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_REMINDER));
-
- when(mEntry.isCategory(Notification.CATEGORY_REMINDER)).thenReturn(false);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE));
- }
-
- @Test
- public void testEventCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.isCategory(Notification.CATEGORY_EVENT)).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_EVENT, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_EVENT));
-
- when(mEntry.isCategory(Notification.CATEGORY_EVENT)).thenReturn(false);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- }
-
- @Test
- public void testSystemCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.getImportance()).thenReturn(IMPORTANCE_DEFAULT);
- when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID - 1);
-
- assertEquals(NotificationCategorizer.CATEGORY_SYSTEM, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_SYSTEM));
-
- when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- }
-
- @Test
- public void testSystemLowCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_LOW));
- when(mEntry.getImportance()).thenReturn(IMPORTANCE_LOW);
- when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID - 1);
-
- assertEquals(NotificationCategorizer.CATEGORY_SYSTEM_LOW, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_SYSTEM_LOW));
-
- when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java
deleted file mode 100644
index c02607927b1b9..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java
+++ /dev/null
@@ -1,266 +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.ext.services.notification;
-
-import static android.app.Notification.FLAG_CAN_COLORIZE;
-import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.media.AudioAttributes.USAGE_ALARM;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.Person;
-import android.content.ComponentName;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Icon;
-import android.media.AudioAttributes;
-import android.os.Build;
-import android.os.Process;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import android.testing.TestableContext;
-
-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;
-
-import java.util.ArrayList;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-@RunWith(AndroidJUnit4.class)
-public class NotificationEntryTest {
- private String mPkg = "pkg";
- private int mUid = 2018;
- @Mock
- private IPackageManager mPackageManager;
- @Mock
- private ApplicationInfo mAppInfo;
- @Mock
- private SmsHelper mSmsHelper;
-
- private static final String DEFAULT_SMS_PACKAGE_NAME = "foo";
-
- @Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getContext(), null);
-
- private StatusBarNotification generateSbn(String channelId) {
- Notification n = new Notification.Builder(mContext, channelId)
- .setContentTitle("foo")
- .build();
-
- return new StatusBarNotification(mPkg, mPkg, 0, "tag", mUid, mUid, n,
- UserHandle.SYSTEM, null, 0);
- }
-
- private StatusBarNotification generateSbn(String channelId, String packageName) {
- Notification n = new Notification.Builder(mContext, channelId)
- .setContentTitle("foo")
- .build();
-
- return new StatusBarNotification(packageName, packageName, 0, "tag", mUid, mUid, n,
- UserHandle.SYSTEM, null, 0);
- }
-
- private StatusBarNotification generateSbn(Notification n) {
- return new StatusBarNotification(mPkg, mPkg, 0, "tag", mUid, mUid, n,
- UserHandle.SYSTEM, null, 0);
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mPkg = mContext.getPackageName();
- mUid = Process.myUid();
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
- .thenReturn(mAppInfo);
- mAppInfo.targetSdkVersion = Build.VERSION_CODES.P;
- when(mSmsHelper.getDefaultSmsApplication())
- .thenReturn(new ComponentName(DEFAULT_SMS_PACKAGE_NAME, "bar"));
- }
-
- @Test
- public void testHasPerson() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId());
- ArrayList people = new ArrayList<>();
- people.add(new Person.Builder().setKey("mailto:testing@android.com").build());
- sbn.getNotification().extras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, people);
-
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- assertTrue(entry.involvesPeople());
- }
-
- @Test
- public void testNotPerson() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- assertFalse(entry.involvesPeople());
- }
-
- @Test
- public void testHasPerson_matchesDefaultSmsApp() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId(), DEFAULT_SMS_PACKAGE_NAME);
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- assertTrue(entry.involvesPeople());
- }
-
- @Test
- public void testHasPerson_doesntMatchDefaultSmsApp() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId(), "abc");
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- assertFalse(entry.involvesPeople());
- }
-
- @Test
- public void testIsInboxStyle() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setStyle(new Notification.InboxStyle())
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
- assertTrue(entry.hasStyle(Notification.InboxStyle.class));
- }
-
- @Test
- public void testIsMessagingStyle() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setStyle(new Notification.MessagingStyle(""))
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
- assertTrue(entry.hasStyle(Notification.MessagingStyle.class));
- }
-
- @Test
- public void testIsNotPersonStyle() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setStyle(new Notification.BigPictureStyle())
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
- assertFalse(entry.hasStyle(Notification.InboxStyle.class));
- assertFalse(entry.hasStyle(Notification.MessagingStyle.class));
- }
-
- @Test
- public void testIsAudioAttributes() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- channel.setSound(null, new AudioAttributes.Builder().setUsage(USAGE_ALARM).build());
-
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
-
- assertTrue(entry.isAudioAttributesUsage(USAGE_ALARM));
- }
-
- @Test
- public void testIsNotAudioAttributes() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
-
- assertFalse(entry.isAudioAttributesUsage(USAGE_ALARM));
- }
-
- @Test
- public void testIsCategory() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setCategory(Notification.CATEGORY_EMAIL)
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
-
- assertTrue(entry.isCategory(Notification.CATEGORY_EMAIL));
- assertFalse(entry.isCategory(Notification.CATEGORY_MESSAGE));
- }
-
- @Test
- public void testIsOngoing() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setFlag(FLAG_FOREGROUND_SERVICE, true)
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
-
- assertTrue(entry.isOngoing());
- }
-
- @Test
- public void testIsNotOngoing() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setFlag(FLAG_CAN_COLORIZE, true)
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
-
- assertFalse(entry.isOngoing());
- }
-
- @Test
- public void testShrinkNotification() {
- Notification n = new Notification.Builder(mContext, "")
- .setLargeIcon(Icon.createWithResource(
- mContext, android.R.drawable.alert_dark_frame))
- .setSmallIcon(android.R.drawable.sym_def_app_icon)
- .build();
- n.largeIcon = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
-
- assertNull(entry.getNotification().getSmallIcon());
- assertNull(entry.getNotification().getLargeIcon());
- assertNull(entry.getNotification().largeIcon);
- assertNull(entry.getNotification().extras.getParcelable(Notification.EXTRA_LARGE_ICON));
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
deleted file mode 100644
index 69abe87cd4290..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
+++ /dev/null
@@ -1,564 +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.ext.services.notification;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Person;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.IPackageManager;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.os.Process;
-import android.service.notification.NotificationAssistantService;
-import android.service.notification.StatusBarNotification;
-import android.view.textclassifier.ConversationAction;
-import android.view.textclassifier.ConversationActions;
-import android.view.textclassifier.TextClassificationManager;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextClassifierEvent;
-
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.Subject;
-import com.google.common.truth.SubjectFactory;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.time.Instant;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-import javax.annotation.Nullable;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-@RunWith(AndroidJUnit4.class)
-public class SmartActionsHelperTest {
- private static final String RESULT_ID = "id";
- private static final float SCORE = 0.7f;
- private static final CharSequence SMART_REPLY = "Home";
- private static final ConversationAction REPLY_ACTION =
- new ConversationAction.Builder(ConversationAction.TYPE_TEXT_REPLY)
- .setTextReply(SMART_REPLY)
- .setConfidenceScore(SCORE)
- .build();
- private static final String MESSAGE = "Where are you?";
-
- @Mock
- IPackageManager mIPackageManager;
- @Mock
- private TextClassifier mTextClassifier;
- private StatusBarNotification mStatusBarNotification;
- @Mock
- private SmsHelper mSmsHelper;
-
- private SmartActionsHelper mSmartActionsHelper;
- private Context mContext;
- private Notification.Builder mNotificationBuilder;
- private AssistantSettings mSettings;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getTargetContext();
-
- mContext.getSystemService(TextClassificationManager.class)
- .setTextClassifier(mTextClassifier);
- when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
- .thenReturn(new ConversationActions(Arrays.asList(REPLY_ACTION), RESULT_ID));
-
- mNotificationBuilder = new Notification.Builder(mContext, "channel");
- mSettings = AssistantSettings.createForTesting(
- null, null, Process.myUserHandle().getIdentifier(), null);
- mSettings.mGenerateActions = true;
- mSettings.mGenerateReplies = true;
- mSmartActionsHelper = new SmartActionsHelper(mContext, mSettings);
- }
-
- private void setStatusBarNotification(Notification n) {
- mStatusBarNotification = new StatusBarNotification("random.app", "random.app", 0,
- "tag", Process.myUid(), Process.myPid(), n, Process.myUserHandle(), null, 0);
- }
-
- @Test
- public void testSuggest_notMessageNotification() {
- Notification notification = mNotificationBuilder.setContentText(MESSAGE).build();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- verify(mTextClassifier, never())
- .suggestConversationActions(any(ConversationActions.Request.class));
- }
-
- @Test
- public void testSuggest_noInlineReply() {
- Notification notification =
- mNotificationBuilder
- .setContentText(MESSAGE)
- .setCategory(Notification.CATEGORY_MESSAGE)
- .build();
- setStatusBarNotification(notification);
-
- ConversationActions.Request request = runSuggestAndCaptureRequest();
-
- // actions are enabled, but replies are not.
- assertThat(
- request.getTypeConfig().resolveEntityListModifications(
- Arrays.asList(ConversationAction.TYPE_TEXT_REPLY,
- ConversationAction.TYPE_OPEN_URL)))
- .containsExactly(ConversationAction.TYPE_OPEN_URL);
- }
-
- @Test
- public void testSuggest_settingsOff() {
- mSettings.mGenerateActions = false;
- mSettings.mGenerateReplies = false;
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- verify(mTextClassifier, never())
- .suggestConversationActions(any(ConversationActions.Request.class));
- }
-
- @Test
- public void testSuggest_settings_repliesOnActionsOff() {
- mSettings.mGenerateReplies = true;
- mSettings.mGenerateActions = false;
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- ConversationActions.Request request = runSuggestAndCaptureRequest();
-
- // replies are enabled, but actions are not.
- assertThat(
- request.getTypeConfig().resolveEntityListModifications(
- Arrays.asList(ConversationAction.TYPE_TEXT_REPLY,
- ConversationAction.TYPE_OPEN_URL)))
- .containsExactly(ConversationAction.TYPE_TEXT_REPLY);
- }
-
- @Test
- public void testSuggest_settings_repliesOffActionsOn() {
- mSettings.mGenerateReplies = false;
- mSettings.mGenerateActions = true;
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- ConversationActions.Request request = runSuggestAndCaptureRequest();
-
- // actions are enabled, but replies are not.
- assertThat(
- request.getTypeConfig().resolveEntityListModifications(
- Arrays.asList(ConversationAction.TYPE_TEXT_REPLY,
- ConversationAction.TYPE_OPEN_URL)))
- .containsExactly(ConversationAction.TYPE_OPEN_URL);
- }
-
-
- @Test
- public void testSuggest_nonMessageStyleMessageNotification() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- List messages =
- runSuggestAndCaptureRequest().getConversation();
-
- assertThat(messages).hasSize(1);
- MessageSubject.assertThat(messages.get(0)).hasText(MESSAGE);
- ArgumentCaptor argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
- TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
- assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertThat(textClassifierEvent.getEntityTypes()).asList()
- .containsExactly(ConversationAction.TYPE_TEXT_REPLY);
- }
-
- @Test
- public void testSuggest_messageStyle() {
- Person me = new Person.Builder().setName("Me").build();
- Person userA = new Person.Builder().setName("A").build();
- Person userB = new Person.Builder().setName("B").build();
- Notification.MessagingStyle style =
- new Notification.MessagingStyle(me)
- .addMessage("firstMessage", 1000, (Person) null)
- .addMessage("secondMessage", 2000, me)
- .addMessage("thirdMessage", 3000, userA)
- .addMessage("fourthMessage", 4000, userB);
- Notification notification =
- mNotificationBuilder
- .setContentText("You have three new messages")
- .setStyle(style)
- .setActions(createReplyAction())
- .build();
- setStatusBarNotification(notification);
-
- List messages =
- runSuggestAndCaptureRequest().getConversation();
- assertThat(messages).hasSize(4);
-
- ConversationActions.Message firstMessage = messages.get(0);
- MessageSubject.assertThat(firstMessage).hasText("firstMessage");
- MessageSubject.assertThat(firstMessage)
- .hasPerson(ConversationActions.Message.PERSON_USER_SELF);
- MessageSubject.assertThat(firstMessage)
- .hasReferenceTime(createZonedDateTimeFromMsUtc(1000));
-
- ConversationActions.Message secondMessage = messages.get(1);
- MessageSubject.assertThat(secondMessage).hasText("secondMessage");
- MessageSubject.assertThat(secondMessage)
- .hasPerson(ConversationActions.Message.PERSON_USER_SELF);
- MessageSubject.assertThat(secondMessage)
- .hasReferenceTime(createZonedDateTimeFromMsUtc(2000));
-
- ConversationActions.Message thirdMessage = messages.get(2);
- MessageSubject.assertThat(thirdMessage).hasText("thirdMessage");
- MessageSubject.assertThat(thirdMessage).hasPerson(userA);
- MessageSubject.assertThat(thirdMessage)
- .hasReferenceTime(createZonedDateTimeFromMsUtc(3000));
-
- ConversationActions.Message fourthMessage = messages.get(3);
- MessageSubject.assertThat(fourthMessage).hasText("fourthMessage");
- MessageSubject.assertThat(fourthMessage).hasPerson(userB);
- MessageSubject.assertThat(fourthMessage)
- .hasReferenceTime(createZonedDateTimeFromMsUtc(4000));
-
- ArgumentCaptor argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
- TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
- assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertThat(textClassifierEvent.getEntityTypes()).asList()
- .containsExactly(ConversationAction.TYPE_TEXT_REPLY);
- }
-
- @Test
- public void testSuggest_lastMessageLocalUser() {
- Person me = new Person.Builder().setName("Me").build();
- Person userA = new Person.Builder().setName("A").build();
- Notification.MessagingStyle style =
- new Notification.MessagingStyle(me)
- .addMessage("firstMessage", 1000, userA)
- .addMessage("secondMessage", 2000, me);
- Notification notification =
- mNotificationBuilder
- .setContentText("You have two new messages")
- .setStyle(style)
- .setActions(createReplyAction())
- .build();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- verify(mTextClassifier, never())
- .suggestConversationActions(any(ConversationActions.Request.class));
- }
-
- @Test
- public void testSuggest_messageStyle_noPerson() {
- Person me = new Person.Builder().setName("Me").build();
- Notification.MessagingStyle style =
- new Notification.MessagingStyle(me).addMessage("message", 1000, (Person) null);
- Notification notification =
- mNotificationBuilder
- .setContentText("You have one new message")
- .setStyle(style)
- .setActions(createReplyAction())
- .build();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- verify(mTextClassifier, never())
- .suggestConversationActions(any(ConversationActions.Request.class));
- }
-
- @Test
- public void testOnSuggestedReplySent() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY,
- NotificationAssistantService.SOURCE_FROM_ASSISTANT);
-
- ArgumentCaptor argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
- List events = argumentCaptor.getAllValues();
- assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_SMART_ACTION);
- float[] scores = events.get(1).getScores();
- assertThat(scores).hasLength(1);
- assertThat(scores[0]).isEqualTo(SCORE);
- }
-
- @Test
- public void testOnSuggestedReplySent_anotherNotification() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onSuggestedReplySent(
- "something_else", MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
-
- verify(mTextClassifier, never()).onTextClassifierEvent(
- argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_SMART_ACTION)));
- }
-
- @Test
- public void testOnSuggestedReplySent_missingResultId() {
- when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
- .thenReturn(new ConversationActions(Collections.singletonList(REPLY_ACTION), null));
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY,
- NotificationAssistantService.SOURCE_FROM_ASSISTANT);
-
- verify(mTextClassifier, never()).onTextClassifierEvent(any(TextClassifierEvent.class));
- }
-
- @Test
- public void testOnNotificationDirectReply() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onNotificationDirectReplied(mStatusBarNotification.getKey());
-
- ArgumentCaptor argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
- List events = argumentCaptor.getAllValues();
- assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_MANUAL_REPLY);
- }
-
- @Test
- public void testOnNotificationExpansionChanged() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true);
-
- ArgumentCaptor argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
- List events = argumentCaptor.getAllValues();
- assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN);
- }
-
- @Test
- public void testOnNotificationsSeen_notExpanded() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), false);
-
- verify(mTextClassifier, never()).onTextClassifierEvent(
- argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_ACTIONS_SHOWN)));
- }
-
- @Test
- public void testOnNotifications_expanded() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true);
-
- ArgumentCaptor argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
- List events = argumentCaptor.getAllValues();
- assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN);
- }
-
- @Test
- public void testCopyAction() {
- Bundle extras = new Bundle();
- Bundle entitiesExtras = new Bundle();
- entitiesExtras.putString(SmartActionsHelper.KEY_TEXT, "12345");
- extras.putParcelable(SmartActionsHelper.ENTITIES_EXTRAS, entitiesExtras);
- ConversationAction conversationAction =
- new ConversationAction.Builder(ConversationAction.TYPE_COPY)
- .setExtras(extras)
- .build();
- when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
- .thenReturn(
- new ConversationActions(
- Collections.singletonList(conversationAction), null));
-
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
- SmartActionsHelper.SmartSuggestions suggestions =
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- assertThat(suggestions.actions).hasSize(1);
- Notification.Action action = suggestions.actions.get(0);
- assertThat(action.title).isEqualTo("12345");
- }
-
- private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
- return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneOffset.systemDefault());
- }
-
- private ConversationActions.Request runSuggestAndCaptureRequest() {
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- ArgumentCaptor argumentCaptor =
- ArgumentCaptor.forClass(ConversationActions.Request.class);
- verify(mTextClassifier).suggestConversationActions(argumentCaptor.capture());
- return argumentCaptor.getValue();
- }
-
- private Notification.Action createReplyAction() {
- PendingIntent pendingIntent =
- PendingIntent.getActivity(mContext, 0, new Intent(mContext, this.getClass()), 0);
- RemoteInput remoteInput = new RemoteInput.Builder("result")
- .setAllowFreeFormInput(true)
- .build();
- return new Notification.Action.Builder(
- Icon.createWithResource(mContext.getResources(),
- android.R.drawable.stat_sys_warning),
- "Reply", pendingIntent)
- .addRemoteInput(remoteInput)
- .build();
- }
-
- private NotificationEntry createNotificationEntry() {
- NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
- return new NotificationEntry(
- mContext, mIPackageManager, mStatusBarNotification, channel, mSmsHelper);
- }
-
- private Notification createMessageNotification() {
- return mNotificationBuilder
- .setContentText(MESSAGE)
- .setCategory(Notification.CATEGORY_MESSAGE)
- .setActions(createReplyAction())
- .build();
- }
-
- private void assertTextClassifierEvent(
- TextClassifierEvent textClassifierEvent, int expectedEventType) {
- assertThat(textClassifierEvent.getEventCategory())
- .isEqualTo(TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS);
- assertThat(textClassifierEvent.getEventContext().getPackageName())
- .isEqualTo(InstrumentationRegistry.getTargetContext().getPackageName());
- assertThat(textClassifierEvent.getEventContext().getWidgetType())
- .isEqualTo(TextClassifier.WIDGET_TYPE_NOTIFICATION);
- assertThat(textClassifierEvent.getEventType()).isEqualTo(expectedEventType);
- }
-
- private static final class MessageSubject
- extends Subject {
-
- private static final SubjectFactory FACTORY =
- new SubjectFactory() {
- @Override
- public MessageSubject getSubject(
- @NonNull FailureStrategy failureStrategy,
- @NonNull ConversationActions.Message subject) {
- return new MessageSubject(failureStrategy, subject);
- }
- };
-
- private MessageSubject(
- FailureStrategy failureStrategy, @Nullable ConversationActions.Message subject) {
- super(failureStrategy, subject);
- }
-
- private void hasText(String text) {
- if (!Objects.equals(text, getSubject().getText().toString())) {
- failWithBadResults("has text", text, "has", getSubject().getText());
- }
- }
-
- private void hasPerson(Person person) {
- if (!Objects.equals(person, getSubject().getAuthor())) {
- failWithBadResults("has author", person, "has", getSubject().getAuthor());
- }
- }
-
- private void hasReferenceTime(ZonedDateTime referenceTime) {
- if (!Objects.equals(referenceTime, getSubject().getReferenceTime())) {
- failWithBadResults(
- "has reference time",
- referenceTime,
- "has",
- getSubject().getReferenceTime());
- }
- }
-
- private static MessageSubject assertThat(ConversationActions.Message message) {
- return assertAbout(FACTORY).that(message);
- }
- }
-
- private final class TextClassifierEventMatcher implements ArgumentMatcher {
-
- private int mType;
-
- private TextClassifierEventMatcher(int type) {
- mType = type;
- }
-
- @Override
- public boolean matches(TextClassifierEvent textClassifierEvent) {
- if (textClassifierEvent == null) {
- return false;
- }
- return mType == textClassifierEvent.getEventType();
- }
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java
deleted file mode 100644
index 12575a63d0add..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java
+++ /dev/null
@@ -1,35 +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.ext.services.sms;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-
-import org.junit.Test;
-
-/**
- * Contains the base tests for FinancialSmsServiceImpl.
- */
-public class FinancialSmsServiceImplTest {
-
- private final FinancialSmsServiceImpl mService = new FinancialSmsServiceImpl();
-
- @Test
- public void testOnGetSmsMessages_nullWithNoParamData() {
- assertThat(mService.onGetSmsMessages(new Bundle())).isNull();
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java
deleted file mode 100644
index df4738ff1948c..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java
+++ /dev/null
@@ -1,150 +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.ext.services.storage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.app.usage.CacheQuotaHint;
-import android.app.usage.UsageStats;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.test.ServiceTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Answers;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-public class CacheQuotaServiceImplTest extends ServiceTestCase {
- private static final String sTestVolUuid = "uuid";
- private static final String sSecondTestVolUuid = "otherUuid";
-
- @Mock private Context mContext;
- @Mock private File mFile;
- @Mock private VolumeInfo mVolume;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS) private StorageManager mStorageManager;
-
- public CacheQuotaServiceImplTest() {
- super(CacheQuotaServiceImpl.class);
- }
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- mContext = Mockito.spy(new ContextWrapper(getSystemContext()));
- setContext(mContext);
- when(mContext.getSystemService(Context.STORAGE_SERVICE)).thenReturn(mStorageManager);
-
- when(mFile.getUsableSpace()).thenReturn(10000L);
- when(mVolume.getPath()).thenReturn(mFile);
- when(mStorageManager.findVolumeByUuid(sTestVolUuid)).thenReturn(mVolume);
- when(mStorageManager.findVolumeByUuid(sSecondTestVolUuid)).thenReturn(mVolume);
-
- Intent intent = new Intent(getContext(), CacheQuotaServiceImpl.class);
- startService(intent);
- }
-
- @Test
- public void testNoApps() {
- CacheQuotaServiceImpl service = getService();
- assertEquals(service.onComputeCacheQuotaHints(new ArrayList()).size(), 0);
- }
-
- @Test
- public void testOneApp() throws Exception {
- ArrayList requests = new ArrayList<>();
- CacheQuotaHint request = makeNewRequest("com.test", sTestVolUuid, 1001, 100L);
- requests.add(request);
-
- List output = getService().onComputeCacheQuotaHints(requests);
-
- assertThat(output).hasSize(1);
- assertThat(output.get(0).getQuota()).isEqualTo(1500L);
- }
-
- @Test
- public void testTwoAppsOneVolume() throws Exception {
- ArrayList requests = new ArrayList<>();
- requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
- requests.add(makeNewRequest("com.test2", sTestVolUuid, 1002, 99L));
-
- List output = getService().onComputeCacheQuotaHints(requests);
-
- // Note that the sizes are just the cache area split up.
- assertThat(output).hasSize(2);
- assertThat(output.get(0).getQuota()).isEqualTo(883);
- assertThat(output.get(1).getQuota()).isEqualTo(1500 - 883);
- }
-
- @Test
- public void testTwoAppsTwoVolumes() throws Exception {
- ArrayList requests = new ArrayList<>();
- requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
- requests.add(makeNewRequest("com.test2", sSecondTestVolUuid, 1002, 99L));
-
- List output = getService().onComputeCacheQuotaHints(requests);
-
- assertThat(output).hasSize(2);
- assertThat(output.get(0).getQuota()).isEqualTo(1500);
- assertThat(output.get(1).getQuota()).isEqualTo(1500);
- }
-
- @Test
- public void testMultipleAppsPerUidIsCollated() throws Exception {
- ArrayList requests = new ArrayList<>();
- requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
- requests.add(makeNewRequest("com.test2", sTestVolUuid, 1001, 99L));
-
- List output = getService().onComputeCacheQuotaHints(requests);
-
- assertThat(output).hasSize(1);
- assertThat(output.get(0).getQuota()).isEqualTo(1500);
- }
-
- @Test
- public void testTwoAppsTwoVolumesTwoUuidsShouldBESeparate() throws Exception {
- ArrayList requests = new ArrayList<>();
- requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
- requests.add(makeNewRequest("com.test2", sSecondTestVolUuid, 1001, 99L));
-
- List output = getService().onComputeCacheQuotaHints(requests);
-
- assertThat(output).hasSize(2);
- assertThat(output.get(0).getQuota()).isEqualTo(1500);
- assertThat(output.get(1).getQuota()).isEqualTo(1500);
- }
-
- private CacheQuotaHint makeNewRequest(String packageName, String uuid, int uid, long foregroundTime) {
- UsageStats stats = new UsageStats();
- stats.mPackageName = packageName;
- stats.mTotalTimeInForeground = foregroundTime;
- return new CacheQuotaHint.Builder()
- .setVolumeUuid(uuid).setUid(uid).setUsageStats(stats).setQuota(-1).build();
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImplTest.java
deleted file mode 100644
index a9cb63e2a2004..0000000000000
--- a/packages/ExtServices/tests/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImplTest.java
+++ /dev/null
@@ -1,201 +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.ext.services.watchdog;
-
-import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_REQUESTED_PACKAGES;
-import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_SUPPORTED_PACKAGES;
-import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeFalse;
-
-import android.Manifest;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.RemoteCallback;
-import android.service.watchdog.ExplicitHealthCheckService;
-import android.service.watchdog.IExplicitHealthCheckService;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.rule.ServiceTestRule;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Contains the base tests that does not rely on the specific algorithm implementation.
- */
-public class ExplicitHealthCheckServiceImplTest {
- private static final String NETWORK_STACK_CONNECTOR_CLASS =
- "android.net.INetworkStackConnector";
-
- private final Context mContext = InstrumentationRegistry.getContext();
- private IExplicitHealthCheckService mService;
- private String mNetworkStackPackageName;
-
- @Rule
- public ServiceTestRule mServiceTestRule;
-
- @Before
- public void setUp() throws Exception {
- InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .adoptShellPermissionIdentity(
- Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE);
-
- mServiceTestRule = new ServiceTestRule();
- mService = IExplicitHealthCheckService.Stub.asInterface(
- mServiceTestRule.bindService(getExtServiceIntent()));
- mNetworkStackPackageName = getNetworkStackPackage();
- assumeFalse(mNetworkStackPackageName == null);
- }
-
- @After
- public void tearDown() {
- InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .dropShellPermissionIdentity();
- }
-
- @Test
- public void testHealthCheckSupportedPackage() throws Exception {
- List supportedPackages = new ArrayList<>();
- CountDownLatch latch = new CountDownLatch(1);
-
- mService.getSupportedPackages(new RemoteCallback(result -> {
- supportedPackages.addAll(result.getParcelableArrayList(EXTRA_SUPPORTED_PACKAGES));
- latch.countDown();
- }));
- latch.await();
-
- // TODO: Support DeviceConfig changes for the health check timeout
- assertThat(supportedPackages).hasSize(1);
- assertThat(supportedPackages.get(0).getPackageName())
- .isEqualTo(mNetworkStackPackageName);
- assertThat(supportedPackages.get(0).getHealthCheckTimeoutMillis())
- .isEqualTo(ExplicitHealthCheckServiceImpl.DEFAULT_REQUEST_TIMEOUT_MILLIS);
- }
-
- @Test
- public void testHealthCheckRequests() throws Exception {
- List requestedPackages = new ArrayList<>();
- CountDownLatch latch1 = new CountDownLatch(1);
- CountDownLatch latch2 = new CountDownLatch(1);
- CountDownLatch latch3 = new CountDownLatch(1);
-
- // Initially, no health checks requested
- mService.getRequestedPackages(new RemoteCallback(result -> {
- requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES));
- latch1.countDown();
- }));
-
- // Verify that no health checks requested
- latch1.await();
- assertThat(requestedPackages).isEmpty();
-
- // Then request health check
- mService.request(mNetworkStackPackageName);
-
- // Verify that health check is requested for network stack
- mService.getRequestedPackages(new RemoteCallback(result -> {
- requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES));
- latch2.countDown();
- }));
- latch2.await();
- assertThat(requestedPackages).hasSize(1);
- assertThat(requestedPackages.get(0)).isEqualTo(mNetworkStackPackageName);
-
- // Then cancel health check
- requestedPackages.clear();
- mService.cancel(mNetworkStackPackageName);
-
- // Verify that health check is cancelled for network stack
- mService.getRequestedPackages(new RemoteCallback(result -> {
- requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES));
- latch3.countDown();
- }));
- latch3.await();
- assertThat(requestedPackages).isEmpty();
- }
-
- private String getNetworkStackPackage() {
- Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- if (comp != null) {
- return comp.getPackageName();
- } else {
- // On Go devices, or any device that does not ship the network stack module.
- // The network stack will live in system_server process, so no need to monitor.
- return null;
- }
- }
-
- private Intent getExtServiceIntent() {
- ComponentName component = getExtServiceComponentNameLocked();
- if (component == null) {
- fail("Health check service not found");
- }
- Intent intent = new Intent();
- intent.setComponent(component);
- return intent;
- }
-
- private ComponentName getExtServiceComponentNameLocked() {
- ServiceInfo serviceInfo = getExtServiceInfoLocked();
- if (serviceInfo == null) {
- return null;
- }
-
- final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- if (!Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE
- .equals(serviceInfo.permission)) {
- return null;
- }
- return name;
- }
-
- private ServiceInfo getExtServiceInfoLocked() {
- final String packageName =
- mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
- if (packageName == null) {
- return null;
- }
-
- final Intent intent = new Intent(ExplicitHealthCheckService.SERVICE_INTERFACE);
- intent.setPackage(packageName);
- final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
- if (resolveInfo == null || resolveInfo.serviceInfo == null) {
- return null;
- }
- return resolveInfo.serviceInfo;
- }
-}
diff --git a/packages/NetworkPermissionConfig/Android.bp b/packages/NetworkPermissionConfig/Android.bp
deleted file mode 100644
index 6e50459a1dd38..0000000000000
--- a/packages/NetworkPermissionConfig/Android.bp
+++ /dev/null
@@ -1,41 +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_defaults {
- name: "NetworkPermissionConfigDefaults",
- // TODO: mark app as hasCode=false in manifest once soong stops complaining about apps without
- // a classes.dex.
- srcs: ["src/**/*.java"],
- platform_apis: true,
- min_sdk_version: "28",
- privileged: true,
- manifest: "AndroidManifest.xml",
-}
-
-// Stub APK to define permissions for NetworkStack
-android_app {
- name: "NetworkPermissionConfig",
- defaults: ["NetworkPermissionConfigDefaults"],
- certificate: "networkstack",
-}
-
-// Alternative stub APK signed with platform certificate. To use with InProcessNetworkStack.
-android_app {
- name: "PlatformNetworkPermissionConfig",
- defaults: ["NetworkPermissionConfigDefaults"],
- certificate: "platform",
- overrides: ["NetworkPermissionConfig"],
-}
diff --git a/packages/NetworkPermissionConfig/AndroidManifest.xml b/packages/NetworkPermissionConfig/AndroidManifest.xml
deleted file mode 100644
index 496fa4defcde2..0000000000000
--- a/packages/NetworkPermissionConfig/AndroidManifest.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/packages/NetworkPermissionConfig/OWNERS b/packages/NetworkPermissionConfig/OWNERS
deleted file mode 100644
index 52193eb2a12b7..0000000000000
--- a/packages/NetworkPermissionConfig/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-set noparent
-
-lorenzo@google.com
-baligh@google.com
-delphij@google.com
diff --git a/packages/NetworkPermissionConfig/src/com/android/server/NetworkPermissionConfig.java b/packages/NetworkPermissionConfig/src/com/android/server/NetworkPermissionConfig.java
deleted file mode 100644
index c904e23636896..0000000000000
--- a/packages/NetworkPermissionConfig/src/com/android/server/NetworkPermissionConfig.java
+++ /dev/null
@@ -1,26 +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.server;
-
-import android.app.Application;
-
-/**
- * Empty application for NetworkPermissionConfig that only exists because
- * soong builds complain if APKs have no source file.
- */
-public class NetworkPermissionConfig extends Application {
-}
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
deleted file mode 100644
index c013b8c09ed65..0000000000000
--- a/packages/NetworkStack/Android.bp
+++ /dev/null
@@ -1,134 +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.
-//
-
-java_library {
- name: "captiveportal-lib",
- srcs: ["common/**/*.java"],
- libs: [
- "androidx.annotation_annotation",
- ],
- sdk_version: "system_current",
-}
-
-java_defaults {
- name: "NetworkStackCommon",
- sdk_version: "system_current",
- min_sdk_version: "28",
-}
-
-// Library including the network stack, used to compile both variants of the network stack
-android_library {
- name: "NetworkStackBase",
- defaults: ["NetworkStackCommon"],
- srcs: [
- "src/**/*.java",
- ":framework-networkstack-shared-srcs",
- ":services-networkstack-shared-srcs",
- ":statslog-networkstack-java-gen",
- ],
- static_libs: [
- "androidx.annotation_annotation",
- "ipmemorystore-client",
- "netd_aidl_interface-V2-java",
- "networkstack-aidl-interfaces-V3-java",
- "datastallprotosnano",
- "networkstackprotosnano",
- "captiveportal-lib",
- ],
- manifest: "AndroidManifestBase.xml",
-}
-
-cc_library_shared {
- name: "libnetworkstackutilsjni",
- srcs: [
- "jni/network_stack_utils_jni.cpp"
- ],
- sdk_version: "current",
- shared_libs: [
- "liblog",
- "libnativehelper_compat_libc++",
- ],
-
- // We cannot use plain "libc++" here to link libc++ dynamically because it results in:
- // java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found
- // even if "libc++" is added into jni_libs below. Adding "libc++_shared" into jni_libs doesn't
- // build because soong complains of:
- // module NetworkStack missing dependencies: libc++_shared
- //
- // So, link libc++ statically. This means that we also need to ensure that all the C++ libraries
- // we depend on do not dynamically link libc++. This is currently the case, because liblog is
- // C-only and libnativehelper_compat_libc also uses stl: "c++_static".
- //
- // TODO: find a better solution for this in R.
- stl: "c++_static",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- ],
-}
-
-java_defaults {
- name: "NetworkStackAppCommon",
- defaults: ["NetworkStackCommon"],
- privileged: true,
- static_libs: [
- "NetworkStackBase",
- ],
- jni_libs: [
- "libnativehelper_compat_libc++",
- "libnetworkstackutilsjni",
- ],
- // Resources already included in NetworkStackBase
- resource_dirs: [],
- jarjar_rules: "jarjar-rules-shared.txt",
- optimize: {
- proguard_flags_files: ["proguard.flags"],
- },
-}
-
-// Non-updatable network stack running in the system server process for devices not using the module
-android_app {
- name: "InProcessNetworkStack",
- defaults: ["NetworkStackAppCommon"],
- certificate: "platform",
- manifest: "AndroidManifest_InProcess.xml",
- // InProcessNetworkStack is a replacement for NetworkStack
- overrides: ["NetworkStack"],
- // The permission configuration *must* be included to ensure security of the device
- // The InProcessNetworkStack goes together with the PlatformCaptivePortalLogin, which replaces
- // the default CaptivePortalLogin.
- required: ["PlatformNetworkPermissionConfig", "PlatformCaptivePortalLogin"],
-}
-
-// Updatable network stack packaged as an application
-android_app {
- name: "NetworkStack",
- defaults: ["NetworkStackAppCommon"],
- certificate: "networkstack",
- manifest: "AndroidManifest.xml",
- use_embedded_native_libs: true,
- // The permission configuration *must* be included to ensure security of the device
- required: ["NetworkPermissionConfig"],
-}
-
-genrule {
- name: "statslog-networkstack-java-gen",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --java $(out) --module network_stack" +
- " --javaPackage com.android.networkstack.metrics --javaClass NetworkStackStatsLog",
- out: ["com/android/networkstack/metrics/NetworkStackStatsLog.java"],
-}
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
deleted file mode 100644
index 6166c66e0f428..0000000000000
--- a/packages/NetworkStack/AndroidManifest.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/NetworkStack/AndroidManifestBase.xml b/packages/NetworkStack/AndroidManifestBase.xml
deleted file mode 100644
index 69a4da46e0881..0000000000000
--- a/packages/NetworkStack/AndroidManifestBase.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
diff --git a/packages/NetworkStack/AndroidManifest_InProcess.xml b/packages/NetworkStack/AndroidManifest_InProcess.xml
deleted file mode 100644
index 2778a2a5900bc..0000000000000
--- a/packages/NetworkStack/AndroidManifest_InProcess.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/NetworkStack/OWNERS b/packages/NetworkStack/OWNERS
deleted file mode 100644
index 52193eb2a12b7..0000000000000
--- a/packages/NetworkStack/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-set noparent
-
-lorenzo@google.com
-baligh@google.com
-delphij@google.com
diff --git a/packages/NetworkStack/TEST_MAPPING b/packages/NetworkStack/TEST_MAPPING
deleted file mode 100644
index fe9731fe577e5..0000000000000
--- a/packages/NetworkStack/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit": [
- {
- "name": "NetworkStackTests"
- }
- ]
-}
\ No newline at end of file
diff --git a/packages/NetworkStack/common/CaptivePortalProbeResult.java b/packages/NetworkStack/common/CaptivePortalProbeResult.java
deleted file mode 100644
index 48cd48b30085b..0000000000000
--- a/packages/NetworkStack/common/CaptivePortalProbeResult.java
+++ /dev/null
@@ -1,88 +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.captiveportal;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Result of calling isCaptivePortal().
- * @hide
- */
-public final class CaptivePortalProbeResult {
- public static final int SUCCESS_CODE = 204;
- public static final int FAILED_CODE = 599;
- public static final int PORTAL_CODE = 302;
- // Set partial connectivity http response code to -1 to prevent conflict with the other http
- // response codes. Besides the default http response code of probe result is set as 599 in
- // NetworkMonitor#sendParallelHttpProbes(), so response code will be set as -1 only when
- // NetworkMonitor detects partial connectivity.
- /**
- * @hide
- */
- public static final int PARTIAL_CODE = -1;
-
- @NonNull
- public static final CaptivePortalProbeResult FAILED = new CaptivePortalProbeResult(FAILED_CODE);
- @NonNull
- public static final CaptivePortalProbeResult SUCCESS =
- new CaptivePortalProbeResult(SUCCESS_CODE);
- public static final CaptivePortalProbeResult PARTIAL =
- new CaptivePortalProbeResult(PARTIAL_CODE);
-
- private final int mHttpResponseCode; // HTTP response code returned from Internet probe.
- @Nullable
- public final String redirectUrl; // Redirect destination returned from Internet probe.
- @Nullable
- public final String detectUrl; // URL where a 204 response code indicates
- // captive portal has been appeased.
- @Nullable
- public final CaptivePortalProbeSpec probeSpec;
-
- public CaptivePortalProbeResult(int httpResponseCode) {
- this(httpResponseCode, null, null);
- }
-
- public CaptivePortalProbeResult(int httpResponseCode, @Nullable String redirectUrl,
- @Nullable String detectUrl) {
- this(httpResponseCode, redirectUrl, detectUrl, null);
- }
-
- public CaptivePortalProbeResult(int httpResponseCode, @Nullable String redirectUrl,
- @Nullable String detectUrl, @Nullable CaptivePortalProbeSpec probeSpec) {
- mHttpResponseCode = httpResponseCode;
- this.redirectUrl = redirectUrl;
- this.detectUrl = detectUrl;
- this.probeSpec = probeSpec;
- }
-
- public boolean isSuccessful() {
- return mHttpResponseCode == SUCCESS_CODE;
- }
-
- public boolean isPortal() {
- return !isSuccessful() && (mHttpResponseCode >= 200) && (mHttpResponseCode <= 399);
- }
-
- public boolean isFailed() {
- return !isSuccessful() && !isPortal();
- }
-
- public boolean isPartialConnectivity() {
- return mHttpResponseCode == PARTIAL_CODE;
- }
-}
diff --git a/packages/NetworkStack/common/CaptivePortalProbeSpec.java b/packages/NetworkStack/common/CaptivePortalProbeSpec.java
deleted file mode 100644
index bf983a50ab512..0000000000000
--- a/packages/NetworkStack/common/CaptivePortalProbeSpec.java
+++ /dev/null
@@ -1,195 +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.captiveportal;
-
-import static android.net.captiveportal.CaptivePortalProbeResult.PORTAL_CODE;
-import static android.net.captiveportal.CaptivePortalProbeResult.SUCCESS_CODE;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-
-/** @hide */
-public abstract class CaptivePortalProbeSpec {
- private static final String TAG = CaptivePortalProbeSpec.class.getSimpleName();
- private static final String REGEX_SEPARATOR = "@@/@@";
- private static final String SPEC_SEPARATOR = "@@,@@";
-
- private final String mEncodedSpec;
- private final URL mUrl;
-
- CaptivePortalProbeSpec(@NonNull String encodedSpec, @NonNull URL url) {
- mEncodedSpec = checkNotNull(encodedSpec);
- mUrl = checkNotNull(url);
- }
-
- /**
- * Parse a {@link CaptivePortalProbeSpec} from a {@link String}.
- *
- *
The valid format is a URL followed by two regular expressions, each separated by "@@/@@".
- * @throws MalformedURLException The URL has invalid format for {@link URL#URL(String)}.
- * @throws ParseException The string is empty, does not match the above format, or a regular
- * expression is invalid for {@link Pattern#compile(String)}.
- * @hide
- */
- @VisibleForTesting
- @NonNull
- public static CaptivePortalProbeSpec parseSpec(@NonNull String spec) throws ParseException,
- MalformedURLException {
- if (TextUtils.isEmpty(spec)) {
- throw new ParseException("Empty probe spec", 0 /* errorOffset */);
- }
-
- String[] splits = TextUtils.split(spec, REGEX_SEPARATOR);
- if (splits.length != 3) {
- throw new ParseException("Probe spec does not have 3 parts", 0 /* errorOffset */);
- }
-
- final int statusRegexPos = splits[0].length() + REGEX_SEPARATOR.length();
- final int locationRegexPos = statusRegexPos + splits[1].length() + REGEX_SEPARATOR.length();
- final Pattern statusRegex = parsePatternIfNonEmpty(splits[1], statusRegexPos);
- final Pattern locationRegex = parsePatternIfNonEmpty(splits[2], locationRegexPos);
-
- return new RegexMatchProbeSpec(spec, new URL(splits[0]), statusRegex, locationRegex);
- }
-
- @Nullable
- private static Pattern parsePatternIfNonEmpty(@Nullable String pattern, int pos)
- throws ParseException {
- if (TextUtils.isEmpty(pattern)) {
- return null;
- }
- try {
- return Pattern.compile(pattern);
- } catch (PatternSyntaxException e) {
- throw new ParseException(
- String.format("Invalid status pattern [%s]: %s", pattern, e),
- pos /* errorOffset */);
- }
- }
-
- /**
- * Parse a {@link CaptivePortalProbeSpec} from a {@link String}, or return a fallback spec
- * based on the status code of the provided URL if the spec cannot be parsed.
- */
- @Nullable
- public static CaptivePortalProbeSpec parseSpecOrNull(@Nullable String spec) {
- if (spec != null) {
- try {
- return parseSpec(spec);
- } catch (ParseException | MalformedURLException e) {
- Log.e(TAG, "Invalid probe spec: " + spec, e);
- // Fall through
- }
- }
- return null;
- }
-
- /**
- * Parse a config String to build an array of {@link CaptivePortalProbeSpec}.
- *
- *
Each spec is separated by @@,@@ and follows the format for {@link #parseSpec(String)}.
- *
This method does not throw but ignores any entry that could not be parsed.
- */
- @NonNull
- public static Collection parseCaptivePortalProbeSpecs(
- @NonNull String settingsVal) {
- List specs = new ArrayList<>();
- if (settingsVal != null) {
- for (String spec : TextUtils.split(settingsVal, SPEC_SEPARATOR)) {
- try {
- specs.add(parseSpec(spec));
- } catch (ParseException | MalformedURLException e) {
- Log.e(TAG, "Invalid probe spec: " + spec, e);
- }
- }
- }
-
- if (specs.isEmpty()) {
- Log.e(TAG, String.format("could not create any validation spec from %s", settingsVal));
- }
- return specs;
- }
-
- /**
- * Get the probe result from HTTP status and location header.
- */
- @NonNull
- public abstract CaptivePortalProbeResult getResult(int status, @Nullable String locationHeader);
-
- @NonNull
- public String getEncodedSpec() {
- return mEncodedSpec;
- }
-
- @NonNull
- public URL getUrl() {
- return mUrl;
- }
-
- /**
- * Implementation of {@link CaptivePortalProbeSpec} that is based on configurable regular
- * expressions for the HTTP status code and location header (if any). Matches indicate that
- * the page is not a portal.
- * This probe cannot fail: it always returns SUCCESS_CODE or PORTAL_CODE
- */
- private static class RegexMatchProbeSpec extends CaptivePortalProbeSpec {
- @Nullable
- final Pattern mStatusRegex;
- @Nullable
- final Pattern mLocationHeaderRegex;
-
- RegexMatchProbeSpec(
- String spec, URL url, Pattern statusRegex, Pattern locationHeaderRegex) {
- super(spec, url);
- mStatusRegex = statusRegex;
- mLocationHeaderRegex = locationHeaderRegex;
- }
-
- @Override
- public CaptivePortalProbeResult getResult(int status, String locationHeader) {
- final boolean statusMatch = safeMatch(String.valueOf(status), mStatusRegex);
- final boolean locationMatch = safeMatch(locationHeader, mLocationHeaderRegex);
- final int returnCode = statusMatch && locationMatch ? SUCCESS_CODE : PORTAL_CODE;
- return new CaptivePortalProbeResult(
- returnCode, locationHeader, getUrl().toString(), this);
- }
- }
-
- private static boolean safeMatch(@Nullable String value, @Nullable Pattern pattern) {
- // No value is a match ("no location header" passes the location rule for non-redirects)
- return pattern == null || TextUtils.isEmpty(value) || pattern.matcher(value).matches();
- }
-
- // Throws NullPointerException if the input is null.
- private static T checkNotNull(T object) {
- if (object == null) throw new NullPointerException();
- return object;
- }
-}
diff --git a/packages/NetworkStack/jarjar-rules-shared.txt b/packages/NetworkStack/jarjar-rules-shared.txt
deleted file mode 100644
index 7346b1ae81e69..0000000000000
--- a/packages/NetworkStack/jarjar-rules-shared.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-rule com.android.internal.util.** android.net.networkstack.util.@1
-
-rule android.net.shared.Inet4AddressUtils* android.net.networkstack.shared.Inet4AddressUtils@1
-rule android.net.shared.InetAddressUtils* android.net.networkstack.shared.InetAddressUtils@1
-
-# Ignore DhcpResultsParcelable, but jarjar DhcpResults
-# TODO: move DhcpResults into services.net and delete from here
-rule android.net.DhcpResultsParcelable* @0
-rule android.net.DhcpResults* android.net.networkstack.DhcpResults@1
-rule android.net.LocalLog* android.net.networkstack.LocalLog@1
diff --git a/packages/NetworkStack/jni/network_stack_utils_jni.cpp b/packages/NetworkStack/jni/network_stack_utils_jni.cpp
deleted file mode 100644
index f2ba5757ed80f..0000000000000
--- a/packages/NetworkStack/jni/network_stack_utils_jni.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#define LOG_TAG "NetworkStackUtils-JNI"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-#include
-#include
-
-namespace android {
-constexpr const char NETWORKSTACKUTILS_PKG_NAME[] = "android/net/util/NetworkStackUtils";
-
-static const uint32_t kEtherTypeOffset = offsetof(ether_header, ether_type);
-static const uint32_t kEtherHeaderLen = sizeof(ether_header);
-static const uint32_t kIPv4Protocol = kEtherHeaderLen + offsetof(iphdr, protocol);
-static const uint32_t kIPv4FlagsOffset = kEtherHeaderLen + offsetof(iphdr, frag_off);
-static const uint32_t kIPv6NextHeader = kEtherHeaderLen + offsetof(ip6_hdr, ip6_nxt);
-static const uint32_t kIPv6PayloadStart = kEtherHeaderLen + sizeof(ip6_hdr);
-static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
-static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, source);
-static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest);
-static const uint16_t kDhcpClientPort = 68;
-
-static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst) {
- if (env->GetArrayLength(addr) != len) {
- return false;
- }
- env->GetByteArrayRegion(addr, 0, len, reinterpret_cast(dst));
- return true;
-}
-
-static void network_stack_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray ethAddr,
- jbyteArray ipv4Addr, jstring ifname, jobject javaFd) {
- arpreq req = {};
- sockaddr_in& netAddrStruct = *reinterpret_cast(&req.arp_pa);
- sockaddr& ethAddrStruct = req.arp_ha;
-
- ethAddrStruct.sa_family = ARPHRD_ETHER;
- if (!checkLenAndCopy(env, ethAddr, ETH_ALEN, ethAddrStruct.sa_data)) {
- jniThrowException(env, "java/io/IOException", "Invalid ethAddr length");
- return;
- }
-
- netAddrStruct.sin_family = AF_INET;
- if (!checkLenAndCopy(env, ipv4Addr, sizeof(in_addr), &netAddrStruct.sin_addr)) {
- jniThrowException(env, "java/io/IOException", "Invalid ipv4Addr length");
- return;
- }
-
- int ifLen = env->GetStringLength(ifname);
- // IFNAMSIZ includes the terminating NULL character
- if (ifLen >= IFNAMSIZ) {
- jniThrowException(env, "java/io/IOException", "ifname too long");
- return;
- }
- env->GetStringUTFRegion(ifname, 0, ifLen, req.arp_dev);
-
- req.arp_flags = ATF_COM; // Completed entry (ha valid)
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- if (fd < 0) {
- jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
- return;
- }
- // See also: man 7 arp
- if (ioctl(fd, SIOCSARP, &req)) {
- jniThrowExceptionFmt(env, "java/io/IOException", "ioctl error: %s", strerror(errno));
- return;
- }
-}
-
-static void network_stack_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd) {
- static sock_filter filter_code[] = {
- // Check the protocol is UDP.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6),
-
- // Check this is not a fragment.
- BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
- BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 4, 0),
-
- // Get the IP header length.
- BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
-
- // Check the destination port.
- BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 0, 1),
-
- // Accept or reject.
- BPF_STMT(BPF_RET | BPF_K, 0xffff),
- BPF_STMT(BPF_RET | BPF_K, 0)
- };
- static 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 network_stack_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject javaFd,
- jint hardwareAddressType) {
- if (hardwareAddressType != ARPHRD_ETHER) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "attachRaFilter only supports ARPHRD_ETHER");
- return;
- }
-
- static sock_filter filter_code[] = {
- // Check IPv6 Next Header is ICMPv6.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
-
- // Check ICMPv6 type is Router Advertisement.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1),
-
- // Accept or reject.
- BPF_STMT(BPF_RET | BPF_K, 0xffff),
- BPF_STMT(BPF_RET | BPF_K, 0)
- };
- static 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));
- }
-}
-
-// TODO: Move all this filter code into libnetutils.
-static void network_stack_utils_attachControlPacketFilter(
- JNIEnv *env, jobject clazz, jobject javaFd, jint hardwareAddressType) {
- if (hardwareAddressType != ARPHRD_ETHER) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "attachControlPacketFilter only supports ARPHRD_ETHER");
- return;
- }
-
- // Capture all:
- // - ARPs
- // - DHCPv4 packets
- // - Router Advertisements & Solicitations
- // - Neighbor Advertisements & Solicitations
- //
- // tcpdump:
- // arp or
- // '(ip and udp port 68)' or
- // '(icmp6 and ip6[40] >= 133 and ip6[40] <= 136)'
- static sock_filter filter_code[] = {
- // Load the link layer next payload field.
- BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kEtherTypeOffset),
-
- // Accept all ARP.
- // TODO: Figure out how to better filter ARPs on noisy networks.
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 16, 0),
-
- // If IPv4:
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IP, 0, 9),
-
- // Check the protocol is UDP.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 14),
-
- // Check this is not a fragment.
- BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
- BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 12, 0),
-
- // Get the IP header length.
- BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
-
- // Check the source port.
- BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPSrcPortIndirectOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 8, 0),
-
- // Check the destination port.
- BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 6, 7),
-
- // IPv6 ...
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 6),
- // ... check IPv6 Next Header is ICMPv6 (ignore fragments), ...
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 4),
- // ... and check the ICMPv6 type is one of RS/RA/NS/NA.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
- BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, ND_ROUTER_SOLICIT, 0, 2),
- BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, ND_NEIGHBOR_ADVERT, 1, 0),
-
- // Accept or reject.
- BPF_STMT(BPF_RET | BPF_K, 0xffff),
- BPF_STMT(BPF_RET | BPF_K, 0)
- };
- static 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));
- }
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gNetworkStackUtilsMethods[] = {
- /* name, signature, funcPtr */
- { "addArpEntry", "([B[BLjava/lang/String;Ljava/io/FileDescriptor;)V", (void*) network_stack_utils_addArpEntry },
- { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) network_stack_utils_attachDhcpFilter },
- { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) network_stack_utils_attachRaFilter },
- { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) network_stack_utils_attachControlPacketFilter },
-};
-
-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 (jniRegisterNativeMethods(env, NETWORKSTACKUTILS_PKG_NAME,
- gNetworkStackUtilsMethods, NELEM(gNetworkStackUtilsMethods)) < 0) {
- return JNI_ERR;
- }
-
- return JNI_VERSION_1_6;
-
-}
-}; // namespace android
diff --git a/packages/NetworkStack/proguard.flags b/packages/NetworkStack/proguard.flags
deleted file mode 100644
index c60f6c338d839..0000000000000
--- a/packages/NetworkStack/proguard.flags
+++ /dev/null
@@ -1,9 +0,0 @@
--keepclassmembers class android.net.ip.IpClient {
- static final int CMD_*;
- static final int EVENT_*;
-}
-
--keepclassmembers class android.net.dhcp.DhcpClient {
- static final int CMD_*;
- static final int EVENT_*;
-}
diff --git a/packages/NetworkStack/res/values-mcc460/config.xml b/packages/NetworkStack/res/values-mcc460/config.xml
deleted file mode 100644
index fd4a8481ab225..0000000000000
--- a/packages/NetworkStack/res/values-mcc460/config.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
- https://connectivitycheck.gstatic.com/generate_204
-
- http://www.googleapis.cn/generate_204
-
-
diff --git a/packages/NetworkStack/res/values/config.xml b/packages/NetworkStack/res/values/config.xml
deleted file mode 100644
index 478ed6b06596f..0000000000000
--- a/packages/NetworkStack/res/values/config.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
- 12500
-
-
- http://connectivitycheck.gstatic.com/generate_204
-
-
- https://www.google.com/generate_204
-
-
-
- http://www.google.com/gen_204
- http://play.googleapis.com/generate_204
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/NetworkStack/res/values/overlayable.xml b/packages/NetworkStack/res/values/overlayable.xml
deleted file mode 100644
index b9d5337ec5726..0000000000000
--- a/packages/NetworkStack/res/values/overlayable.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java b/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java
deleted file mode 100644
index 41715b2a4798f..0000000000000
--- a/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java
+++ /dev/null
@@ -1,44 +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.annotation.NonNull;
-import android.content.Context;
-
-import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
-
-/**
- * service used to communicate with the ip memory store service in network stack,
- * which is running in the same module.
- * @see com.android.server.connectivity.ipmemorystore.IpMemoryStoreService
- * @hide
- */
-public class NetworkStackIpMemoryStore extends IpMemoryStoreClient {
- @NonNull private final IIpMemoryStore mService;
-
- public NetworkStackIpMemoryStore(@NonNull final Context context,
- @NonNull final IIpMemoryStore service) {
- super(context);
- mService = service;
- }
-
- @Override
- protected void runWhenServiceReady(Consumer cb) throws ExecutionException {
- cb.accept(mService);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
deleted file mode 100644
index f05431968684c..0000000000000
--- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java
+++ /dev/null
@@ -1,1981 +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.apf;
-
-import static android.net.util.SocketUtils.makePacketSocketAddress;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ARPHRD_ETHER;
-import static android.system.OsConstants.ETH_P_ARP;
-import static android.system.OsConstants.ETH_P_IP;
-import static android.system.OsConstants.ETH_P_IPV6;
-import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.IPPROTO_TCP;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_RAW;
-
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
-
-import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NattKeepalivePacketDataParcelable;
-import android.net.TcpKeepalivePacketDataParcelable;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpClient.IpClientCallbacksWrapper;
-import android.net.metrics.ApfProgramEvent;
-import android.net.metrics.ApfStats;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.RaEvent;
-import android.net.util.InterfaceParams;
-import android.net.util.NetworkStackUtils;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.format.DateUtils;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.HexDump;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * For networks that support packet filtering via APF programs, {@code ApfFilter}
- * listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to
- * filter out redundant duplicate ones.
- *
- * Threading model:
- * A collection of RAs we've received is kept in mRas. Generating APF programs uses mRas to
- * know what RAs to filter for, thus generating APF programs is dependent on mRas.
- * mRas can be accessed by multiple threads:
- * - ReceiveThread, which listens for RAs and adds them to mRas, and generates APF programs.
- * - callers of:
- * - setMulticastFilter(), which can cause an APF program to be generated.
- * - dump(), which dumps mRas among other things.
- * - shutdown(), which clears mRas.
- * So access to mRas is synchronized.
- *
- * @hide
- */
-public class ApfFilter {
-
- // Helper class for specifying functional filter parameters.
- public static class ApfConfiguration {
- public ApfCapabilities apfCapabilities;
- public boolean multicastFilter;
- public boolean ieee802_3Filter;
- public int[] ethTypeBlackList;
- }
-
- // Enums describing the outcome of receiving an RA packet.
- private static enum ProcessRaResult {
- MATCH, // Received RA matched a known RA
- DROPPED, // Received RA ignored due to MAX_RAS
- PARSE_ERROR, // Received RA could not be parsed
- ZERO_LIFETIME, // Received RA had 0 lifetime
- UPDATE_NEW_RA, // APF program updated for new RA
- UPDATE_EXPIRY // APF program updated for expiry
- }
-
- /**
- * APF packet counters.
- *
- * Packet counters are 32bit big-endian values, and allocated near the end of the APF data
- * buffer, using negative byte offsets, where -4 is equivalent to maximumApfProgramSize - 4,
- * the last writable 32bit word.
- */
- @VisibleForTesting
- public static enum Counter {
- RESERVED_OOB, // Points to offset 0 from the end of the buffer (out-of-bounds)
- TOTAL_PACKETS,
- PASSED_ARP,
- PASSED_DHCP,
- PASSED_IPV4,
- PASSED_IPV6_NON_ICMP,
- PASSED_IPV4_UNICAST,
- PASSED_IPV6_ICMP,
- PASSED_IPV6_UNICAST_NON_ICMP,
- PASSED_ARP_NON_IPV4,
- PASSED_ARP_UNKNOWN,
- PASSED_ARP_UNICAST_REPLY,
- PASSED_NON_IP_UNICAST,
- DROPPED_ETH_BROADCAST,
- DROPPED_RA,
- DROPPED_GARP_REPLY,
- DROPPED_ARP_OTHER_HOST,
- DROPPED_IPV4_L2_BROADCAST,
- DROPPED_IPV4_BROADCAST_ADDR,
- DROPPED_IPV4_BROADCAST_NET,
- DROPPED_IPV4_MULTICAST,
- DROPPED_IPV6_ROUTER_SOLICITATION,
- DROPPED_IPV6_MULTICAST_NA,
- DROPPED_IPV6_MULTICAST,
- DROPPED_IPV6_MULTICAST_PING,
- DROPPED_IPV6_NON_ICMP_MULTICAST,
- DROPPED_802_3_FRAME,
- DROPPED_ETHERTYPE_BLACKLISTED,
- DROPPED_ARP_REPLY_SPA_NO_HOST,
- DROPPED_IPV4_KEEPALIVE_ACK,
- DROPPED_IPV6_KEEPALIVE_ACK,
- DROPPED_IPV4_NATT_KEEPALIVE;
-
- // Returns the negative byte offset from the end of the APF data segment for
- // a given counter.
- public int offset() {
- return - this.ordinal() * 4; // Currently, all counters are 32bit long.
- }
-
- // Returns the total size of the data segment in bytes.
- public static int totalSize() {
- return (Counter.class.getEnumConstants().length - 1) * 4;
- }
- }
-
- /**
- * When APFv4 is supported, loads R1 with the offset of the specified counter.
- */
- private void maybeSetupCounter(ApfGenerator gen, Counter c) {
- if (mApfCapabilities.hasDataAccess()) {
- gen.addLoadImmediate(Register.R1, c.offset());
- }
- }
-
- // When APFv4 is supported, these point to the trampolines generated by emitEpilogue().
- // Otherwise, they're just aliases for PASS_LABEL and DROP_LABEL.
- private final String mCountAndPassLabel;
- private final String mCountAndDropLabel;
-
- // Thread to listen for RAs.
- @VisibleForTesting
- class ReceiveThread extends Thread {
- private final byte[] mPacket = new byte[1514];
- private final FileDescriptor mSocket;
- private final long mStart = SystemClock.elapsedRealtime();
-
- private int mReceivedRas = 0;
- private int mMatchingRas = 0;
- private int mDroppedRas = 0;
- private int mParseErrors = 0;
- private int mZeroLifetimeRas = 0;
- private int mProgramUpdates = 0;
-
- private volatile boolean mStopped;
-
- public ReceiveThread(FileDescriptor socket) {
- mSocket = socket;
- }
-
- public void halt() {
- mStopped = true;
- // Interrupts the read() call the thread is blocked in.
- NetworkStackUtils.closeSocketQuietly(mSocket);
- }
-
- @Override
- public void run() {
- log("begin monitoring");
- while (!mStopped) {
- try {
- int length = Os.read(mSocket, mPacket, 0, mPacket.length);
- updateStats(processRa(mPacket, length));
- } catch (IOException|ErrnoException e) {
- if (!mStopped) {
- Log.e(TAG, "Read error", e);
- }
- }
- }
- logStats();
- }
-
- private void updateStats(ProcessRaResult result) {
- mReceivedRas++;
- switch(result) {
- case MATCH:
- mMatchingRas++;
- return;
- case DROPPED:
- mDroppedRas++;
- return;
- case PARSE_ERROR:
- mParseErrors++;
- return;
- case ZERO_LIFETIME:
- mZeroLifetimeRas++;
- return;
- case UPDATE_EXPIRY:
- mMatchingRas++;
- mProgramUpdates++;
- return;
- case UPDATE_NEW_RA:
- mProgramUpdates++;
- return;
- }
- }
-
- private void logStats() {
- final long nowMs = SystemClock.elapsedRealtime();
- synchronized (this) {
- final ApfStats stats = new ApfStats.Builder()
- .setReceivedRas(mReceivedRas)
- .setMatchingRas(mMatchingRas)
- .setDroppedRas(mDroppedRas)
- .setParseErrors(mParseErrors)
- .setZeroLifetimeRas(mZeroLifetimeRas)
- .setProgramUpdates(mProgramUpdates)
- .setDurationMs(nowMs - mStart)
- .setMaxProgramSize(mApfCapabilities.maximumApfProgramSize)
- .setProgramUpdatesAll(mNumProgramUpdates)
- .setProgramUpdatesAllowingMulticast(mNumProgramUpdatesAllowingMulticast)
- .build();
- mMetricsLog.log(stats);
- logApfProgramEventLocked(nowMs / DateUtils.SECOND_IN_MILLIS);
- }
- }
- }
-
- private static final String TAG = "ApfFilter";
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
- private static final int ETH_HEADER_LEN = 14;
- private static final int ETH_DEST_ADDR_OFFSET = 0;
- private static final int ETH_ETHERTYPE_OFFSET = 12;
- private static final int ETH_TYPE_MIN = 0x0600;
- private static final int ETH_TYPE_MAX = 0xFFFF;
- private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
- {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
- // TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN.
- private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2;
- private static final int IPV4_FRAGMENT_OFFSET_OFFSET = ETH_HEADER_LEN + 6;
- // Endianness is not an issue for this constant because the APF interpreter always operates in
- // network byte order.
- private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff;
- private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
- private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
- private static final int IPV4_ANY_HOST_ADDRESS = 0;
- private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255
- private static final int IPV4_HEADER_LEN = 20; // Without options
-
- // Traffic class and Flow label are not byte aligned. Luckily we
- // don't care about either value so we'll consider bytes 1-3 of the
- // IPv6 header as don't care.
- private static final int IPV6_FLOW_LABEL_OFFSET = ETH_HEADER_LEN + 1;
- private static final int IPV6_FLOW_LABEL_LEN = 3;
- private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
- private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
- private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
- private static final int IPV6_HEADER_LEN = 40;
- // The IPv6 all nodes address ff02::1
- private static final byte[] IPV6_ALL_NODES_ADDRESS =
- { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
-
- private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
-
- // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
- private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
- private static final int UDP_HEADER_LEN = 8;
-
- private static final int TCP_HEADER_SIZE_OFFSET = 12;
-
- private static final int DHCP_CLIENT_PORT = 68;
- // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
- private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
-
- private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
- private static final byte[] ARP_IPV4_HEADER = {
- 0, 1, // Hardware type: Ethernet (1)
- 8, 0, // Protocol type: IP (0x0800)
- 6, // Hardware size: 6
- 4, // Protocol size: 4
- };
- private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6;
- // Opcode: ARP request (0x0001), ARP reply (0x0002)
- private static final short ARP_OPCODE_REQUEST = 1;
- private static final short ARP_OPCODE_REPLY = 2;
- private static final int ARP_SOURCE_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 14;
- private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 24;
- // Do not log ApfProgramEvents whose actual lifetimes was less than this.
- private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2;
- // Limit on the Black List size to cap on program usage for this
- // TODO: Select a proper max length
- private static final int APF_MAX_ETH_TYPE_BLACK_LIST_LEN = 20;
-
- private final ApfCapabilities mApfCapabilities;
- private final IpClientCallbacksWrapper mIpClientCallback;
- private final InterfaceParams mInterfaceParams;
- private final IpConnectivityLog mMetricsLog;
-
- @VisibleForTesting
- byte[] mHardwareAddress;
- @VisibleForTesting
- ReceiveThread mReceiveThread;
- @GuardedBy("this")
- private long mUniqueCounter;
- @GuardedBy("this")
- private boolean mMulticastFilter;
- @GuardedBy("this")
- private boolean mInDozeMode;
- private final boolean mDrop802_3Frames;
- private final int[] mEthTypeBlackList;
-
- // Detects doze mode state transitions.
- private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
- PowerManager powerManager =
- (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- final boolean deviceIdle = powerManager.isDeviceIdleMode();
- setDozeMode(deviceIdle);
- }
- }
- };
- private final Context mContext;
-
- // Our IPv4 address, if we have just one, otherwise null.
- @GuardedBy("this")
- private byte[] mIPv4Address;
- // The subnet prefix length of our IPv4 network. Only valid if mIPv4Address is not null.
- @GuardedBy("this")
- private int mIPv4PrefixLength;
-
- @VisibleForTesting
- ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams,
- IpClientCallbacksWrapper ipClientCallback, IpConnectivityLog log) {
- mApfCapabilities = config.apfCapabilities;
- mIpClientCallback = ipClientCallback;
- mInterfaceParams = ifParams;
- mMulticastFilter = config.multicastFilter;
- mDrop802_3Frames = config.ieee802_3Filter;
- mContext = context;
-
- if (mApfCapabilities.hasDataAccess()) {
- mCountAndPassLabel = "countAndPass";
- mCountAndDropLabel = "countAndDrop";
- } else {
- // APFv4 unsupported: turn jumps to the counter trampolines to immediately PASS or DROP,
- // preserving the original pre-APFv4 behavior.
- mCountAndPassLabel = ApfGenerator.PASS_LABEL;
- mCountAndDropLabel = ApfGenerator.DROP_LABEL;
- }
-
- // Now fill the black list from the passed array
- mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList);
-
- mMetricsLog = log;
-
- // TODO: ApfFilter should not generate programs until IpClient sends provisioning success.
- maybeStartFilter();
-
- // Listen for doze-mode transition changes to enable/disable the IPv6 multicast filter.
- mContext.registerReceiver(mDeviceIdleReceiver,
- new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED));
- }
-
- public synchronized void setDataSnapshot(byte[] data) {
- mDataSnapshot = data;
- }
-
- private void log(String s) {
- Log.d(TAG, "(" + mInterfaceParams.name + "): " + s);
- }
-
- @GuardedBy("this")
- private long getUniqueNumberLocked() {
- return mUniqueCounter++;
- }
-
- @GuardedBy("this")
- private static int[] filterEthTypeBlackList(int[] ethTypeBlackList) {
- ArrayList bl = new ArrayList();
-
- for (int p : ethTypeBlackList) {
- // Check if the protocol is a valid ether type
- if ((p < ETH_TYPE_MIN) || (p > ETH_TYPE_MAX)) {
- continue;
- }
-
- // Check if the protocol is not repeated in the passed array
- if (bl.contains(p)) {
- continue;
- }
-
- // Check if list reach its max size
- if (bl.size() == APF_MAX_ETH_TYPE_BLACK_LIST_LEN) {
- Log.w(TAG, "Passed EthType Black List size too large (" + bl.size() +
- ") using top " + APF_MAX_ETH_TYPE_BLACK_LIST_LEN + " protocols");
- break;
- }
-
- // Now add the protocol to the list
- bl.add(p);
- }
-
- return bl.stream().mapToInt(Integer::intValue).toArray();
- }
-
- /**
- * Attempt to start listening for RAs and, if RAs are received, generating and installing
- * filters to ignore useless RAs.
- */
- @VisibleForTesting
- void maybeStartFilter() {
- FileDescriptor socket;
- try {
- mHardwareAddress = mInterfaceParams.macAddr.toByteArray();
- synchronized(this) {
- // Clear the APF memory to reset all counters upon connecting to the first AP
- // in an SSID. This is limited to APFv4 devices because this large write triggers
- // a crash on some older devices (b/78905546).
- if (mApfCapabilities.hasDataAccess()) {
- byte[] zeroes = new byte[mApfCapabilities.maximumApfProgramSize];
- mIpClientCallback.installPacketFilter(zeroes);
- }
-
- // Install basic filters
- installNewProgramLocked();
- }
- socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
- SocketAddress addr = makePacketSocketAddress(
- (short) ETH_P_IPV6, mInterfaceParams.index);
- Os.bind(socket, addr);
- NetworkStackUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error starting filter", e);
- return;
- }
- mReceiveThread = new ReceiveThread(socket);
- mReceiveThread.start();
- }
-
- // Returns seconds since device boot.
- @VisibleForTesting
- protected long currentTimeSeconds() {
- return SystemClock.elapsedRealtime() / DateUtils.SECOND_IN_MILLIS;
- }
-
- public static class InvalidRaException extends Exception {
- public InvalidRaException(String m) {
- super(m);
- }
- }
-
- // A class to hold information about an RA.
- @VisibleForTesting
- class Ra {
- // From RFC4861:
- private static final int ICMP6_RA_HEADER_LEN = 16;
- private static final int ICMP6_RA_CHECKSUM_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 2;
- private static final int ICMP6_RA_CHECKSUM_LEN = 2;
- private static final int ICMP6_RA_OPTION_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
- private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 6;
- private static final int ICMP6_RA_ROUTER_LIFETIME_LEN = 2;
- // Prefix information option.
- private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
- private static final int ICMP6_PREFIX_OPTION_LEN = 32;
- private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
- private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN = 4;
- private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
- private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN = 4;
-
- // From RFC6106: Recursive DNS Server option
- private static final int ICMP6_RDNSS_OPTION_TYPE = 25;
- // From RFC6106: DNS Search List option
- private static final int ICMP6_DNSSL_OPTION_TYPE = 31;
-
- // From RFC4191: Route Information option
- private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
- // Above three options all have the same format:
- private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
- private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
-
- // Note: mPacket's position() cannot be assumed to be reset.
- private final ByteBuffer mPacket;
- // List of binary ranges that include the whole packet except the lifetimes.
- // Pairs consist of offset and length.
- private final ArrayList> mNonLifetimes =
- new ArrayList>();
- // Minimum lifetime in packet
- long mMinLifetime;
- // When the packet was last captured, in seconds since Unix Epoch
- long mLastSeen;
-
- // For debugging only. Offsets into the packet where PIOs are.
- private final ArrayList mPrefixOptionOffsets = new ArrayList<>();
-
- // For debugging only. Offsets into the packet where RDNSS options are.
- private final ArrayList mRdnssOptionOffsets = new ArrayList<>();
-
- // For debugging only. How many times this RA was seen.
- int seenCount = 0;
-
- // For debugging only. Returns the hex representation of the last matching packet.
- String getLastMatchingPacket() {
- return HexDump.toHexString(mPacket.array(), 0, mPacket.capacity(),
- false /* lowercase */);
- }
-
- // For debugging only. Returns the string representation of the IPv6 address starting at
- // position pos in the packet.
- private String IPv6AddresstoString(int pos) {
- try {
- byte[] array = mPacket.array();
- // Can't just call copyOfRange() and see if it throws, because if it reads past the
- // end it pads with zeros instead of throwing.
- if (pos < 0 || pos + 16 > array.length || pos + 16 < pos) {
- return "???";
- }
- byte[] addressBytes = Arrays.copyOfRange(array, pos, pos + 16);
- InetAddress address = (Inet6Address) InetAddress.getByAddress(addressBytes);
- return address.getHostAddress();
- } catch (UnsupportedOperationException e) {
- // array() failed. Cannot happen, mPacket is array-backed and read-write.
- return "???";
- } catch (ClassCastException|UnknownHostException e) {
- // Cannot happen.
- return "???";
- }
- }
-
- // Can't be static because it's in a non-static inner class.
- // TODO: Make this static once RA is its own class.
- private void prefixOptionToString(StringBuffer sb, int offset) {
- String prefix = IPv6AddresstoString(offset + 16);
- int length = getUint8(mPacket, offset + 2);
- long valid = getUint32(mPacket, offset + 4);
- long preferred = getUint32(mPacket, offset + 8);
- sb.append(String.format("%s/%d %ds/%ds ", prefix, length, valid, preferred));
- }
-
- private void rdnssOptionToString(StringBuffer sb, int offset) {
- int optLen = getUint8(mPacket, offset + 1) * 8;
- if (optLen < 24) return; // Malformed or empty.
- long lifetime = getUint32(mPacket, offset + 4);
- int numServers = (optLen - 8) / 16;
- sb.append("DNS ").append(lifetime).append("s");
- for (int server = 0; server < numServers; server++) {
- sb.append(" ").append(IPv6AddresstoString(offset + 8 + 16 * server));
- }
- }
-
- public String toString() {
- try {
- StringBuffer sb = new StringBuffer();
- sb.append(String.format("RA %s -> %s %ds ",
- IPv6AddresstoString(IPV6_SRC_ADDR_OFFSET),
- IPv6AddresstoString(IPV6_DEST_ADDR_OFFSET),
- getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET)));
- for (int i: mPrefixOptionOffsets) {
- prefixOptionToString(sb, i);
- }
- for (int i: mRdnssOptionOffsets) {
- rdnssOptionToString(sb, i);
- }
- return sb.toString();
- } catch (BufferUnderflowException|IndexOutOfBoundsException e) {
- return "";
- }
- }
-
- /**
- * Add a binary range of the packet that does not include a lifetime to mNonLifetimes.
- * Assumes mPacket.position() is as far as we've parsed the packet.
- * @param lastNonLifetimeStart offset within packet of where the last binary range of
- * data not including a lifetime.
- * @param lifetimeOffset offset from mPacket.position() to the next lifetime data.
- * @param lifetimeLength length of the next lifetime data.
- * @return offset within packet of where the next binary range of data not including
- * a lifetime. This can be passed into the next invocation of this function
- * via {@code lastNonLifetimeStart}.
- */
- private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset,
- int lifetimeLength) {
- lifetimeOffset += mPacket.position();
- mNonLifetimes.add(new Pair(lastNonLifetimeStart,
- lifetimeOffset - lastNonLifetimeStart));
- return lifetimeOffset + lifetimeLength;
- }
-
- private int addNonLifetimeU32(int lastNonLifetimeStart) {
- return addNonLifetime(lastNonLifetimeStart,
- ICMP6_4_BYTE_LIFETIME_OFFSET, ICMP6_4_BYTE_LIFETIME_LEN);
- }
-
- // Note that this parses RA and may throw InvalidRaException (from
- // Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException
- // (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
- // specifications.
- Ra(byte[] packet, int length) throws InvalidRaException {
- if (length < ICMP6_RA_OPTION_OFFSET) {
- throw new InvalidRaException("Not an ICMP6 router advertisement");
- }
-
- mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
- mLastSeen = currentTimeSeconds();
-
- // Sanity check packet in case a packet arrives before we attach RA filter
- // to our packet socket. b/29586253
- if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 ||
- getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 ||
- getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMPV6_ROUTER_ADVERTISEMENT) {
- throw new InvalidRaException("Not an ICMP6 router advertisement");
- }
-
-
- RaEvent.Builder builder = new RaEvent.Builder();
-
- // Ignore the flow label and low 4 bits of traffic class.
- int lastNonLifetimeStart = addNonLifetime(0,
- IPV6_FLOW_LABEL_OFFSET,
- IPV6_FLOW_LABEL_LEN);
-
- // Ignore the checksum.
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_RA_CHECKSUM_OFFSET,
- ICMP6_RA_CHECKSUM_LEN);
-
- // Parse router lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_RA_ROUTER_LIFETIME_OFFSET,
- ICMP6_RA_ROUTER_LIFETIME_LEN);
- builder.updateRouterLifetime(getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET));
-
- // Ensures that the RA is not truncated.
- mPacket.position(ICMP6_RA_OPTION_OFFSET);
- while (mPacket.hasRemaining()) {
- final int position = mPacket.position();
- final int optionType = getUint8(mPacket, position);
- final int optionLength = getUint8(mPacket, position + 1) * 8;
- long lifetime;
- switch (optionType) {
- case ICMP6_PREFIX_OPTION_TYPE:
- // Parse valid lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
- ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN);
- lifetime = getUint32(mPacket,
- position + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET);
- builder.updatePrefixValidLifetime(lifetime);
- // Parse preferred lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
- ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN);
- lifetime = getUint32(mPacket,
- position + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET);
- builder.updatePrefixPreferredLifetime(lifetime);
- mPrefixOptionOffsets.add(position);
- break;
- // These three options have the same lifetime offset and size, and
- // are processed with the same specialized addNonLifetimeU32:
- case ICMP6_RDNSS_OPTION_TYPE:
- mRdnssOptionOffsets.add(position);
- lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
- lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
- builder.updateRdnssLifetime(lifetime);
- break;
- case ICMP6_ROUTE_INFO_OPTION_TYPE:
- lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
- lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
- builder.updateRouteInfoLifetime(lifetime);
- break;
- case ICMP6_DNSSL_OPTION_TYPE:
- lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
- lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
- builder.updateDnsslLifetime(lifetime);
- break;
- default:
- // RFC4861 section 4.2 dictates we ignore unknown options for fowards
- // compatibility.
- break;
- }
- if (optionLength <= 0) {
- throw new InvalidRaException(String.format(
- "Invalid option length opt=%d len=%d", optionType, optionLength));
- }
- mPacket.position(position + optionLength);
- }
- // Mark non-lifetime bytes since last lifetime.
- addNonLifetime(lastNonLifetimeStart, 0, 0);
- mMinLifetime = minLifetime(packet, length);
- mMetricsLog.log(builder.build());
- }
-
- // Ignoring lifetimes (which may change) does {@code packet} match this RA?
- boolean matches(byte[] packet, int length) {
- if (length != mPacket.capacity()) return false;
- byte[] referencePacket = mPacket.array();
- for (Pair nonLifetime : mNonLifetimes) {
- for (int i = nonLifetime.first; i < (nonLifetime.first + nonLifetime.second); i++) {
- if (packet[i] != referencePacket[i]) return false;
- }
- }
- return true;
- }
-
- // What is the minimum of all lifetimes within {@code packet} in seconds?
- // Precondition: matches(packet, length) already returned true.
- long minLifetime(byte[] packet, int length) {
- long minLifetime = Long.MAX_VALUE;
- // Wrap packet in ByteBuffer so we can read big-endian values easily
- ByteBuffer byteBuffer = ByteBuffer.wrap(packet);
- for (int i = 0; (i + 1) < mNonLifetimes.size(); i++) {
- int offset = mNonLifetimes.get(i).first + mNonLifetimes.get(i).second;
-
- // The flow label is in mNonLifetimes, but it's not a lifetime.
- if (offset == IPV6_FLOW_LABEL_OFFSET) {
- continue;
- }
-
- // The checksum is in mNonLifetimes, but it's not a lifetime.
- if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
- continue;
- }
-
- final int lifetimeLength = mNonLifetimes.get(i+1).first - offset;
- final long optionLifetime;
- switch (lifetimeLength) {
- case 2:
- optionLifetime = getUint16(byteBuffer, offset);
- break;
- case 4:
- optionLifetime = getUint32(byteBuffer, offset);
- break;
- default:
- throw new IllegalStateException("bogus lifetime size " + lifetimeLength);
- }
- minLifetime = Math.min(minLifetime, optionLifetime);
- }
- return minLifetime;
- }
-
- // How many seconds does this RA's have to live, taking into account the fact
- // that we might have seen it a while ago.
- long currentLifetime() {
- return mMinLifetime - (currentTimeSeconds() - mLastSeen);
- }
-
- boolean isExpired() {
- // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll
- // have to calculate the filter lifetime specially as a fraction of 0 is still 0.
- return currentLifetime() <= 0;
- }
-
- // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped.
- // Jump to the next filter if packet doesn't match this RA.
- @GuardedBy("ApfFilter.this")
- long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- String nextFilterLabel = "Ra" + getUniqueNumberLocked();
- // Skip if packet is not the right size
- gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
- gen.addJumpIfR0NotEquals(mPacket.capacity(), nextFilterLabel);
- int filterLifetime = (int)(currentLifetime() / FRACTION_OF_LIFETIME_TO_FILTER);
- // Skip filter if expired
- gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
- gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel);
- for (int i = 0; i < mNonLifetimes.size(); i++) {
- // Generate code to match the packet bytes
- Pair nonLifetime = mNonLifetimes.get(i);
- // Don't generate JNEBS instruction for 0 bytes as it always fails the
- // ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm - 1) check where cmp_imm is
- // the number of bytes to compare. nonLifetime is zero between the
- // valid and preferred lifetimes in the prefix option.
- if (nonLifetime.second != 0) {
- gen.addLoadImmediate(Register.R0, nonLifetime.first);
- gen.addJumpIfBytesNotEqual(Register.R0,
- Arrays.copyOfRange(mPacket.array(), nonLifetime.first,
- nonLifetime.first + nonLifetime.second),
- nextFilterLabel);
- }
- // Generate code to test the lifetimes haven't gone down too far
- if ((i + 1) < mNonLifetimes.size()) {
- Pair nextNonLifetime = mNonLifetimes.get(i + 1);
- int offset = nonLifetime.first + nonLifetime.second;
-
- // Skip the Flow label.
- if (offset == IPV6_FLOW_LABEL_OFFSET) {
- continue;
- }
- // Skip the checksum.
- if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
- continue;
- }
- int length = nextNonLifetime.first - offset;
- switch (length) {
- case 4: gen.addLoad32(Register.R0, offset); break;
- case 2: gen.addLoad16(Register.R0, offset); break;
- default: throw new IllegalStateException("bogus lifetime size " + length);
- }
- gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel);
- }
- }
- maybeSetupCounter(gen, Counter.DROPPED_RA);
- gen.addJump(mCountAndDropLabel);
- gen.defineLabel(nextFilterLabel);
- return filterLifetime;
- }
- }
-
- // TODO: Refactor these subclasses to avoid so much repetition.
- private abstract static class KeepalivePacket {
- // Note that the offset starts from IP header.
- // These must be added ether header length when generating program.
- static final int IP_HEADER_OFFSET = 0;
- static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12;
-
- // Append a filter for this keepalive ack to {@code gen}.
- // Jump to drop if it matches the keepalive ack.
- // Jump to the next filter if packet doesn't match the keepalive ack.
- abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
- }
-
- // A class to hold NAT-T keepalive ack information.
- private class NattKeepaliveResponse extends KeepalivePacket {
- static final int UDP_LENGTH_OFFSET = 4;
- static final int UDP_HEADER_LEN = 8;
-
- protected class NattKeepaliveResponseData {
- public final byte[] srcAddress;
- public final int srcPort;
- public final byte[] dstAddress;
- public final int dstPort;
-
- NattKeepaliveResponseData(final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
- srcAddress = sentKeepalivePacket.dstAddress;
- srcPort = sentKeepalivePacket.dstPort;
- dstAddress = sentKeepalivePacket.srcAddress;
- dstPort = sentKeepalivePacket.srcPort;
- }
- }
-
- protected final NattKeepaliveResponseData mPacket;
- protected final byte[] mSrcDstAddr;
- protected final byte[] mPortFingerprint;
- // NAT-T keepalive packet
- protected final byte[] mPayload = {(byte) 0xff};
-
- NattKeepaliveResponse(final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
- mPacket = new NattKeepaliveResponseData(sentKeepalivePacket);
- mSrcDstAddr = concatArrays(mPacket.srcAddress, mPacket.dstAddress);
- mPortFingerprint = generatePortFingerprint(mPacket.srcPort, mPacket.dstPort);
- }
-
- byte[] generatePortFingerprint(int srcPort, int dstPort) {
- final ByteBuffer fp = ByteBuffer.allocate(4);
- fp.order(ByteOrder.BIG_ENDIAN);
- fp.putShort((short) srcPort);
- fp.putShort((short) dstPort);
- return fp.array();
- }
-
- @Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- final String nextFilterLabel = "natt_keepalive_filter" + getUniqueNumberLocked();
-
- gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel);
-
- // A NAT-T keepalive packet contains 1 byte payload with the value 0xff
- // Check payload length is 1
- gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addAdd(UDP_HEADER_LEN);
- gen.addSwap();
- gen.addLoad16(Register.R0, IPV4_TOTAL_LENGTH_OFFSET);
- gen.addNeg(Register.R1);
- gen.addAddR1();
- gen.addJumpIfR0NotEquals(1, nextFilterLabel);
-
- // Check that the ports match
- gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addAdd(ETH_HEADER_LEN);
- gen.addJumpIfBytesNotEqual(Register.R0, mPortFingerprint, nextFilterLabel);
-
- // Payload offset = R0 + UDP header length
- gen.addAdd(UDP_HEADER_LEN);
- gen.addJumpIfBytesNotEqual(Register.R0, mPayload, nextFilterLabel);
-
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_NATT_KEEPALIVE);
- gen.addJump(mCountAndDropLabel);
- gen.defineLabel(nextFilterLabel);
- }
-
- public String toString() {
- try {
- return String.format("%s -> %s",
- NetworkStackUtils.addressAndPortToString(
- InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort),
- NetworkStackUtils.addressAndPortToString(
- InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort));
- } catch (UnknownHostException e) {
- return "Unknown host";
- }
- }
- }
-
- // A class to hold TCP keepalive ack information.
- private abstract static class TcpKeepaliveAck extends KeepalivePacket {
- protected static class TcpKeepaliveAckData {
- public final byte[] srcAddress;
- public final int srcPort;
- public final byte[] dstAddress;
- public final int dstPort;
- public final int seq;
- public final int ack;
-
- // Create the characteristics of the ack packet from the sent keepalive packet.
- TcpKeepaliveAckData(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
- srcAddress = sentKeepalivePacket.dstAddress;
- srcPort = sentKeepalivePacket.dstPort;
- dstAddress = sentKeepalivePacket.srcAddress;
- dstPort = sentKeepalivePacket.srcPort;
- seq = sentKeepalivePacket.ack;
- ack = sentKeepalivePacket.seq + 1;
- }
- }
-
- protected final TcpKeepaliveAckData mPacket;
- protected final byte[] mSrcDstAddr;
- protected final byte[] mPortSeqAckFingerprint;
-
- TcpKeepaliveAck(final TcpKeepaliveAckData packet, final byte[] srcDstAddr) {
- mPacket = packet;
- mSrcDstAddr = srcDstAddr;
- mPortSeqAckFingerprint = generatePortSeqAckFingerprint(mPacket.srcPort,
- mPacket.dstPort, mPacket.seq, mPacket.ack);
- }
-
- static byte[] generatePortSeqAckFingerprint(int srcPort, int dstPort, int seq, int ack) {
- final ByteBuffer fp = ByteBuffer.allocate(12);
- fp.order(ByteOrder.BIG_ENDIAN);
- fp.putShort((short) srcPort);
- fp.putShort((short) dstPort);
- fp.putInt(seq);
- fp.putInt(ack);
- return fp.array();
- }
-
- public String toString() {
- try {
- return String.format("%s -> %s , seq=%d, ack=%d",
- NetworkStackUtils.addressAndPortToString(
- InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort),
- NetworkStackUtils.addressAndPortToString(
- InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort),
- Integer.toUnsignedLong(mPacket.seq),
- Integer.toUnsignedLong(mPacket.ack));
- } catch (UnknownHostException e) {
- return "Unknown host";
- }
- }
-
- // Append a filter for this keepalive ack to {@code gen}.
- // Jump to drop if it matches the keepalive ack.
- // Jump to the next filter if packet doesn't match the keepalive ack.
- abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
- }
-
- private class TcpKeepaliveAckV4 extends TcpKeepaliveAck {
-
- TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
- this(new TcpKeepaliveAckData(sentKeepalivePacket));
- }
- TcpKeepaliveAckV4(final TcpKeepaliveAckData packet) {
- super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
- }
-
- @Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked();
-
- gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel);
-
- // Skip to the next filter if it's not zero-sized :
- // TCP_HEADER_SIZE + IPV4_HEADER_SIZE - ipv4_total_length == 0
- // Load the IP header size into R1
- gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- // Load the TCP header size into R0 (it's indexed by R1)
- gen.addLoad8Indexed(Register.R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET);
- // Size offset is in the top nibble, but it must be multiplied by 4, and the two
- // top bits of the low nibble are guaranteed to be zeroes. Right-shift R0 by 2.
- gen.addRightShift(2);
- // R0 += R1 -> R0 contains TCP + IP headers length
- gen.addAddR1();
- // Load IPv4 total length
- gen.addLoad16(Register.R1, IPV4_TOTAL_LENGTH_OFFSET);
- gen.addNeg(Register.R0);
- gen.addAddR1();
- gen.addJumpIfR0NotEquals(0, nextFilterLabel);
- // Add IPv4 header length
- gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN);
- gen.addAddR1();
- gen.addJumpIfBytesNotEqual(Register.R0, mPortSeqAckFingerprint, nextFilterLabel);
-
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_KEEPALIVE_ACK);
- gen.addJump(mCountAndDropLabel);
- gen.defineLabel(nextFilterLabel);
- }
- }
-
- private class TcpKeepaliveAckV6 extends TcpKeepaliveAck {
- TcpKeepaliveAckV6(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
- this(new TcpKeepaliveAckData(sentKeepalivePacket));
- }
- TcpKeepaliveAckV6(final TcpKeepaliveAckData packet) {
- super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
- }
-
- @Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- throw new UnsupportedOperationException("IPv6 TCP Keepalive is not supported yet");
- }
- }
-
- // Maximum number of RAs to filter for.
- private static final int MAX_RAS = 10;
-
- @GuardedBy("this")
- private ArrayList mRas = new ArrayList<>();
- @GuardedBy("this")
- private SparseArray mKeepalivePackets = new SparseArray<>();
-
- // There is always some marginal benefit to updating the installed APF program when an RA is
- // seen because we can extend the program's lifetime slightly, but there is some cost to
- // updating the program, so don't bother unless the program is going to expire soon. This
- // constant defines "soon" in seconds.
- private static final long MAX_PROGRAM_LIFETIME_WORTH_REFRESHING = 30;
- // We don't want to filter an RA for it's whole lifetime as it'll be expired by the time we ever
- // see a refresh. Using half the lifetime might be a good idea except for the fact that
- // packets may be dropped, so let's use 6.
- private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6;
-
- // When did we last install a filter program? In seconds since Unix Epoch.
- @GuardedBy("this")
- private long mLastTimeInstalledProgram;
- // How long should the last installed filter program live for? In seconds.
- @GuardedBy("this")
- private long mLastInstalledProgramMinLifetime;
- @GuardedBy("this")
- private ApfProgramEvent.Builder mLastInstallEvent;
-
- // For debugging only. The last program installed.
- @GuardedBy("this")
- private byte[] mLastInstalledProgram;
-
- /**
- * For debugging only. Contains the latest APF buffer snapshot captured from the firmware.
- *
- * A typical size for this buffer is 4KB. It is present only if the WiFi HAL supports
- * IWifiStaIface#readApfPacketFilterData(), and the APF interpreter advertised support for
- * the opcodes to access the data buffer (LDDW and STDW).
- */
- @GuardedBy("this") @Nullable
- private byte[] mDataSnapshot;
-
- // How many times the program was updated since we started.
- @GuardedBy("this")
- private int mNumProgramUpdates = 0;
- // How many times the program was updated since we started for allowing multicast traffic.
- @GuardedBy("this")
- private int mNumProgramUpdatesAllowingMulticast = 0;
-
- /**
- * Generate filter code to process ARP packets. Execution of this code ends in either the
- * DROP_LABEL or PASS_LABEL and does not fall off the end.
- * Preconditions:
- * - Packet being filtered is ARP
- */
- @GuardedBy("this")
- private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- // Here's a basic summary of what the ARP filter program does:
- //
- // if not ARP IPv4
- // pass
- // if not ARP IPv4 reply or request
- // pass
- // if ARP reply source ip is 0.0.0.0
- // drop
- // if unicast ARP reply
- // pass
- // if interface has no IPv4 address
- // if target ip is 0.0.0.0
- // drop
- // else
- // if target ip is not the interface ip
- // drop
- // pass
-
- final String checkTargetIPv4 = "checkTargetIPv4";
-
- // Pass if not ARP IPv4.
- gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
- maybeSetupCounter(gen, Counter.PASSED_ARP_NON_IPV4);
- gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, mCountAndPassLabel);
-
- // Pass if unknown ARP opcode.
- gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET);
- gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check
- maybeSetupCounter(gen, Counter.PASSED_ARP_UNKNOWN);
- gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndPassLabel);
-
- // Drop if ARP reply source IP is 0.0.0.0
- gen.addLoad32(Register.R0, ARP_SOURCE_IP_ADDRESS_OFFSET);
- maybeSetupCounter(gen, Counter.DROPPED_ARP_REPLY_SPA_NO_HOST);
- gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
-
- // Pass if unicast reply.
- gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- maybeSetupCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
-
- // Either a unicast request, a unicast reply, or a broadcast reply.
- gen.defineLabel(checkTargetIPv4);
- if (mIPv4Address == null) {
- // When there is no IPv4 address, drop GARP replies (b/29404209).
- gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
- maybeSetupCounter(gen, Counter.DROPPED_GARP_REPLY);
- gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
- } else {
- // When there is an IPv4 address, drop unicast/broadcast requests
- // and broadcast replies with a different target IPv4 address.
- gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
- maybeSetupCounter(gen, Counter.DROPPED_ARP_OTHER_HOST);
- gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, mCountAndDropLabel);
- }
-
- maybeSetupCounter(gen, Counter.PASSED_ARP);
- gen.addJump(mCountAndPassLabel);
- }
-
- /**
- * Generate filter code to process IPv4 packets. Execution of this code ends in either the
- * DROP_LABEL or PASS_LABEL and does not fall off the end.
- * Preconditions:
- * - Packet being filtered is IPv4
- */
- @GuardedBy("this")
- private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- // Here's a basic summary of what the IPv4 filter program does:
- //
- // if filtering multicast (i.e. multicast lock not held):
- // if it's DHCP destined to our MAC:
- // pass
- // if it's L2 broadcast:
- // drop
- // if it's IPv4 multicast:
- // drop
- // if it's IPv4 broadcast:
- // drop
- // if keepalive ack
- // drop
- // pass
-
- if (mMulticastFilter) {
- final String skipDhcpv4Filter = "skip_dhcp_v4_filter";
-
- // Pass DHCP addressed to us.
- // Check it's UDP.
- gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
- gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipDhcpv4Filter);
- // Check it's not a fragment. This matches the BPF filter installed by the DHCP client.
- gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
- gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, skipDhcpv4Filter);
- // Check it's addressed to DHCP client port.
- gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
- gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, skipDhcpv4Filter);
- // Check it's DHCP to our MAC address.
- gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
- // NOTE: Relies on R1 containing IPv4 header offset.
- gen.addAddR1();
- gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter);
- maybeSetupCounter(gen, Counter.PASSED_DHCP);
- gen.addJump(mCountAndPassLabel);
-
- // Drop all multicasts/broadcasts.
- gen.defineLabel(skipDhcpv4Filter);
-
- // If IPv4 destination address is in multicast range, drop.
- gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
- gen.addAnd(0xf0);
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_MULTICAST);
- gen.addJumpIfR0Equals(0xe0, mCountAndDropLabel);
-
- // If IPv4 broadcast packet, drop regardless of L2 (b/30231088).
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR);
- gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET);
- gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, mCountAndDropLabel);
- if (mIPv4Address != null && mIPv4PrefixLength < 31) {
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET);
- int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength);
- gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
- }
-
- // If any TCP keepalive filter matches, drop
- generateV4KeepaliveFilters(gen);
-
- // If any NAT-T keepalive filter matches, drop
- generateV4NattKeepaliveFilters(gen);
-
- // Otherwise, this is an IPv4 unicast, pass
- // If L2 broadcast packet, drop.
- // TODO: can we invert this condition to fall through to the common pass case below?
- maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST);
- gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
- gen.addJump(mCountAndDropLabel);
- } else {
- generateV4KeepaliveFilters(gen);
- generateV4NattKeepaliveFilters(gen);
- }
-
- // Otherwise, pass
- maybeSetupCounter(gen, Counter.PASSED_IPV4);
- gen.addJump(mCountAndPassLabel);
- }
-
- private void generateKeepaliveFilters(ApfGenerator gen, Class> filterType, int proto,
- int offset, String label) throws IllegalInstructionException {
- final boolean haveKeepaliveResponses = NetworkStackUtils.any(mKeepalivePackets,
- ack -> filterType.isInstance(ack));
-
- // If no keepalive packets of this type
- if (!haveKeepaliveResponses) return;
-
- // If not the right proto, skip keepalive filters
- gen.addLoad8(Register.R0, offset);
- gen.addJumpIfR0NotEquals(proto, label);
-
- // Drop Keepalive responses
- for (int i = 0; i < mKeepalivePackets.size(); ++i) {
- final KeepalivePacket response = mKeepalivePackets.valueAt(i);
- if (filterType.isInstance(response)) response.generateFilterLocked(gen);
- }
-
- gen.defineLabel(label);
- }
-
- private void generateV4KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
- generateKeepaliveFilters(gen, TcpKeepaliveAckV4.class, IPPROTO_TCP, IPV4_PROTOCOL_OFFSET,
- "skip_v4_keepalive_filter");
- }
-
- private void generateV4NattKeepaliveFilters(ApfGenerator gen)
- throws IllegalInstructionException {
- generateKeepaliveFilters(gen, NattKeepaliveResponse.class,
- IPPROTO_UDP, IPV4_PROTOCOL_OFFSET, "skip_v4_nattkeepalive_filter");
- }
-
- /**
- * Generate filter code to process IPv6 packets. Execution of this code ends in either the
- * DROP_LABEL or PASS_LABEL, or falls off the end for ICMPv6 packets.
- * Preconditions:
- * - Packet being filtered is IPv6
- */
- @GuardedBy("this")
- private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- // Here's a basic summary of what the IPv6 filter program does:
- //
- // if we're dropping multicast
- // if it's not IPCMv6 or it's ICMPv6 but we're in doze mode:
- // if it's multicast:
- // drop
- // pass
- // if it's ICMPv6 RS to any:
- // drop
- // if it's ICMPv6 NA to ff02::1:
- // drop
- // if keepalive ack
- // drop
-
- gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
-
- // Drop multicast if the multicast filter is enabled.
- if (mMulticastFilter) {
- final String skipIPv6MulticastFilterLabel = "skipIPv6MulticastFilter";
- final String dropAllIPv6MulticastsLabel = "dropAllIPv6Multicast";
-
- // While in doze mode, drop ICMPv6 multicast pings, let the others pass.
- // While awake, let all ICMPv6 multicasts through.
- if (mInDozeMode) {
- // Not ICMPv6? -> Proceed to multicast filtering
- gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, dropAllIPv6MulticastsLabel);
-
- // ICMPv6 but not ECHO? -> Skip the multicast filter.
- // (ICMPv6 ECHO requests will go through the multicast filter below).
- gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
- gen.addJumpIfR0NotEquals(ICMPV6_ECHO_REQUEST_TYPE, skipIPv6MulticastFilterLabel);
- } else {
- gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIPv6MulticastFilterLabel);
- }
-
- // Drop all other packets sent to ff00::/8 (multicast prefix).
- gen.defineLabel(dropAllIPv6MulticastsLabel);
- maybeSetupCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST);
- gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
- gen.addJumpIfR0Equals(0xff, mCountAndDropLabel);
- // If any keepalive filter matches, drop
- generateV6KeepaliveFilters(gen);
- // Not multicast. Pass.
- maybeSetupCounter(gen, Counter.PASSED_IPV6_UNICAST_NON_ICMP);
- gen.addJump(mCountAndPassLabel);
- gen.defineLabel(skipIPv6MulticastFilterLabel);
- } else {
- generateV6KeepaliveFilters(gen);
- // If not ICMPv6, pass.
- maybeSetupCounter(gen, Counter.PASSED_IPV6_NON_ICMP);
- gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, mCountAndPassLabel);
- }
-
- // If we got this far, the packet is ICMPv6. Drop some specific types.
-
- // Add unsolicited multicast neighbor announcements filter
- String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
- gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
- // Drop all router solicitations (b/32833400)
- maybeSetupCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION);
- gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel);
- // If not neighbor announcements, skip filter.
- gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel);
- // If to ff02::1, drop.
- // TODO: Drop only if they don't contain the address of on-link neighbours.
- gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
- skipUnsolicitedMulticastNALabel);
- maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
- gen.addJump(mCountAndDropLabel);
- gen.defineLabel(skipUnsolicitedMulticastNALabel);
- }
-
- private void generateV6KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
- generateKeepaliveFilters(gen, TcpKeepaliveAckV6.class, IPPROTO_TCP, IPV6_NEXT_HEADER_OFFSET,
- "skip_v6_keepalive_filter");
- }
-
- /**
- * Begin generating an APF program to:
- *
- *
Drop/Pass 802.3 frames (based on policy)
- *
Drop packets with EtherType within the Black List
- *
Drop ARP requests not for us, if mIPv4Address is set,
- *
Drop IPv4 broadcast packets, except DHCP destined to our MAC,
- *
Drop IPv4 multicast packets, if mMulticastFilter,
- *
Pass all other IPv4 packets,
- *
Drop all broadcast non-IP non-ARP packets.
- *
Pass all non-ICMPv6 IPv6 packets,
- *
Pass all non-IPv4 and non-IPv6 packets,
- *
Drop IPv6 ICMPv6 NAs to ff02::1.
- *
Drop IPv6 ICMPv6 RSs.
- *
Filter IPv4 packets (see generateIPv4FilterLocked())
- *
Filter IPv6 packets (see generateIPv6FilterLocked())
- *
Let execution continue off the end of the program for IPv6 ICMPv6 packets. This allows
- * insertion of RA filters here, or if there aren't any, just passes the packets.
- *
- */
- @GuardedBy("this")
- private ApfGenerator emitPrologueLocked() throws IllegalInstructionException {
- // This is guaranteed to succeed because of the check in maybeCreate.
- ApfGenerator gen = new ApfGenerator(mApfCapabilities.apfVersionSupported);
-
- if (mApfCapabilities.hasDataAccess()) {
- // Increment TOTAL_PACKETS
- maybeSetupCounter(gen, Counter.TOTAL_PACKETS);
- gen.addLoadData(Register.R0, 0); // load counter
- gen.addAdd(1);
- gen.addStoreData(Register.R0, 0); // write-back counter
- }
-
- // Here's a basic summary of what the initial program does:
- //
- // if it's a 802.3 Frame (ethtype < 0x0600):
- // drop or pass based on configurations
- // if it has a ether-type that belongs to the black list
- // drop
- // if it's ARP:
- // insert ARP filter to drop or pass these appropriately
- // if it's IPv4:
- // insert IPv4 filter to drop or pass these appropriately
- // if it's not IPv6:
- // if it's broadcast:
- // drop
- // pass
- // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets
-
- gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET);
-
- if (mDrop802_3Frames) {
- // drop 802.3 frames (ethtype < 0x0600)
- maybeSetupCounter(gen, Counter.DROPPED_802_3_FRAME);
- gen.addJumpIfR0LessThan(ETH_TYPE_MIN, mCountAndDropLabel);
- }
-
- // Handle ether-type black list
- maybeSetupCounter(gen, Counter.DROPPED_ETHERTYPE_BLACKLISTED);
- for (int p : mEthTypeBlackList) {
- gen.addJumpIfR0Equals(p, mCountAndDropLabel);
- }
-
- // Add ARP filters:
- String skipArpFiltersLabel = "skipArpFilters";
- gen.addJumpIfR0NotEquals(ETH_P_ARP, skipArpFiltersLabel);
- generateArpFilterLocked(gen);
- gen.defineLabel(skipArpFiltersLabel);
-
- // Add IPv4 filters:
- String skipIPv4FiltersLabel = "skipIPv4Filters";
- // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
- // execute the ARP filter, since that filter does not fall through, but either drops or
- // passes.
- gen.addJumpIfR0NotEquals(ETH_P_IP, skipIPv4FiltersLabel);
- generateIPv4FilterLocked(gen);
- gen.defineLabel(skipIPv4FiltersLabel);
-
- // Check for IPv6:
- // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
- // execute the ARP or IPv4 filters, since those filters do not fall through, but either
- // drop or pass.
- String ipv6FilterLabel = "IPv6Filters";
- gen.addJumpIfR0Equals(ETH_P_IPV6, ipv6FilterLabel);
-
- // Drop non-IP non-ARP broadcasts, pass the rest
- gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- maybeSetupCounter(gen, Counter.PASSED_NON_IP_UNICAST);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
- maybeSetupCounter(gen, Counter.DROPPED_ETH_BROADCAST);
- gen.addJump(mCountAndDropLabel);
-
- // Add IPv6 filters:
- gen.defineLabel(ipv6FilterLabel);
- generateIPv6FilterLocked(gen);
- return gen;
- }
-
- /**
- * Append packet counting epilogue to the APF program.
- *
- * Currently, the epilogue consists of two trampolines which count passed and dropped packets
- * before jumping to the actual PASS and DROP labels.
- */
- @GuardedBy("this")
- private void emitEpilogue(ApfGenerator gen) throws IllegalInstructionException {
- // If APFv4 is unsupported, no epilogue is necessary: if execution reached this far, it
- // will just fall-through to the PASS label.
- if (!mApfCapabilities.hasDataAccess()) return;
-
- // Execution will reach the bottom of the program if none of the filters match,
- // which will pass the packet to the application processor.
- maybeSetupCounter(gen, Counter.PASSED_IPV6_ICMP);
-
- // Append the count & pass trampoline, which increments the counter at the data address
- // pointed to by R1, then jumps to the pass label. This saves a few bytes over inserting
- // the entire sequence inline for every counter.
- gen.defineLabel(mCountAndPassLabel);
- gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0)
- gen.addAdd(1); // R0++
- gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0
- gen.addJump(gen.PASS_LABEL);
-
- // Same as above for the count & drop trampoline.
- gen.defineLabel(mCountAndDropLabel);
- gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0)
- gen.addAdd(1); // R0++
- gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0
- gen.addJump(gen.DROP_LABEL);
- }
-
- /**
- * Generate and install a new filter program.
- */
- @GuardedBy("this")
- @VisibleForTesting
- void installNewProgramLocked() {
- purgeExpiredRasLocked();
- ArrayList rasToFilter = new ArrayList<>();
- final byte[] program;
- long programMinLifetime = Long.MAX_VALUE;
- long maximumApfProgramSize = mApfCapabilities.maximumApfProgramSize;
- if (mApfCapabilities.hasDataAccess()) {
- // Reserve space for the counters.
- maximumApfProgramSize -= Counter.totalSize();
- }
-
- try {
- // Step 1: Determine how many RA filters we can fit in the program.
- ApfGenerator gen = emitPrologueLocked();
-
- // The epilogue normally goes after the RA filters, but add it early to include its
- // length when estimating the total.
- emitEpilogue(gen);
-
- // Can't fit the program even without any RA filters?
- if (gen.programLengthOverEstimate() > maximumApfProgramSize) {
- Log.e(TAG, "Program exceeds maximum size " + maximumApfProgramSize);
- return;
- }
-
- for (Ra ra : mRas) {
- ra.generateFilterLocked(gen);
- // Stop if we get too big.
- if (gen.programLengthOverEstimate() > maximumApfProgramSize) break;
- rasToFilter.add(ra);
- }
-
- // Step 2: Actually generate the program
- gen = emitPrologueLocked();
- for (Ra ra : rasToFilter) {
- programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen));
- }
- emitEpilogue(gen);
- program = gen.generate();
- } catch (IllegalInstructionException|IllegalStateException e) {
- Log.e(TAG, "Failed to generate APF program.", e);
- return;
- }
- final long now = currentTimeSeconds();
- mLastTimeInstalledProgram = now;
- mLastInstalledProgramMinLifetime = programMinLifetime;
- mLastInstalledProgram = program;
- mNumProgramUpdates++;
-
- if (VDBG) {
- hexDump("Installing filter: ", program, program.length);
- }
- mIpClientCallback.installPacketFilter(program);
- logApfProgramEventLocked(now);
- mLastInstallEvent = new ApfProgramEvent.Builder()
- .setLifetime(programMinLifetime)
- .setFilteredRas(rasToFilter.size())
- .setCurrentRas(mRas.size())
- .setProgramLength(program.length)
- .setFlags(mIPv4Address != null, mMulticastFilter);
- }
-
- @GuardedBy("this")
- private void logApfProgramEventLocked(long now) {
- if (mLastInstallEvent == null) {
- return;
- }
- ApfProgramEvent.Builder ev = mLastInstallEvent;
- mLastInstallEvent = null;
- final long actualLifetime = now - mLastTimeInstalledProgram;
- ev.setActualLifetime(actualLifetime);
- if (actualLifetime < APF_PROGRAM_EVENT_LIFETIME_THRESHOLD) {
- return;
- }
- mMetricsLog.log(ev.build());
- }
-
- /**
- * Returns {@code true} if a new program should be installed because the current one dies soon.
- */
- private boolean shouldInstallnewProgram() {
- long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
- return expiry < currentTimeSeconds() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING;
- }
-
- private void hexDump(String msg, byte[] packet, int length) {
- log(msg + HexDump.toHexString(packet, 0, length, false /* lowercase */));
- }
-
- @GuardedBy("this")
- private void purgeExpiredRasLocked() {
- for (int i = 0; i < mRas.size();) {
- if (mRas.get(i).isExpired()) {
- log("Expiring " + mRas.get(i));
- mRas.remove(i);
- } else {
- i++;
- }
- }
- }
-
- /**
- * Process an RA packet, updating the list of known RAs and installing a new APF program
- * if the current APF program should be updated.
- * @return a ProcessRaResult enum describing what action was performed.
- */
- @VisibleForTesting
- synchronized ProcessRaResult processRa(byte[] packet, int length) {
- if (VDBG) hexDump("Read packet = ", packet, length);
-
- // Have we seen this RA before?
- for (int i = 0; i < mRas.size(); i++) {
- Ra ra = mRas.get(i);
- if (ra.matches(packet, length)) {
- if (VDBG) log("matched RA " + ra);
- // Update lifetimes.
- ra.mLastSeen = currentTimeSeconds();
- ra.mMinLifetime = ra.minLifetime(packet, length);
- ra.seenCount++;
-
- // Keep mRas in LRU order so as to prioritize generating filters for recently seen
- // RAs. LRU prioritizes this because RA filters are generated in order from mRas
- // until the filter program exceeds the maximum filter program size allowed by the
- // chipset, so RAs appearing earlier in mRas are more likely to make it into the
- // filter program.
- // TODO: consider sorting the RAs in order of increasing expiry time as well.
- // Swap to front of array.
- mRas.add(0, mRas.remove(i));
-
- // If the current program doesn't expire for a while, don't update.
- if (shouldInstallnewProgram()) {
- installNewProgramLocked();
- return ProcessRaResult.UPDATE_EXPIRY;
- }
- return ProcessRaResult.MATCH;
- }
- }
- purgeExpiredRasLocked();
- // TODO: figure out how to proceed when we've received more then MAX_RAS RAs.
- if (mRas.size() >= MAX_RAS) {
- return ProcessRaResult.DROPPED;
- }
- final Ra ra;
- try {
- ra = new Ra(packet, length);
- } catch (Exception e) {
- Log.e(TAG, "Error parsing RA", e);
- return ProcessRaResult.PARSE_ERROR;
- }
- // Ignore 0 lifetime RAs.
- if (ra.isExpired()) {
- return ProcessRaResult.ZERO_LIFETIME;
- }
- log("Adding " + ra);
- mRas.add(ra);
- installNewProgramLocked();
- return ProcessRaResult.UPDATE_NEW_RA;
- }
-
- /**
- * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet
- * filtering using APF programs.
- */
- public static ApfFilter maybeCreate(Context context, ApfConfiguration config,
- InterfaceParams ifParams, IpClientCallbacksWrapper ipClientCallback) {
- if (context == null || config == null || ifParams == null) return null;
- ApfCapabilities apfCapabilities = config.apfCapabilities;
- if (apfCapabilities == null) return null;
- if (apfCapabilities.apfVersionSupported == 0) return null;
- if (apfCapabilities.maximumApfProgramSize < 512) {
- Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize);
- return null;
- }
- // For now only support generating programs for Ethernet frames. If this restriction is
- // lifted:
- // 1. the program generator will need its offsets adjusted.
- // 2. the packet filter attached to our packet socket will need its offset adjusted.
- if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null;
- if (!ApfGenerator.supportsVersion(apfCapabilities.apfVersionSupported)) {
- Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
- return null;
- }
-
- return new ApfFilter(context, config, ifParams, ipClientCallback, new IpConnectivityLog());
- }
-
- public synchronized void shutdown() {
- if (mReceiveThread != null) {
- log("shutting down");
- mReceiveThread.halt(); // Also closes socket.
- mReceiveThread = null;
- }
- mRas.clear();
- mContext.unregisterReceiver(mDeviceIdleReceiver);
- }
-
- public synchronized void setMulticastFilter(boolean isEnabled) {
- if (mMulticastFilter == isEnabled) return;
- mMulticastFilter = isEnabled;
- if (!isEnabled) {
- mNumProgramUpdatesAllowingMulticast++;
- }
- installNewProgramLocked();
- }
-
- @VisibleForTesting
- public synchronized void setDozeMode(boolean isEnabled) {
- if (mInDozeMode == isEnabled) return;
- mInDozeMode = isEnabled;
- installNewProgramLocked();
- }
-
- /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */
- private static LinkAddress findIPv4LinkAddress(LinkProperties lp) {
- LinkAddress ipv4Address = null;
- for (LinkAddress address : lp.getLinkAddresses()) {
- if (!(address.getAddress() instanceof Inet4Address)) {
- continue;
- }
- if (ipv4Address != null && !ipv4Address.isSameAddressAs(address)) {
- // More than one IPv4 address, abort.
- return null;
- }
- ipv4Address = address;
- }
- return ipv4Address;
- }
-
- public synchronized void setLinkProperties(LinkProperties lp) {
- // NOTE: Do not keep a copy of LinkProperties as it would further duplicate state.
- final LinkAddress ipv4Address = findIPv4LinkAddress(lp);
- final byte[] addr = (ipv4Address != null) ? ipv4Address.getAddress().getAddress() : null;
- final int prefix = (ipv4Address != null) ? ipv4Address.getPrefixLength() : 0;
- if ((prefix == mIPv4PrefixLength) && Arrays.equals(addr, mIPv4Address)) {
- return;
- }
- mIPv4Address = addr;
- mIPv4PrefixLength = prefix;
- installNewProgramLocked();
- }
-
- /**
- * Add TCP keepalive ack packet filter.
- * This will add a filter to drop acks to the keepalive packet passed as an argument.
- *
- * @param slot The index used to access the filter.
- * @param sentKeepalivePacket The attributes of the sent keepalive packet.
- */
- public synchronized void addTcpKeepalivePacketFilter(final int slot,
- final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
- log("Adding keepalive ack(" + slot + ")");
- if (null != mKeepalivePackets.get(slot)) {
- throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied");
- }
- final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6;
- mKeepalivePackets.put(slot, (ipVersion == 4)
- ? new TcpKeepaliveAckV4(sentKeepalivePacket)
- : new TcpKeepaliveAckV6(sentKeepalivePacket));
- installNewProgramLocked();
- }
-
- /**
- * Add NAT-T keepalive packet filter.
- * This will add a filter to drop NAT-T keepalive packet which is passed as an argument.
- *
- * @param slot The index used to access the filter.
- * @param sentKeepalivePacket The attributes of the sent keepalive packet.
- */
- public synchronized void addNattKeepalivePacketFilter(final int slot,
- final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
- log("Adding NAT-T keepalive packet(" + slot + ")");
- if (null != mKeepalivePackets.get(slot)) {
- throw new IllegalArgumentException("NAT-T Keepalive slot " + slot + " is occupied");
- }
- if (sentKeepalivePacket.srcAddress.length != 4) {
- throw new IllegalArgumentException("NAT-T keepalive is only supported on IPv4");
- }
- mKeepalivePackets.put(slot, new NattKeepaliveResponse(sentKeepalivePacket));
- installNewProgramLocked();
- }
-
- /**
- * Remove keepalive packet filter.
- *
- * @param slot The index used to access the filter.
- */
- public synchronized void removeKeepalivePacketFilter(int slot) {
- log("Removing keepalive packet(" + slot + ")");
- mKeepalivePackets.remove(slot);
- installNewProgramLocked();
- }
-
- static public long counterValue(byte[] data, Counter counter)
- throws ArrayIndexOutOfBoundsException {
- // Follow the same wrap-around addressing scheme of the interpreter.
- int offset = counter.offset();
- if (offset < 0) {
- offset = data.length + offset;
- }
-
- // Decode 32bit big-endian integer into a long so we can count up beyond 2^31.
- long value = 0;
- for (int i = 0; i < 4; i++) {
- value = value << 8 | (data[offset] & 0xFF);
- offset++;
- }
- return value;
- }
-
- public synchronized void dump(IndentingPrintWriter pw) {
- pw.println("Capabilities: " + mApfCapabilities);
- pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED"));
- pw.println("Multicast: " + (mMulticastFilter ? "DROP" : "ALLOW"));
- try {
- pw.println("IPv4 address: " + InetAddress.getByAddress(mIPv4Address).getHostAddress());
- } catch (UnknownHostException|NullPointerException e) {}
-
- if (mLastTimeInstalledProgram == 0) {
- pw.println("No program installed.");
- return;
- }
- pw.println("Program updates: " + mNumProgramUpdates);
- pw.println(String.format(
- "Last program length %d, installed %ds ago, lifetime %ds",
- mLastInstalledProgram.length, currentTimeSeconds() - mLastTimeInstalledProgram,
- mLastInstalledProgramMinLifetime));
-
- pw.println("RA filters:");
- pw.increaseIndent();
- for (Ra ra: mRas) {
- pw.println(ra);
- pw.increaseIndent();
- pw.println(String.format(
- "Seen: %d, last %ds ago", ra.seenCount, currentTimeSeconds() - ra.mLastSeen));
- if (DBG) {
- pw.println("Last match:");
- pw.increaseIndent();
- pw.println(ra.getLastMatchingPacket());
- pw.decreaseIndent();
- }
- pw.decreaseIndent();
- }
- pw.decreaseIndent();
-
- pw.println("TCP Keepalive filters:");
- pw.increaseIndent();
- for (int i = 0; i < mKeepalivePackets.size(); ++i) {
- final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i);
- if (keepalivePacket instanceof TcpKeepaliveAck) {
- pw.print("Slot ");
- pw.print(mKeepalivePackets.keyAt(i));
- pw.print(": ");
- pw.println(keepalivePacket);
- }
- }
- pw.decreaseIndent();
-
- pw.println("NAT-T Keepalive filters:");
- pw.increaseIndent();
- for (int i = 0; i < mKeepalivePackets.size(); ++i) {
- final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i);
- if (keepalivePacket instanceof NattKeepaliveResponse) {
- pw.print("Slot ");
- pw.print(mKeepalivePackets.keyAt(i));
- pw.print(": ");
- pw.println(keepalivePacket);
- }
- }
- pw.decreaseIndent();
-
- if (DBG) {
- pw.println("Last program:");
- pw.increaseIndent();
- pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */));
- pw.decreaseIndent();
- }
-
- pw.println("APF packet counters: ");
- pw.increaseIndent();
- if (!mApfCapabilities.hasDataAccess()) {
- pw.println("APF counters not supported");
- } else if (mDataSnapshot == null) {
- pw.println("No last snapshot.");
- } else {
- try {
- Counter[] counters = Counter.class.getEnumConstants();
- for (Counter c : Arrays.asList(counters).subList(1, counters.length)) {
- long value = counterValue(mDataSnapshot, c);
- // Only print non-zero counters
- if (value != 0) {
- pw.println(c.toString() + ": " + value);
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- pw.println("Uh-oh: " + e);
- }
- if (VDBG) {
- pw.println("Raw data dump: ");
- pw.println(HexDump.dumpHexString(mDataSnapshot));
- }
- }
- pw.decreaseIndent();
- }
-
- // TODO: move to android.net.NetworkUtils
- @VisibleForTesting
- public static int ipv4BroadcastAddress(byte[] addrBytes, int prefixLength) {
- return bytesToBEInt(addrBytes) | (int) (Integer.toUnsignedLong(-1) >>> prefixLength);
- }
-
- private static int uint8(byte b) {
- return b & 0xff;
- }
-
- private static int getUint16(ByteBuffer buffer, int position) {
- return buffer.getShort(position) & 0xffff;
- }
-
- private static long getUint32(ByteBuffer buffer, int position) {
- return Integer.toUnsignedLong(buffer.getInt(position));
- }
-
- private static int getUint8(ByteBuffer buffer, int position) {
- return uint8(buffer.get(position));
- }
-
- private static int bytesToBEInt(byte[] bytes) {
- return (uint8(bytes[0]) << 24)
- + (uint8(bytes[1]) << 16)
- + (uint8(bytes[2]) << 8)
- + (uint8(bytes[3]));
- }
-
- private static byte[] concatArrays(final byte[]... arr) {
- int size = 0;
- for (byte[] a : arr) {
- size += a.length;
- }
- final byte[] result = new byte[size];
- int offset = 0;
- for (byte[] a : arr) {
- System.arraycopy(a, 0, result, offset, a.length);
- offset += a.length;
- }
- return result;
- }
-}
diff --git a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
deleted file mode 100644
index 44ce2db8547cc..0000000000000
--- a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
+++ /dev/null
@@ -1,937 +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.apf;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * APF assembler/generator. A tool for generating an APF program.
- *
- * Call add*() functions to add instructions to the program, then call
- * {@link generate} to get the APF bytecode for the program.
- *
- * @hide
- */
-public class ApfGenerator {
- /**
- * This exception is thrown when an attempt is made to generate an illegal instruction.
- */
- public static class IllegalInstructionException extends Exception {
- IllegalInstructionException(String msg) {
- super(msg);
- }
- }
- private enum Opcodes {
- LABEL(-1),
- LDB(1), // Load 1 byte from immediate offset, e.g. "ldb R0, [5]"
- LDH(2), // Load 2 bytes from immediate offset, e.g. "ldh R0, [5]"
- LDW(3), // Load 4 bytes from immediate offset, e.g. "ldw R0, [5]"
- LDBX(4), // Load 1 byte from immediate offset plus register, e.g. "ldbx R0, [5]R0"
- LDHX(5), // Load 2 byte from immediate offset plus register, e.g. "ldhx R0, [5]R0"
- LDWX(6), // Load 4 byte from immediate offset plus register, e.g. "ldwx R0, [5]R0"
- ADD(7), // Add, e.g. "add R0,5"
- MUL(8), // Multiply, e.g. "mul R0,5"
- DIV(9), // Divide, e.g. "div R0,5"
- AND(10), // And, e.g. "and R0,5"
- OR(11), // Or, e.g. "or R0,5"
- SH(12), // Left shift, e.g, "sh R0, 5" or "sh R0, -5" (shifts right)
- LI(13), // Load immediate, e.g. "li R0,5" (immediate encoded as signed value)
- JMP(14), // Jump, e.g. "jmp label"
- JEQ(15), // Compare equal and branch, e.g. "jeq R0,5,label"
- JNE(16), // Compare not equal and branch, e.g. "jne R0,5,label"
- JGT(17), // Compare greater than and branch, e.g. "jgt R0,5,label"
- JLT(18), // Compare less than and branch, e.g. "jlt R0,5,label"
- JSET(19), // Compare any bits set and branch, e.g. "jset R0,5,label"
- JNEBS(20), // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455"
- EXT(21), // Followed by immediate indicating ExtendedOpcodes.
- LDDW(22), // Load 4 bytes from data memory address (register + immediate): "lddw R0, [5]R1"
- STDW(23); // Store 4 bytes to data memory address (register + immediate): "stdw R0, [5]R1"
-
- final int value;
-
- private Opcodes(int value) {
- this.value = value;
- }
- }
- // Extended opcodes. Primary opcode is Opcodes.EXT. ExtendedOpcodes are encoded in the immediate
- // field.
- private enum ExtendedOpcodes {
- LDM(0), // Load from memory, e.g. "ldm R0,5"
- STM(16), // Store to memory, e.g. "stm R0,5"
- NOT(32), // Not, e.g. "not R0"
- NEG(33), // Negate, e.g. "neg R0"
- SWAP(34), // Swap, e.g. "swap R0,R1"
- MOVE(35); // Move, e.g. "move R0,R1"
-
- final int value;
-
- private ExtendedOpcodes(int value) {
- this.value = value;
- }
- }
- public enum Register {
- R0(0),
- R1(1);
-
- final int value;
-
- private Register(int value) {
- this.value = value;
- }
- }
- private class Instruction {
- private final byte mOpcode; // A "Opcode" value.
- private final byte mRegister; // A "Register" value.
- private boolean mHasImm;
- private byte mImmSize;
- private boolean mImmSigned;
- private int mImm;
- // When mOpcode is a jump:
- private byte mTargetLabelSize;
- private String mTargetLabel;
- // When mOpcode == Opcodes.LABEL:
- private String mLabel;
- // When mOpcode == Opcodes.JNEBS:
- private byte[] mCompareBytes;
- // Offset in bytes from the beginning of this program. Set by {@link ApfGenerator#generate}.
- int offset;
-
- Instruction(Opcodes opcode, Register register) {
- mOpcode = (byte)opcode.value;
- mRegister = (byte)register.value;
- }
-
- Instruction(Opcodes opcode) {
- this(opcode, Register.R0);
- }
-
- void setImm(int imm, boolean signed) {
- mHasImm = true;
- mImm = imm;
- mImmSigned = signed;
- mImmSize = calculateImmSize(imm, signed);
- }
-
- void setUnsignedImm(int imm) {
- setImm(imm, false);
- }
-
- void setSignedImm(int imm) {
- setImm(imm, true);
- }
-
- void setLabel(String label) throws IllegalInstructionException {
- if (mLabels.containsKey(label)) {
- throw new IllegalInstructionException("duplicate label " + label);
- }
- if (mOpcode != Opcodes.LABEL.value) {
- throw new IllegalStateException("adding label to non-label instruction");
- }
- mLabel = label;
- mLabels.put(label, this);
- }
-
- void setTargetLabel(String label) {
- mTargetLabel = label;
- mTargetLabelSize = 4; // May shrink later on in generate().
- }
-
- void setCompareBytes(byte[] bytes) {
- if (mOpcode != Opcodes.JNEBS.value) {
- throw new IllegalStateException("adding compare bytes to non-JNEBS instruction");
- }
- mCompareBytes = bytes;
- }
-
- /**
- * @return size of instruction in bytes.
- */
- int size() {
- if (mOpcode == Opcodes.LABEL.value) {
- return 0;
- }
- int size = 1;
- if (mHasImm) {
- size += generatedImmSize();
- }
- if (mTargetLabel != null) {
- size += generatedImmSize();
- }
- if (mCompareBytes != null) {
- size += mCompareBytes.length;
- }
- return size;
- }
-
- /**
- * Resize immediate value field so that it's only as big as required to
- * contain the offset of the jump destination.
- * @return {@code true} if shrunk.
- */
- boolean shrink() throws IllegalInstructionException {
- if (mTargetLabel == null) {
- return false;
- }
- int oldSize = size();
- int oldTargetLabelSize = mTargetLabelSize;
- mTargetLabelSize = calculateImmSize(calculateTargetLabelOffset(), false);
- if (mTargetLabelSize > oldTargetLabelSize) {
- throw new IllegalStateException("instruction grew");
- }
- return size() < oldSize;
- }
-
- /**
- * Assemble value for instruction size field.
- */
- private byte generateImmSizeField() {
- byte immSize = generatedImmSize();
- // Encode size field to fit in 2 bits: 0->0, 1->1, 2->2, 3->4.
- return immSize == 4 ? 3 : immSize;
- }
-
- /**
- * Assemble first byte of generated instruction.
- */
- private byte generateInstructionByte() {
- byte sizeField = generateImmSizeField();
- return (byte)((mOpcode << 3) | (sizeField << 1) | mRegister);
- }
-
- /**
- * Write {@code value} at offset {@code writingOffset} into {@code bytecode}.
- * {@link generatedImmSize} bytes are written. {@code value} is truncated to
- * {@code generatedImmSize} bytes. {@code value} is treated simply as a
- * 32-bit value, so unsigned values should be zero extended and the truncation
- * should simply throw away their zero-ed upper bits, and signed values should
- * be sign extended and the truncation should simply throw away their signed
- * upper bits.
- */
- private int writeValue(int value, byte[] bytecode, int writingOffset) {
- for (int i = generatedImmSize() - 1; i >= 0; i--) {
- bytecode[writingOffset++] = (byte)((value >> (i * 8)) & 255);
- }
- return writingOffset;
- }
-
- /**
- * Generate bytecode for this instruction at offset {@link offset}.
- */
- void generate(byte[] bytecode) throws IllegalInstructionException {
- if (mOpcode == Opcodes.LABEL.value) {
- return;
- }
- int writingOffset = offset;
- bytecode[writingOffset++] = generateInstructionByte();
- if (mTargetLabel != null) {
- writingOffset = writeValue(calculateTargetLabelOffset(), bytecode, writingOffset);
- }
- if (mHasImm) {
- writingOffset = writeValue(mImm, bytecode, writingOffset);
- }
- if (mCompareBytes != null) {
- System.arraycopy(mCompareBytes, 0, bytecode, writingOffset, mCompareBytes.length);
- writingOffset += mCompareBytes.length;
- }
- if ((writingOffset - offset) != size()) {
- throw new IllegalStateException("wrote " + (writingOffset - offset) +
- " but should have written " + size());
- }
- }
-
- /**
- * Calculate the size of either the immediate field or the target label field, if either is
- * present. Most instructions have either an immediate or a target label field, but for the
- * instructions that have both, the size of the target label field must be the same as the
- * size of the immediate field, because there is only one length field in the instruction
- * byte, hence why this function simply takes the maximum of the two sizes, so neither is
- * truncated.
- */
- private byte generatedImmSize() {
- return mImmSize > mTargetLabelSize ? mImmSize : mTargetLabelSize;
- }
-
- private int calculateTargetLabelOffset() throws IllegalInstructionException {
- Instruction targetLabelInstruction;
- if (mTargetLabel == DROP_LABEL) {
- targetLabelInstruction = mDropLabel;
- } else if (mTargetLabel == PASS_LABEL) {
- targetLabelInstruction = mPassLabel;
- } else {
- targetLabelInstruction = mLabels.get(mTargetLabel);
- }
- if (targetLabelInstruction == null) {
- throw new IllegalInstructionException("label not found: " + mTargetLabel);
- }
- // Calculate distance from end of this instruction to instruction.offset.
- final int targetLabelOffset = targetLabelInstruction.offset - (offset + size());
- if (targetLabelOffset < 0) {
- throw new IllegalInstructionException("backward branches disallowed; label: " +
- mTargetLabel);
- }
- return targetLabelOffset;
- }
-
- private byte calculateImmSize(int imm, boolean signed) {
- if (imm == 0) {
- return 0;
- }
- if (signed && (imm >= -128 && imm <= 127) ||
- !signed && (imm >= 0 && imm <= 255)) {
- return 1;
- }
- if (signed && (imm >= -32768 && imm <= 32767) ||
- !signed && (imm >= 0 && imm <= 65535)) {
- return 2;
- }
- return 4;
- }
- }
-
- /**
- * Jump to this label to terminate the program and indicate the packet
- * should be dropped.
- */
- public static final String DROP_LABEL = "__DROP__";
-
- /**
- * Jump to this label to terminate the program and indicate the packet
- * should be passed to the AP.
- */
- public static final String PASS_LABEL = "__PASS__";
-
- /**
- * Number of memory slots available for access via APF stores to memory and loads from memory.
- * The memory slots are numbered 0 to {@code MEMORY_SLOTS} - 1. This must be kept in sync with
- * the APF interpreter.
- */
- public static final int MEMORY_SLOTS = 16;
-
- /**
- * Memory slot number that is prefilled with the IPv4 header length.
- * Note that this memory slot may be overwritten by a program that
- * executes stores to this memory slot. This must be kept in sync with
- * the APF interpreter.
- */
- public static final int IPV4_HEADER_SIZE_MEMORY_SLOT = 13;
-
- /**
- * Memory slot number that is prefilled with the size of the packet being filtered in bytes.
- * Note that this memory slot may be overwritten by a program that
- * executes stores to this memory slot. This must be kept in sync with the APF interpreter.
- */
- public static final int PACKET_SIZE_MEMORY_SLOT = 14;
-
- /**
- * Memory slot number that is prefilled with the age of the filter in seconds. The age of the
- * filter is the time since the filter was installed until now.
- * Note that this memory slot may be overwritten by a program that
- * executes stores to this memory slot. This must be kept in sync with the APF interpreter.
- */
- public static final int FILTER_AGE_MEMORY_SLOT = 15;
-
- /**
- * First memory slot containing prefilled values. Can be used in range comparisons to determine
- * if memory slot index is within prefilled slots.
- */
- public static final int FIRST_PREFILLED_MEMORY_SLOT = IPV4_HEADER_SIZE_MEMORY_SLOT;
-
- /**
- * Last memory slot containing prefilled values. Can be used in range comparisons to determine
- * if memory slot index is within prefilled slots.
- */
- public static final int LAST_PREFILLED_MEMORY_SLOT = FILTER_AGE_MEMORY_SLOT;
-
- // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
- private static final int MIN_APF_VERSION = 2;
-
- private final ArrayList mInstructions = new ArrayList();
- private final HashMap mLabels = new HashMap();
- private final Instruction mDropLabel = new Instruction(Opcodes.LABEL);
- private final Instruction mPassLabel = new Instruction(Opcodes.LABEL);
- private final int mVersion;
- private boolean mGenerated;
-
- /**
- * Creates an ApfGenerator instance which is able to emit instructions for the specified
- * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if
- * the requested version is unsupported.
- */
- ApfGenerator(int version) throws IllegalInstructionException {
- mVersion = version;
- requireApfVersion(MIN_APF_VERSION);
- }
-
- /**
- * Returns true if the ApfGenerator supports the specified {@code version}, otherwise false.
- */
- public static boolean supportsVersion(int version) {
- return version >= MIN_APF_VERSION;
- }
-
- private void requireApfVersion(int minimumVersion) throws IllegalInstructionException {
- if (mVersion < minimumVersion) {
- throw new IllegalInstructionException("Requires APF >= " + minimumVersion);
- }
- }
-
- private void addInstruction(Instruction instruction) {
- if (mGenerated) {
- throw new IllegalStateException("Program already generated");
- }
- mInstructions.add(instruction);
- }
-
- /**
- * Define a label at the current end of the program. Jumps can jump to this label. Labels are
- * their own separate instructions, though with size 0. This facilitates having labels with
- * no corresponding code to execute, for example a label at the end of a program. For example
- * an {@link ApfGenerator} might be passed to a function that adds a filter like so:
- *
- * load from packet
- * compare loaded data, jump if not equal to "next_filter"
- * load from packet
- * compare loaded data, jump if not equal to "next_filter"
- * jump to drop label
- * define "next_filter" here
- *
- * In this case "next_filter" may not have any generated code associated with it.
- */
- public ApfGenerator defineLabel(String name) throws IllegalInstructionException {
- Instruction instruction = new Instruction(Opcodes.LABEL);
- instruction.setLabel(name);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an unconditional jump instruction to the end of the program.
- */
- public ApfGenerator addJump(String target) {
- Instruction instruction = new Instruction(Opcodes.JMP);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load the byte at offset {@code offset}
- * bytes from the beginning of the packet into {@code register}.
- */
- public ApfGenerator addLoad8(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDB, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 16-bits at offset {@code offset}
- * bytes from the beginning of the packet into {@code register}.
- */
- public ApfGenerator addLoad16(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDH, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 32-bits at offset {@code offset}
- * bytes from the beginning of the packet into {@code register}.
- */
- public ApfGenerator addLoad32(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDW, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load a byte from the packet into
- * {@code register}. The offset of the loaded byte from the beginning of the packet is
- * the sum of {@code offset} and the value in register R1.
- */
- public ApfGenerator addLoad8Indexed(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDBX, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 16-bits from the packet into
- * {@code register}. The offset of the loaded 16-bits from the beginning of the packet is
- * the sum of {@code offset} and the value in register R1.
- */
- public ApfGenerator addLoad16Indexed(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDHX, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 32-bits from the packet into
- * {@code register}. The offset of the loaded 32-bits from the beginning of the packet is
- * the sum of {@code offset} and the value in register R1.
- */
- public ApfGenerator addLoad32Indexed(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDWX, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to add {@code value} to register R0.
- */
- public ApfGenerator addAdd(int value) {
- Instruction instruction = new Instruction(Opcodes.ADD);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to multiply register R0 by {@code value}.
- */
- public ApfGenerator addMul(int value) {
- Instruction instruction = new Instruction(Opcodes.MUL);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to divide register R0 by {@code value}.
- */
- public ApfGenerator addDiv(int value) {
- Instruction instruction = new Instruction(Opcodes.DIV);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically and register R0 with {@code value}.
- */
- public ApfGenerator addAnd(int value) {
- Instruction instruction = new Instruction(Opcodes.AND);
- instruction.setUnsignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically or register R0 with {@code value}.
- */
- public ApfGenerator addOr(int value) {
- Instruction instruction = new Instruction(Opcodes.OR);
- instruction.setUnsignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to shift left register R0 by {@code value} bits.
- */
- public ApfGenerator addLeftShift(int value) {
- Instruction instruction = new Instruction(Opcodes.SH);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to shift right register R0 by {@code value}
- * bits.
- */
- public ApfGenerator addRightShift(int value) {
- Instruction instruction = new Instruction(Opcodes.SH);
- instruction.setSignedImm(-value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to add register R1 to register R0.
- */
- public ApfGenerator addAddR1() {
- Instruction instruction = new Instruction(Opcodes.ADD, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to multiply register R0 by register R1.
- */
- public ApfGenerator addMulR1() {
- Instruction instruction = new Instruction(Opcodes.MUL, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to divide register R0 by register R1.
- */
- public ApfGenerator addDivR1() {
- Instruction instruction = new Instruction(Opcodes.DIV, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically and register R0 with register R1
- * and store the result back into register R0.
- */
- public ApfGenerator addAndR1() {
- Instruction instruction = new Instruction(Opcodes.AND, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically or register R0 with register R1
- * and store the result back into register R0.
- */
- public ApfGenerator addOrR1() {
- Instruction instruction = new Instruction(Opcodes.OR, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to shift register R0 left by the value in
- * register R1.
- */
- public ApfGenerator addLeftShiftR1() {
- Instruction instruction = new Instruction(Opcodes.SH, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to move {@code value} into {@code register}.
- */
- public ApfGenerator addLoadImmediate(Register register, int value) {
- Instruction instruction = new Instruction(Opcodes.LI, register);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value equals {@code value}.
- */
- public ApfGenerator addJumpIfR0Equals(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JEQ);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value does not equal {@code value}.
- */
- public ApfGenerator addJumpIfR0NotEquals(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JNE);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is greater than {@code value}.
- */
- public ApfGenerator addJumpIfR0GreaterThan(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JGT);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is less than {@code value}.
- */
- public ApfGenerator addJumpIfR0LessThan(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JLT);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value has any bits set that are also set in {@code value}.
- */
- public ApfGenerator addJumpIfR0AnyBitsSet(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JSET);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value equals register R1's value.
- */
- public ApfGenerator addJumpIfR0EqualsR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JEQ, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value does not equal register R1's value.
- */
- public ApfGenerator addJumpIfR0NotEqualsR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JNE, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is greater than register R1's value.
- */
- public ApfGenerator addJumpIfR0GreaterThanR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JGT, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is less than register R1's value.
- */
- public ApfGenerator addJumpIfR0LessThanR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JLT, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value has any bits set that are also set in R1's value.
- */
- public ApfGenerator addJumpIfR0AnyBitsSetR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JSET, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if the bytes of the
- * packet at an offset specified by {@code register} match {@code bytes}.
- */
- public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target)
- throws IllegalInstructionException {
- if (register == Register.R1) {
- throw new IllegalInstructionException("JNEBS fails with R1");
- }
- Instruction instruction = new Instruction(Opcodes.JNEBS, register);
- instruction.setUnsignedImm(bytes.length);
- instruction.setTargetLabel(target);
- instruction.setCompareBytes(bytes);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load memory slot {@code slot} into
- * {@code register}.
- */
- public ApfGenerator addLoadFromMemory(Register register, int slot)
- throws IllegalInstructionException {
- if (slot < 0 || slot > (MEMORY_SLOTS - 1)) {
- throw new IllegalInstructionException("illegal memory slot number: " + slot);
- }
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.LDM.value + slot);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to store {@code register} into memory slot
- * {@code slot}.
- */
- public ApfGenerator addStoreToMemory(Register register, int slot)
- throws IllegalInstructionException {
- if (slot < 0 || slot > (MEMORY_SLOTS - 1)) {
- throw new IllegalInstructionException("illegal memory slot number: " + slot);
- }
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.STM.value + slot);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically not {@code register}.
- */
- public ApfGenerator addNot(Register register) {
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.NOT.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to negate {@code register}.
- */
- public ApfGenerator addNeg(Register register) {
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.NEG.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to swap the values in register R0 and register R1.
- */
- public ApfGenerator addSwap() {
- Instruction instruction = new Instruction(Opcodes.EXT);
- instruction.setUnsignedImm(ExtendedOpcodes.SWAP.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to move the value into
- * {@code register} from the other register.
- */
- public ApfGenerator addMove(Register register) {
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.MOVE.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 32 bits from the data memory into
- * {@code register}. The source address is computed by adding the signed immediate
- * @{code offset} to the other register.
- * Requires APF v3 or greater.
- */
- public ApfGenerator addLoadData(Register destinationRegister, int offset)
- throws IllegalInstructionException {
- requireApfVersion(3);
- Instruction instruction = new Instruction(Opcodes.LDDW, destinationRegister);
- instruction.setSignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to store 32 bits from {@code register} into the
- * data memory. The destination address is computed by adding the signed immediate
- * @{code offset} to the other register.
- * Requires APF v3 or greater.
- */
- public ApfGenerator addStoreData(Register sourceRegister, int offset)
- throws IllegalInstructionException {
- requireApfVersion(3);
- Instruction instruction = new Instruction(Opcodes.STDW, sourceRegister);
- instruction.setSignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Updates instruction offset fields using latest instruction sizes.
- * @return current program length in bytes.
- */
- private int updateInstructionOffsets() {
- int offset = 0;
- for (Instruction instruction : mInstructions) {
- instruction.offset = offset;
- offset += instruction.size();
- }
- return offset;
- }
-
- /**
- * Returns an overestimate of the size of the generated program. {@link #generate} may return
- * a program that is smaller.
- */
- public int programLengthOverEstimate() {
- return updateInstructionOffsets();
- }
-
- /**
- * Generate the bytecode for the APF program.
- * @return the bytecode.
- * @throws IllegalStateException if a label is referenced but not defined.
- */
- public byte[] generate() throws IllegalInstructionException {
- // Enforce that we can only generate once because we cannot unshrink instructions and
- // PASS/DROP labels may move further away requiring unshrinking if we add further
- // instructions.
- if (mGenerated) {
- throw new IllegalStateException("Can only generate() once!");
- }
- mGenerated = true;
- int total_size;
- boolean shrunk;
- // Shrink the immediate value fields of instructions.
- // As we shrink the instructions some branch offset
- // fields may shrink also, thereby shrinking the
- // instructions further. Loop until we've reached the
- // minimum size. Rarely will this loop more than a few times.
- // Limit iterations to avoid O(n^2) behavior.
- int iterations_remaining = 10;
- do {
- total_size = updateInstructionOffsets();
- // Update drop and pass label offsets.
- mDropLabel.offset = total_size + 1;
- mPassLabel.offset = total_size;
- // Limit run-time in aberant circumstances.
- if (iterations_remaining-- == 0) break;
- // Attempt to shrink instructions.
- shrunk = false;
- for (Instruction instruction : mInstructions) {
- if (instruction.shrink()) {
- shrunk = true;
- }
- }
- } while (shrunk);
- // Generate bytecode for instructions.
- byte[] bytecode = new byte[total_size];
- for (Instruction instruction : mInstructions) {
- instruction.generate(bytecode);
- }
- return bytecode;
- }
-}
-
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpAckPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpAckPacket.java
deleted file mode 100644
index b2eb4e21b5918..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpAckPacket.java
+++ /dev/null
@@ -1,88 +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 android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-ACK packet.
- */
-class DhcpAckPacket extends DhcpPacket {
-
- /**
- * The address of the server which sent this packet.
- */
- private final Inet4Address mSrcIp;
-
- DhcpAckPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address relayIp, Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, serverAddress, relayIp, clientMac, broadcast);
- mBroadcast = broadcast;
- mSrcIp = serverAddress;
- }
-
- public String toString() {
- String s = super.toString();
- String dnsServers = " DNS servers: ";
-
- for (Inet4Address dnsServer: mDnsServers) {
- dnsServers += dnsServer.toString() + " ";
- }
-
- return s + " ACK: your new IP " + mYourIp +
- ", netmask " + mSubnetMask +
- ", gateways " + mGateways + dnsServers +
- ", lease time " + mLeaseTime;
- }
-
- /**
- * Fills in a packet with the requested ACK parameters.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- Inet4Address destIp = mBroadcast ? INADDR_BROADCAST : mYourIp;
- Inet4Address srcIp = mBroadcast ? INADDR_ANY : mSrcIp;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
- DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated ACK packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_ACK);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
-
- addCommonServerTlvs(buffer);
- addTlvEnd(buffer);
- }
-
- /**
- * Un-boxes an Integer, returning 0 if a null reference is supplied.
- */
- private static final int getInt(Integer v) {
- if (v == null) {
- return 0;
- } else {
- return v.intValue();
- }
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
deleted file mode 100644
index ca6c17a65e997..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
+++ /dev/null
@@ -1,1070 +0,0 @@
-/*
- * Copyright (C) 2015 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.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
-import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
-import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
-import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_MTU;
-import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
-import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
-import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
-import static android.net.dhcp.DhcpPacket.INADDR_ANY;
-import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
-import static android.net.util.NetworkStackUtils.closeSocketQuietly;
-import static android.net.util.SocketUtils.makePacketSocketAddress;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ETH_P_IP;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_RAW;
-import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BROADCAST;
-import static android.system.OsConstants.SO_RCVBUF;
-import static android.system.OsConstants.SO_REUSEADDR;
-
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
-
-import android.content.Context;
-import android.net.DhcpResults;
-import android.net.InetAddresses;
-import android.net.TrafficStats;
-import android.net.ip.IpClient;
-import android.net.metrics.DhcpClientEvent;
-import android.net.metrics.DhcpErrorEvent;
-import android.net.metrics.IpConnectivityLog;
-import android.net.util.InterfaceParams;
-import android.net.util.NetworkStackUtils;
-import android.net.util.SocketUtils;
-import android.os.Message;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.util.HexDump;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.TrafficStatsConstants;
-import com.android.internal.util.WakeupMessage;
-import com.android.networkstack.R;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Random;
-
-/**
- * A DHCPv4 client.
- *
- * Written to behave similarly to the DhcpStateMachine + dhcpcd 5.5.6 combination used in Android
- * 5.1 and below, as configured on Nexus 6. The interface is the same as DhcpStateMachine.
- *
- * TODO:
- *
- * - Exponential backoff when receiving NAKs (not specified by the RFC, but current behaviour).
- * - Support persisting lease state and support INIT-REBOOT. Android 5.1 does this, but it does not
- * do so correctly: instead of requesting the lease last obtained on a particular network (e.g., a
- * given SSID), it requests the last-leased IP address on the same interface, causing a delay if
- * the server NAKs or a timeout if it doesn't.
- *
- * Known differences from current behaviour:
- *
- * - Does not request the "static routes" option.
- * - Does not support BOOTP servers. DHCP has been around since 1993, should be everywhere now.
- * - Requests the "broadcast" option, but does nothing with it.
- * - Rejects invalid subnet masks such as 255.255.255.1 (current code treats that as 255.255.255.0).
- *
- * @hide
- */
-public class DhcpClient extends StateMachine {
-
- private static final String TAG = "DhcpClient";
- private static final boolean DBG = true;
- private static final boolean STATE_DBG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean MSG_DBG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean PACKET_DBG = Log.isLoggable(TAG, Log.DEBUG);
-
- // Metrics events: must be kept in sync with server-side aggregation code.
- /** Represents transitions from DhcpInitState to DhcpBoundState */
- private static final String EVENT_INITIAL_BOUND = "InitialBoundState";
- /** Represents transitions from and to DhcpBoundState via DhcpRenewingState */
- private static final String EVENT_RENEWING_BOUND = "RenewingBoundState";
-
- // Timers and timeouts.
- private static final int SECONDS = 1000;
- private static final int FIRST_TIMEOUT_MS = 2 * SECONDS;
- private static final int MAX_TIMEOUT_MS = 128 * SECONDS;
-
- // This is not strictly needed, since the client is asynchronous and implements exponential
- // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
- // a blocking operation with a 30-second timeout. We pick 36 seconds so we can send packets at
- // t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
- private static final int DHCP_TIMEOUT_MS = 36 * SECONDS;
-
- // DhcpClient uses IpClient's handler.
- private static final int PUBLIC_BASE = IpClient.DHCPCLIENT_CMD_BASE;
-
- // Below constants are picked up by MessageUtils and exempt from ProGuard optimization.
- /* Commands from controller to start/stop DHCP */
- public static final int CMD_START_DHCP = PUBLIC_BASE + 1;
- public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2;
-
- /* Notification from DHCP state machine prior to DHCP discovery/renewal */
- public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 3;
- /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
- * success/failure */
- public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 4;
- /* Notification from DHCP state machine before quitting */
- public static final int CMD_ON_QUIT = PUBLIC_BASE + 5;
-
- /* Command from controller to indicate DHCP discovery/renewal can continue
- * after pre DHCP action is complete */
- public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 6;
-
- /* Command and event notification to/from IpManager requesting the setting
- * (or clearing) of an IPv4 LinkAddress.
- */
- public static final int CMD_CLEAR_LINKADDRESS = PUBLIC_BASE + 7;
- public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 8;
- public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 9;
-
- /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
- public static final int DHCP_SUCCESS = 1;
- public static final int DHCP_FAILURE = 2;
-
- // Internal messages.
- private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100;
- private static final int CMD_KICK = PRIVATE_BASE + 1;
- private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
- private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
- private static final int CMD_RENEW_DHCP = PRIVATE_BASE + 4;
- private static final int CMD_REBIND_DHCP = PRIVATE_BASE + 5;
- private static final int CMD_EXPIRE_DHCP = PRIVATE_BASE + 6;
-
- // For message logging.
- private static final Class[] sMessageClasses = { DhcpClient.class };
- private static final SparseArray sMessageNames =
- MessageUtils.findMessageNames(sMessageClasses);
-
- // DHCP parameters that we request.
- /* package */ static final byte[] REQUESTED_PARAMS = new byte[] {
- DHCP_SUBNET_MASK,
- DHCP_ROUTER,
- DHCP_DNS_SERVER,
- DHCP_DOMAIN_NAME,
- DHCP_MTU,
- DHCP_BROADCAST_ADDRESS, // TODO: currently ignored.
- DHCP_LEASE_TIME,
- DHCP_RENEWAL_TIME,
- DHCP_REBINDING_TIME,
- DHCP_VENDOR_INFO,
- };
-
- // DHCP flag that means "yes, we support unicast."
- private static final boolean DO_UNICAST = false;
-
- // System services / libraries we use.
- private final Context mContext;
- private final Random mRandom;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
-
- // Sockets.
- // - We use a packet socket to receive, because servers send us packets bound for IP addresses
- // which we have not yet configured, and the kernel protocol stack drops these.
- // - We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can
- // be off-link as well as on-link).
- private FileDescriptor mPacketSock;
- private FileDescriptor mUdpSock;
- private ReceiveThread mReceiveThread;
-
- // State variables.
- private final StateMachine mController;
- private final WakeupMessage mKickAlarm;
- private final WakeupMessage mTimeoutAlarm;
- private final WakeupMessage mRenewAlarm;
- private final WakeupMessage mRebindAlarm;
- private final WakeupMessage mExpiryAlarm;
- private final String mIfaceName;
-
- private boolean mRegisteredForPreDhcpNotification;
- private InterfaceParams mIface;
- // TODO: MacAddress-ify more of this class hierarchy.
- private byte[] mHwAddr;
- private SocketAddress mInterfaceBroadcastAddr;
- private int mTransactionId;
- private long mTransactionStartMillis;
- private DhcpResults mDhcpLease;
- private long mDhcpLeaseExpiry;
- private DhcpResults mOffer;
-
- // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
- private long mLastInitEnterTime;
- private long mLastBoundExitTime;
-
- // States.
- private State mStoppedState = new StoppedState();
- private State mDhcpState = new DhcpState();
- private State mDhcpInitState = new DhcpInitState();
- private State mDhcpSelectingState = new DhcpSelectingState();
- private State mDhcpRequestingState = new DhcpRequestingState();
- private State mDhcpHaveLeaseState = new DhcpHaveLeaseState();
- private State mConfiguringInterfaceState = new ConfiguringInterfaceState();
- private State mDhcpBoundState = new DhcpBoundState();
- private State mDhcpRenewingState = new DhcpRenewingState();
- private State mDhcpRebindingState = new DhcpRebindingState();
- private State mDhcpInitRebootState = new DhcpInitRebootState();
- private State mDhcpRebootingState = new DhcpRebootingState();
- private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
- private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
-
- private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
- cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
- return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
- }
-
- // TODO: Take an InterfaceParams instance instead of an interface name String.
- private DhcpClient(Context context, StateMachine controller, String iface) {
- super(TAG, controller.getHandler());
-
- mContext = context;
- mController = controller;
- mIfaceName = iface;
-
- addState(mStoppedState);
- addState(mDhcpState);
- addState(mDhcpInitState, mDhcpState);
- addState(mWaitBeforeStartState, mDhcpState);
- addState(mDhcpSelectingState, mDhcpState);
- addState(mDhcpRequestingState, mDhcpState);
- addState(mDhcpHaveLeaseState, mDhcpState);
- addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
- addState(mDhcpBoundState, mDhcpHaveLeaseState);
- addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState);
- addState(mDhcpRenewingState, mDhcpHaveLeaseState);
- addState(mDhcpRebindingState, mDhcpHaveLeaseState);
- addState(mDhcpInitRebootState, mDhcpState);
- addState(mDhcpRebootingState, mDhcpState);
-
- setInitialState(mStoppedState);
-
- mRandom = new Random();
-
- // Used to schedule packet retransmissions.
- mKickAlarm = makeWakeupMessage("KICK", CMD_KICK);
- // Used to time out PacketRetransmittingStates.
- mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
- // Used to schedule DHCP reacquisition.
- mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
- mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP);
- mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP);
- }
-
- public void registerForPreDhcpNotification() {
- mRegisteredForPreDhcpNotification = true;
- }
-
- public static DhcpClient makeDhcpClient(
- Context context, StateMachine controller, InterfaceParams ifParams) {
- DhcpClient client = new DhcpClient(context, controller, ifParams.name);
- client.mIface = ifParams;
- client.start();
- return client;
- }
-
- private boolean initInterface() {
- if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
- if (mIface == null) {
- Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName);
- return false;
- }
-
- mHwAddr = mIface.macAddr.toByteArray();
- mInterfaceBroadcastAddr = makePacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
- return true;
- }
-
- private void startNewTransaction() {
- mTransactionId = mRandom.nextInt();
- mTransactionStartMillis = SystemClock.elapsedRealtime();
- }
-
- private boolean initSockets() {
- return initPacketSocket() && initUdpSocket();
- }
-
- private boolean initPacketSocket() {
- try {
- mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
- SocketAddress addr = makePacketSocketAddress((short) ETH_P_IP, mIface.index);
- Os.bind(mPacketSock, addr);
- NetworkStackUtils.attachDhcpFilter(mPacketSock);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error creating packet socket", e);
- return false;
- }
- return true;
- }
-
- private boolean initUdpSocket() {
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(
- TrafficStatsConstants.TAG_SYSTEM_DHCP);
- try {
- mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
- Os.bind(mUdpSock, IPV4_ADDR_ANY, DhcpPacket.DHCP_CLIENT);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error creating UDP socket", e);
- return false;
- } finally {
- TrafficStats.setThreadStatsTag(oldTag);
- }
- return true;
- }
-
- private boolean connectUdpSock(Inet4Address to) {
- try {
- Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER);
- return true;
- } catch (SocketException|ErrnoException e) {
- Log.e(TAG, "Error connecting UDP socket", e);
- return false;
- }
- }
-
- private void closeSockets() {
- closeSocketQuietly(mUdpSock);
- closeSocketQuietly(mPacketSock);
- }
-
- class ReceiveThread extends Thread {
-
- private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
- private volatile boolean mStopped = false;
-
- public void halt() {
- mStopped = true;
- closeSockets(); // Interrupts the read() call the thread is blocked in.
- }
-
- @Override
- public void run() {
- if (DBG) Log.d(TAG, "Receive thread started");
- while (!mStopped) {
- int length = 0; // Or compiler can't tell it's initialized if a parse error occurs.
- try {
- length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
- DhcpPacket packet = null;
- packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
- if (DBG) Log.d(TAG, "Received packet: " + packet);
- sendMessage(CMD_RECEIVED_PACKET, packet);
- } catch (IOException|ErrnoException e) {
- if (!mStopped) {
- Log.e(TAG, "Read error", e);
- logError(DhcpErrorEvent.RECEIVE_ERROR);
- }
- } catch (DhcpPacket.ParseException e) {
- Log.e(TAG, "Can't parse packet: " + e.getMessage());
- if (PACKET_DBG) {
- Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
- }
- if (e.errorCode == DhcpErrorEvent.DHCP_NO_COOKIE) {
- int snetTagId = 0x534e4554;
- String bugId = "31850211";
- int uid = -1;
- String data = DhcpPacket.ParseException.class.getName();
- EventLog.writeEvent(snetTagId, bugId, uid, data);
- }
- logError(e.errorCode);
- }
- }
- if (DBG) Log.d(TAG, "Receive thread stopped");
- }
- }
-
- private short getSecs() {
- return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
- }
-
- private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
- try {
- if (encap == DhcpPacket.ENCAP_L2) {
- if (DBG) Log.d(TAG, "Broadcasting " + description);
- Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
- } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
- if (DBG) Log.d(TAG, "Broadcasting " + description);
- // We only send L3-encapped broadcasts in DhcpRebindingState,
- // where we have an IP address and an unconnected UDP socket.
- //
- // N.B.: We only need this codepath because DhcpRequestPacket
- // hardcodes the source IP address to 0.0.0.0. We could reuse
- // the packet socket if this ever changes.
- Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
- } else {
- // It's safe to call getpeername here, because we only send unicast packets if we
- // have an IP address, and we connect the UDP socket in DhcpBoundState#enter.
- if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",
- description, Os.getpeername(mUdpSock)));
- Os.write(mUdpSock, buf);
- }
- } catch(ErrnoException|IOException e) {
- Log.e(TAG, "Can't send packet: ", e);
- return false;
- }
- return true;
- }
-
- private boolean sendDiscoverPacket() {
- ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
- DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
- DO_UNICAST, REQUESTED_PARAMS);
- return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
- }
-
- private boolean sendRequestPacket(
- Inet4Address clientAddress, Inet4Address requestedAddress,
- Inet4Address serverAddress, Inet4Address to) {
- // TODO: should we use the transaction ID from the server?
- final int encap = INADDR_ANY.equals(clientAddress)
- ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
-
- ByteBuffer packet = DhcpPacket.buildRequestPacket(
- encap, mTransactionId, getSecs(), clientAddress,
- DO_UNICAST, mHwAddr, requestedAddress,
- serverAddress, REQUESTED_PARAMS, null);
- String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
- String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
- " request=" + requestedAddress.getHostAddress() +
- " serverid=" + serverStr;
- return transmitPacket(packet, description, encap, to);
- }
-
- private void scheduleLeaseTimers() {
- if (mDhcpLeaseExpiry == 0) {
- Log.d(TAG, "Infinite lease, no timer scheduling needed");
- return;
- }
-
- final long now = SystemClock.elapsedRealtime();
-
- // TODO: consider getting the renew and rebind timers from T1 and T2.
- // See also:
- // https://tools.ietf.org/html/rfc2131#section-4.4.5
- // https://tools.ietf.org/html/rfc1533#section-9.9
- // https://tools.ietf.org/html/rfc1533#section-9.10
- final long remainingDelay = mDhcpLeaseExpiry - now;
- final long renewDelay = remainingDelay / 2;
- final long rebindDelay = remainingDelay * 7 / 8;
- mRenewAlarm.schedule(now + renewDelay);
- mRebindAlarm.schedule(now + rebindDelay);
- mExpiryAlarm.schedule(now + remainingDelay);
- Log.d(TAG, "Scheduling renewal in " + (renewDelay / 1000) + "s");
- Log.d(TAG, "Scheduling rebind in " + (rebindDelay / 1000) + "s");
- Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
- }
-
- private void notifySuccess() {
- mController.sendMessage(
- CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
- }
-
- private void notifyFailure() {
- mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
- }
-
- private void acceptDhcpResults(DhcpResults results, String msg) {
- mDhcpLease = results;
- if (mDhcpLease.dnsServers.isEmpty()) {
- // supplement customized dns servers
- String[] dnsServersList =
- mContext.getResources().getStringArray(R.array.config_default_dns_servers);
- for (final String dnsServer : dnsServersList) {
- try {
- mDhcpLease.dnsServers.add(InetAddresses.parseNumericAddress(dnsServer));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Invalid default DNS server: " + dnsServer, e);
- }
- }
- }
- mOffer = null;
- Log.d(TAG, msg + " lease: " + mDhcpLease);
- notifySuccess();
- }
-
- private void clearDhcpState() {
- mDhcpLease = null;
- mDhcpLeaseExpiry = 0;
- mOffer = null;
- }
-
- /**
- * Quit the DhcpStateMachine.
- *
- * @hide
- */
- public void doQuit() {
- Log.d(TAG, "doQuit");
- quit();
- }
-
- @Override
- protected void onQuitting() {
- Log.d(TAG, "onQuitting");
- mController.sendMessage(CMD_ON_QUIT);
- }
-
- abstract class LoggingState extends State {
- private long mEnterTimeMs;
-
- @Override
- public void enter() {
- if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
- mEnterTimeMs = SystemClock.elapsedRealtime();
- }
-
- @Override
- public void exit() {
- long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs;
- logState(getName(), (int) durationMs);
- }
-
- private String messageName(int what) {
- return sMessageNames.get(what, Integer.toString(what));
- }
-
- private String messageToString(Message message) {
- long now = SystemClock.uptimeMillis();
- return new StringBuilder(" ")
- .append(message.getWhen() - now)
- .append(messageName(message.what))
- .append(" ").append(message.arg1)
- .append(" ").append(message.arg2)
- .append(" ").append(message.obj)
- .toString();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (MSG_DBG) {
- Log.d(TAG, getName() + messageToString(message));
- }
- return NOT_HANDLED;
- }
-
- @Override
- public String getName() {
- // All DhcpClient's states are inner classes with a well defined name.
- // Use getSimpleName() and avoid super's getName() creating new String instances.
- return getClass().getSimpleName();
- }
- }
-
- // Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
- // CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState.
- abstract class WaitBeforeOtherState extends LoggingState {
- protected State mOtherState;
-
- @Override
- public void enter() {
- super.enter();
- mController.sendMessage(CMD_PRE_DHCP_ACTION);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_PRE_DHCP_ACTION_COMPLETE:
- transitionTo(mOtherState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class StoppedState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_START_DHCP:
- if (mRegisteredForPreDhcpNotification) {
- transitionTo(mWaitBeforeStartState);
- } else {
- transitionTo(mDhcpInitState);
- }
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class WaitBeforeStartState extends WaitBeforeOtherState {
- public WaitBeforeStartState(State otherState) {
- super();
- mOtherState = otherState;
- }
- }
-
- class WaitBeforeRenewalState extends WaitBeforeOtherState {
- public WaitBeforeRenewalState(State otherState) {
- super();
- mOtherState = otherState;
- }
- }
-
- class DhcpState extends State {
- @Override
- public void enter() {
- clearDhcpState();
- if (initInterface() && initSockets()) {
- mReceiveThread = new ReceiveThread();
- mReceiveThread.start();
- } else {
- notifyFailure();
- transitionTo(mStoppedState);
- }
- }
-
- @Override
- public void exit() {
- if (mReceiveThread != null) {
- mReceiveThread.halt(); // Also closes sockets.
- mReceiveThread = null;
- }
- clearDhcpState();
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_STOP_DHCP:
- transitionTo(mStoppedState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- public boolean isValidPacket(DhcpPacket packet) {
- // TODO: check checksum.
- int xid = packet.getTransactionId();
- if (xid != mTransactionId) {
- Log.d(TAG, "Unexpected transaction ID " + xid + ", expected " + mTransactionId);
- return false;
- }
- if (!Arrays.equals(packet.getClientMac(), mHwAddr)) {
- Log.d(TAG, "MAC addr mismatch: got " +
- HexDump.toHexString(packet.getClientMac()) + ", expected " +
- HexDump.toHexString(packet.getClientMac()));
- return false;
- }
- return true;
- }
-
- public void setDhcpLeaseExpiry(DhcpPacket packet) {
- long leaseTimeMillis = packet.getLeaseTimeMillis();
- mDhcpLeaseExpiry =
- (leaseTimeMillis > 0) ? SystemClock.elapsedRealtime() + leaseTimeMillis : 0;
- }
-
- /**
- * Retransmits packets using jittered exponential backoff with an optional timeout. Packet
- * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
- * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
- * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
- * state.
- *
- * Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
- * packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
- * sent by the receive thread. They may also set mTimeout and implement timeout.
- */
- abstract class PacketRetransmittingState extends LoggingState {
-
- private int mTimer;
- protected int mTimeout = 0;
-
- @Override
- public void enter() {
- super.enter();
- initTimer();
- maybeInitTimeout();
- sendMessage(CMD_KICK);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_KICK:
- sendPacket();
- scheduleKick();
- return HANDLED;
- case CMD_RECEIVED_PACKET:
- receivePacket((DhcpPacket) message.obj);
- return HANDLED;
- case CMD_TIMEOUT:
- timeout();
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- super.exit();
- mKickAlarm.cancel();
- mTimeoutAlarm.cancel();
- }
-
- abstract protected boolean sendPacket();
- abstract protected void receivePacket(DhcpPacket packet);
- protected void timeout() {}
-
- protected void initTimer() {
- mTimer = FIRST_TIMEOUT_MS;
- }
-
- protected int jitterTimer(int baseTimer) {
- int maxJitter = baseTimer / 10;
- int jitter = mRandom.nextInt(2 * maxJitter) - maxJitter;
- return baseTimer + jitter;
- }
-
- protected void scheduleKick() {
- long now = SystemClock.elapsedRealtime();
- long timeout = jitterTimer(mTimer);
- long alarmTime = now + timeout;
- mKickAlarm.schedule(alarmTime);
- mTimer *= 2;
- if (mTimer > MAX_TIMEOUT_MS) {
- mTimer = MAX_TIMEOUT_MS;
- }
- }
-
- protected void maybeInitTimeout() {
- if (mTimeout > 0) {
- long alarmTime = SystemClock.elapsedRealtime() + mTimeout;
- mTimeoutAlarm.schedule(alarmTime);
- }
- }
- }
-
- class DhcpInitState extends PacketRetransmittingState {
- public DhcpInitState() {
- super();
- }
-
- @Override
- public void enter() {
- super.enter();
- startNewTransaction();
- mLastInitEnterTime = SystemClock.elapsedRealtime();
- }
-
- protected boolean sendPacket() {
- return sendDiscoverPacket();
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if (!(packet instanceof DhcpOfferPacket)) return;
- mOffer = packet.toDhcpResults();
- if (mOffer != null) {
- Log.d(TAG, "Got pending lease: " + mOffer);
- transitionTo(mDhcpRequestingState);
- }
- }
- }
-
- // Not implemented. We request the first offer we receive.
- class DhcpSelectingState extends LoggingState {
- }
-
- class DhcpRequestingState extends PacketRetransmittingState {
- public DhcpRequestingState() {
- mTimeout = DHCP_TIMEOUT_MS / 2;
- }
-
- protected boolean sendPacket() {
- return sendRequestPacket(
- INADDR_ANY, // ciaddr
- (Inet4Address) mOffer.ipAddress.getAddress(), // DHCP_REQUESTED_IP
- (Inet4Address) mOffer.serverAddress, // DHCP_SERVER_IDENTIFIER
- INADDR_BROADCAST); // packet destination address
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if ((packet instanceof DhcpAckPacket)) {
- DhcpResults results = packet.toDhcpResults();
- if (results != null) {
- setDhcpLeaseExpiry(packet);
- acceptDhcpResults(results, "Confirmed");
- transitionTo(mConfiguringInterfaceState);
- }
- } else if (packet instanceof DhcpNakPacket) {
- // TODO: Wait a while before returning into INIT state.
- Log.d(TAG, "Received NAK, returning to INIT");
- mOffer = null;
- transitionTo(mDhcpInitState);
- }
- }
-
- @Override
- protected void timeout() {
- // After sending REQUESTs unsuccessfully for a while, go back to init.
- transitionTo(mDhcpInitState);
- }
- }
-
- class DhcpHaveLeaseState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_EXPIRE_DHCP:
- Log.d(TAG, "Lease expired!");
- notifyFailure();
- transitionTo(mDhcpInitState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- // Clear any extant alarms.
- mRenewAlarm.cancel();
- mRebindAlarm.cancel();
- mExpiryAlarm.cancel();
- clearDhcpState();
- // Tell IpManager to clear the IPv4 address. There is no need to
- // wait for confirmation since any subsequent packets are sent from
- // INADDR_ANY anyway (DISCOVER, REQUEST).
- mController.sendMessage(CMD_CLEAR_LINKADDRESS);
- }
- }
-
- class ConfiguringInterfaceState extends LoggingState {
- @Override
- public void enter() {
- super.enter();
- mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case EVENT_LINKADDRESS_CONFIGURED:
- transitionTo(mDhcpBoundState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class DhcpBoundState extends LoggingState {
- @Override
- public void enter() {
- super.enter();
- if (mDhcpLease.serverAddress != null && !connectUdpSock(mDhcpLease.serverAddress)) {
- // There's likely no point in going into DhcpInitState here, we'll probably
- // just repeat the transaction, get the same IP address as before, and fail.
- //
- // NOTE: It is observed that connectUdpSock() basically never fails, due to
- // SO_BINDTODEVICE. Examining the local socket address shows it will happily
- // return an IPv4 address from another interface, or even return "0.0.0.0".
- //
- // TODO: Consider deleting this check, following testing on several kernels.
- notifyFailure();
- transitionTo(mStoppedState);
- }
-
- scheduleLeaseTimers();
- logTimeToBoundState();
- }
-
- @Override
- public void exit() {
- super.exit();
- mLastBoundExitTime = SystemClock.elapsedRealtime();
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_RENEW_DHCP:
- if (mRegisteredForPreDhcpNotification) {
- transitionTo(mWaitBeforeRenewalState);
- } else {
- transitionTo(mDhcpRenewingState);
- }
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- private void logTimeToBoundState() {
- long now = SystemClock.elapsedRealtime();
- if (mLastBoundExitTime > mLastInitEnterTime) {
- logState(EVENT_RENEWING_BOUND, (int) (now - mLastBoundExitTime));
- } else {
- logState(EVENT_INITIAL_BOUND, (int) (now - mLastInitEnterTime));
- }
- }
- }
-
- abstract class DhcpReacquiringState extends PacketRetransmittingState {
- protected String mLeaseMsg;
-
- @Override
- public void enter() {
- super.enter();
- startNewTransaction();
- }
-
- abstract protected Inet4Address packetDestination();
-
- protected boolean sendPacket() {
- return sendRequestPacket(
- (Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr
- INADDR_ANY, // DHCP_REQUESTED_IP
- null, // DHCP_SERVER_IDENTIFIER
- packetDestination()); // packet destination address
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if ((packet instanceof DhcpAckPacket)) {
- final DhcpResults results = packet.toDhcpResults();
- if (results != null) {
- if (!mDhcpLease.ipAddress.equals(results.ipAddress)) {
- Log.d(TAG, "Renewed lease not for our current IP address!");
- notifyFailure();
- transitionTo(mDhcpInitState);
- }
- setDhcpLeaseExpiry(packet);
- // Updating our notion of DhcpResults here only causes the
- // DNS servers and routes to be updated in LinkProperties
- // in IpManager and by any overridden relevant handlers of
- // the registered IpManager.Callback. IP address changes
- // are not supported here.
- acceptDhcpResults(results, mLeaseMsg);
- transitionTo(mDhcpBoundState);
- }
- } else if (packet instanceof DhcpNakPacket) {
- Log.d(TAG, "Received NAK, returning to INIT");
- notifyFailure();
- transitionTo(mDhcpInitState);
- }
- }
- }
-
- class DhcpRenewingState extends DhcpReacquiringState {
- public DhcpRenewingState() {
- mLeaseMsg = "Renewed";
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (super.processMessage(message) == HANDLED) {
- return HANDLED;
- }
-
- switch (message.what) {
- case CMD_REBIND_DHCP:
- transitionTo(mDhcpRebindingState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- protected Inet4Address packetDestination() {
- // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
- // http://b/25343517 . Try to make things work anyway by using broadcast renews.
- return (mDhcpLease.serverAddress != null) ?
- mDhcpLease.serverAddress : INADDR_BROADCAST;
- }
- }
-
- class DhcpRebindingState extends DhcpReacquiringState {
- public DhcpRebindingState() {
- mLeaseMsg = "Rebound";
- }
-
- @Override
- public void enter() {
- super.enter();
-
- // We need to broadcast and possibly reconnect the socket to a
- // completely different server.
- closeSocketQuietly(mUdpSock);
- if (!initUdpSocket()) {
- Log.e(TAG, "Failed to recreate UDP socket");
- transitionTo(mDhcpInitState);
- }
- }
-
- @Override
- protected Inet4Address packetDestination() {
- return INADDR_BROADCAST;
- }
- }
-
- class DhcpInitRebootState extends LoggingState {
- }
-
- class DhcpRebootingState extends LoggingState {
- }
-
- private void logError(int errorCode) {
- mMetricsLog.log(mIfaceName, new DhcpErrorEvent(errorCode));
- }
-
- private void logState(String name, int durationMs) {
- final DhcpClientEvent event = new DhcpClientEvent.Builder()
- .setMsg(name)
- .setDurationMs(durationMs)
- .build();
- mMetricsLog.log(mIfaceName, event);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpDeclinePacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpDeclinePacket.java
deleted file mode 100644
index 7ecdea7b89da2..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpDeclinePacket.java
+++ /dev/null
@@ -1,61 +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 android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-DECLINE packet.
- */
-class DhcpDeclinePacket extends DhcpPacket {
- /**
- * Generates a DECLINE packet with the specified parameters.
- */
- DhcpDeclinePacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
- Inet4Address nextIp, Inet4Address relayIp,
- byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " DECLINE";
- }
-
- /**
- * Fills in a packet with the requested DECLINE attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result,
- DHCP_BOOTREQUEST, false);
- result.flip();
- return result;
- }
-
- /**
- * Adds optional parameters to the DECLINE packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_DECLINE);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- // RFC 2131 says we MUST NOT include our common client TLVs or the parameter request list.
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpDiscoverPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpDiscoverPacket.java
deleted file mode 100644
index 11f2b6118e24f..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpDiscoverPacket.java
+++ /dev/null
@@ -1,67 +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 android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-DISCOVER packet.
- */
-class DhcpDiscoverPacket extends DhcpPacket {
- /**
- * The IP address of the client which sent this packet.
- */
- final Inet4Address mSrcIp;
-
- /**
- * Generates a DISCOVER packet with the specified parameters.
- */
- DhcpDiscoverPacket(int transId, short secs, Inet4Address relayIp, byte[] clientMac,
- boolean broadcast, Inet4Address srcIp) {
- super(transId, secs, INADDR_ANY, INADDR_ANY, INADDR_ANY, relayIp, clientMac, broadcast);
- mSrcIp = srcIp;
- }
-
- public String toString() {
- String s = super.toString();
- return s + " DISCOVER " +
- (mBroadcast ? "broadcast " : "unicast ");
- }
-
- /**
- * Fills in a packet with the requested DISCOVER parameters.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- fillInPacket(encap, INADDR_BROADCAST, mSrcIp, destUdp, srcUdp, result, DHCP_BOOTREQUEST,
- mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds optional parameters to a DISCOVER packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_DISCOVER);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- addCommonClientTlvs(buffer);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpInformPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpInformPacket.java
deleted file mode 100644
index 7a83466c6e054..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpInformPacket.java
+++ /dev/null
@@ -1,62 +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 android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the (unused) DHCP-INFORM packet.
- */
-class DhcpInformPacket extends DhcpPacket {
- /**
- * Generates an INFORM packet with the specified parameters.
- */
- DhcpInformPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
- Inet4Address nextIp, Inet4Address relayIp,
- byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " INFORM";
- }
-
- /**
- * Builds an INFORM packet.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result,
- DHCP_BOOTREQUEST, false);
- result.flip();
- return result;
- }
-
- /**
- * Adds additional parameters to the INFORM packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_INFORM);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- addCommonClientTlvs(buffer);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
deleted file mode 100644
index 6849cfadc22ae..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
+++ /dev/null
@@ -1,153 +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 android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.MacAddress;
-import android.os.SystemClock;
-import android.text.TextUtils;
-
-import com.android.internal.util.HexDump;
-
-import java.net.Inet4Address;
-import java.util.Arrays;
-import java.util.Objects;
-
-/**
- * An IPv4 address assignment done through DHCPv4.
- * @hide
- */
-public class DhcpLease {
- public static final long EXPIRATION_NEVER = Long.MAX_VALUE;
- public static final String HOSTNAME_NONE = null;
-
- @Nullable
- private final byte[] mClientId;
- @NonNull
- private final MacAddress mHwAddr;
- @NonNull
- private final Inet4Address mNetAddr;
- /**
- * Expiration time for the lease, to compare with {@link SystemClock#elapsedRealtime()}.
- */
- private final long mExpTime;
- @Nullable
- private final String mHostname;
-
- public DhcpLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- @NonNull Inet4Address netAddr, long expTime, @Nullable String hostname) {
- mClientId = (clientId == null ? null : Arrays.copyOf(clientId, clientId.length));
- mHwAddr = hwAddr;
- mNetAddr = netAddr;
- mExpTime = expTime;
- mHostname = hostname;
- }
-
- /**
- * Get the clientId associated with this lease, if any.
- *
- *
If the lease is not associated to a clientId, this returns null.
- */
- @Nullable
- public byte[] getClientId() {
- if (mClientId == null) {
- return null;
- }
- return Arrays.copyOf(mClientId, mClientId.length);
- }
-
- @NonNull
- public MacAddress getHwAddr() {
- return mHwAddr;
- }
-
- @Nullable
- public String getHostname() {
- return mHostname;
- }
-
- @NonNull
- public Inet4Address getNetAddr() {
- return mNetAddr;
- }
-
- public long getExpTime() {
- return mExpTime;
- }
-
- /**
- * Push back the expiration time of this lease. If the provided time is sooner than the original
- * expiration time, the lease time will not be updated.
- *
- *
The lease hostname is updated with the provided one if set.
- * @return A {@link DhcpLease} with expiration time set to max(expTime, currentExpTime)
- */
- public DhcpLease renewedLease(long expTime, @Nullable String hostname) {
- return new DhcpLease(mClientId, mHwAddr, mNetAddr, Math.max(expTime, mExpTime),
- (hostname == null ? mHostname : hostname));
- }
-
- /**
- * Determine whether this lease matches a client with the specified parameters.
- * @param clientId clientId of the client if any, or null otherwise.
- * @param hwAddr Hardware address of the client.
- */
- public boolean matchesClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) {
- if (mClientId != null) {
- return Arrays.equals(mClientId, clientId);
- } else {
- return clientId == null && mHwAddr.equals(hwAddr);
- }
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof DhcpLease)) {
- return false;
- }
- final DhcpLease other = (DhcpLease) obj;
- return Arrays.equals(mClientId, other.mClientId)
- && mHwAddr.equals(other.mHwAddr)
- && mNetAddr.equals(other.mNetAddr)
- && mExpTime == other.mExpTime
- && TextUtils.equals(mHostname, other.mHostname);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mClientId, mHwAddr, mNetAddr, mHostname, mExpTime);
- }
-
- static String clientIdToString(byte[] bytes) {
- if (bytes == null) {
- return "null";
- }
- return HexDump.toHexString(bytes);
- }
-
- static String inet4AddrToString(@Nullable Inet4Address addr) {
- return (addr == null) ? "null" : addr.getHostAddress();
- }
-
- @Override
- public String toString() {
- return String.format("clientId: %s, hwAddr: %s, netAddr: %s, expTime: %d, hostname: %s",
- clientIdToString(mClientId), mHwAddr.toString(), inet4AddrToString(mNetAddr),
- mExpTime, mHostname);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
deleted file mode 100644
index 0a15cd7d3bd96..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
+++ /dev/null
@@ -1,546 +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.dhcp.DhcpLease.EXPIRATION_NEVER;
-import static android.net.dhcp.DhcpLease.inet4AddrToString;
-import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
-import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
-import static android.net.shared.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
-
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_BITS;
-
-import static java.lang.Math.min;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.IpPrefix;
-import android.net.MacAddress;
-import android.net.dhcp.DhcpServer.Clock;
-import android.net.util.SharedLog;
-import android.util.ArrayMap;
-
-import java.net.Inet4Address;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.function.Function;
-
-/**
- * A repository managing IPv4 address assignments through DHCPv4.
- *
- *
This class is not thread-safe. All public methods should be called on a common thread or
- * use some synchronization mechanism.
- *
- *
Methods are optimized for a small number of allocated leases, assuming that most of the time
- * only 2~10 addresses will be allocated, which is the common case. Managing a large number of
- * addresses is supported but will be slower: some operations have complexity in O(num_leases).
- * @hide
- */
-class DhcpLeaseRepository {
- public static final byte[] CLIENTID_UNSPEC = null;
- public static final Inet4Address INETADDR_UNSPEC = null;
-
- @NonNull
- private final SharedLog mLog;
- @NonNull
- private final Clock mClock;
-
- @NonNull
- private IpPrefix mPrefix;
- @NonNull
- private Set mReservedAddrs;
- private int mSubnetAddr;
- private int mSubnetMask;
- private int mNumAddresses;
- private long mLeaseTimeMs;
-
- /**
- * Next timestamp when committed or declined leases should be checked for expired ones. This
- * will always be lower than or equal to the time for the first lease to expire: it's OK not to
- * update this when removing entries, but it must always be updated when adding/updating.
- */
- private long mNextExpirationCheck = EXPIRATION_NEVER;
-
- static class DhcpLeaseException extends Exception {
- DhcpLeaseException(String message) {
- super(message);
- }
- }
-
- static class OutOfAddressesException extends DhcpLeaseException {
- OutOfAddressesException(String message) {
- super(message);
- }
- }
-
- static class InvalidAddressException extends DhcpLeaseException {
- InvalidAddressException(String message) {
- super(message);
- }
- }
-
- static class InvalidSubnetException extends DhcpLeaseException {
- InvalidSubnetException(String message) {
- super(message);
- }
- }
-
- /**
- * Leases by IP address
- */
- private final ArrayMap mCommittedLeases = new ArrayMap<>();
-
- /**
- * Map address -> expiration timestamp in ms. Addresses are guaranteed to be valid as defined
- * by {@link #isValidAddress(Inet4Address)}, but are not necessarily otherwise available for
- * assignment.
- */
- private final LinkedHashMap mDeclinedAddrs = new LinkedHashMap<>();
-
- DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set reservedAddrs,
- long leaseTimeMs, @NonNull SharedLog log, @NonNull Clock clock) {
- updateParams(prefix, reservedAddrs, leaseTimeMs);
- mLog = log;
- mClock = clock;
- }
-
- public void updateParams(@NonNull IpPrefix prefix, @NonNull Set reservedAddrs,
- long leaseTimeMs) {
- mPrefix = prefix;
- mReservedAddrs = Collections.unmodifiableSet(new HashSet<>(reservedAddrs));
- mSubnetMask = prefixLengthToV4NetmaskIntHTH(prefix.getPrefixLength());
- mSubnetAddr = inet4AddressToIntHTH((Inet4Address) prefix.getAddress()) & mSubnetMask;
- mNumAddresses = 1 << (IPV4_ADDR_BITS - prefix.getPrefixLength());
- mLeaseTimeMs = leaseTimeMs;
-
- cleanMap(mCommittedLeases);
- cleanMap(mDeclinedAddrs);
- }
-
- /**
- * From a map keyed by {@link Inet4Address}, remove entries where the key is invalid (as
- * specified by {@link #isValidAddress(Inet4Address)}), or is a reserved address.
- */
- private void cleanMap(Map map) {
- final Iterator> it = map.entrySet().iterator();
- while (it.hasNext()) {
- final Inet4Address addr = it.next().getKey();
- if (!isValidAddress(addr) || mReservedAddrs.contains(addr)) {
- it.remove();
- }
- }
- }
-
- /**
- * Get a DHCP offer, to reply to a DHCPDISCOVER. Follows RFC2131 #4.3.1.
- *
- * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC}
- * @param relayAddr Internet address of the relay (giaddr), can be {@link Inet4Address#ANY}
- * @param reqAddr Requested address by the client (option 50), or {@link #INETADDR_UNSPEC}
- * @param hostname Client-provided hostname, or {@link DhcpLease#HOSTNAME_NONE}
- * @throws OutOfAddressesException The server does not have any available address
- * @throws InvalidSubnetException The lease was requested from an unsupported subnet
- */
- @NonNull
- public DhcpLease getOffer(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- @NonNull Inet4Address relayAddr, @Nullable Inet4Address reqAddr,
- @Nullable String hostname) throws OutOfAddressesException, InvalidSubnetException {
- final long currentTime = mClock.elapsedRealtime();
- final long expTime = currentTime + mLeaseTimeMs;
-
- removeExpiredLeases(currentTime);
- checkValidRelayAddr(relayAddr);
-
- final DhcpLease currentLease = findByClient(clientId, hwAddr);
- final DhcpLease newLease;
- if (currentLease != null) {
- newLease = currentLease.renewedLease(expTime, hostname);
- mLog.log("Offering extended lease " + newLease);
- // Do not update lease time in the map: the offer is not committed yet.
- } else if (reqAddr != null && isValidAddress(reqAddr) && isAvailable(reqAddr)) {
- newLease = new DhcpLease(clientId, hwAddr, reqAddr, expTime, hostname);
- mLog.log("Offering requested lease " + newLease);
- } else {
- newLease = makeNewOffer(clientId, hwAddr, expTime, hostname);
- mLog.log("Offering new generated lease " + newLease);
- }
- return newLease;
- }
-
- private void checkValidRelayAddr(@Nullable Inet4Address relayAddr)
- throws InvalidSubnetException {
- // As per #4.3.1, addresses are assigned based on the relay address if present. This
- // implementation only assigns addresses if the relayAddr is inside our configured subnet.
- // This also applies when the client requested a specific address for consistency between
- // requests, and with older behavior.
- if (isIpAddrOutsidePrefix(mPrefix, relayAddr)) {
- throw new InvalidSubnetException("Lease requested by relay from outside of subnet");
- }
- }
-
- private static boolean isIpAddrOutsidePrefix(@NonNull IpPrefix prefix,
- @Nullable Inet4Address addr) {
- return addr != null && !addr.equals(IPV4_ADDR_ANY) && !prefix.contains(addr);
- }
-
- @Nullable
- private DhcpLease findByClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) {
- for (DhcpLease lease : mCommittedLeases.values()) {
- if (lease.matchesClient(clientId, hwAddr)) {
- return lease;
- }
- }
-
- // Note this differs from dnsmasq behavior, which would match by hwAddr if clientId was
- // given but no lease keyed on clientId matched. This would prevent one interface from
- // obtaining multiple leases with different clientId.
- return null;
- }
-
- /**
- * Make a lease conformant to a client DHCPREQUEST or renew the client's existing lease,
- * commit it to the repository and return it.
- *
- *
This method always succeeds and commits the lease if it does not throw, and has no side
- * effects if it throws.
- *
- * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC}
- * @param reqAddr Requested address by the client (option 50), or {@link #INETADDR_UNSPEC}
- * @param sidSet Whether the server identifier was set in the request
- * @return The newly created or renewed lease
- * @throws InvalidAddressException The client provided an address that conflicts with its
- * current configuration, or other committed/reserved leases.
- */
- @NonNull
- public DhcpLease requestLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- @NonNull Inet4Address clientAddr, @NonNull Inet4Address relayAddr,
- @Nullable Inet4Address reqAddr, boolean sidSet, @Nullable String hostname)
- throws InvalidAddressException, InvalidSubnetException {
- final long currentTime = mClock.elapsedRealtime();
- removeExpiredLeases(currentTime);
- checkValidRelayAddr(relayAddr);
- final DhcpLease assignedLease = findByClient(clientId, hwAddr);
-
- final Inet4Address leaseAddr = reqAddr != null ? reqAddr : clientAddr;
- if (assignedLease != null) {
- if (sidSet && reqAddr != null) {
- // Client in SELECTING state; remove any current lease before creating a new one.
- mCommittedLeases.remove(assignedLease.getNetAddr());
- } else if (!assignedLease.getNetAddr().equals(leaseAddr)) {
- // reqAddr null (RENEWING/REBINDING): client renewing its own lease for clientAddr.
- // reqAddr set with sid not set (INIT-REBOOT): client verifying configuration.
- // In both cases, throw if clientAddr or reqAddr does not match the known lease.
- throw new InvalidAddressException("Incorrect address for client in "
- + (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING"));
- }
- }
-
- // In the init-reboot case, RFC2131 #4.3.2 says that the server must not reply if
- // assignedLease == null, but dnsmasq will let the client use the requested address if
- // available, when configured with --dhcp-authoritative. This is preferable to avoid issues
- // if the server lost the lease DB: the client would not get a reply because the server
- // does not know their lease.
- // Similarly in RENEWING/REBINDING state, create a lease when possible if the
- // client-provided lease is unknown.
- final DhcpLease lease =
- checkClientAndMakeLease(clientId, hwAddr, leaseAddr, hostname, currentTime);
- mLog.logf("DHCPREQUEST assignedLease %s, reqAddr=%s, sidSet=%s: created/renewed lease %s",
- assignedLease, inet4AddrToString(reqAddr), sidSet, lease);
- return lease;
- }
-
- /**
- * Check that the client can request the specified address, make or renew the lease if yes, and
- * commit it.
- *
- *
This method always succeeds and returns the lease if it does not throw, and has no
- * side-effect if it throws.
- *
- * @return The newly created or renewed, committed lease
- * @throws InvalidAddressException The client provided an address that conflicts with its
- * current configuration, or other committed/reserved leases.
- */
- private DhcpLease checkClientAndMakeLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- @NonNull Inet4Address addr, @Nullable String hostname, long currentTime)
- throws InvalidAddressException {
- final long expTime = currentTime + mLeaseTimeMs;
- final DhcpLease currentLease = mCommittedLeases.getOrDefault(addr, null);
- if (currentLease != null && !currentLease.matchesClient(clientId, hwAddr)) {
- throw new InvalidAddressException("Address in use");
- }
-
- final DhcpLease lease;
- if (currentLease == null) {
- if (isValidAddress(addr) && !mReservedAddrs.contains(addr)) {
- lease = new DhcpLease(clientId, hwAddr, addr, expTime, hostname);
- } else {
- throw new InvalidAddressException("Lease not found and address unavailable");
- }
- } else {
- lease = currentLease.renewedLease(expTime, hostname);
- }
- commitLease(lease);
- return lease;
- }
-
- private void commitLease(@NonNull DhcpLease lease) {
- mCommittedLeases.put(lease.getNetAddr(), lease);
- maybeUpdateEarliestExpiration(lease.getExpTime());
- }
-
- /**
- * Delete a committed lease from the repository.
- *
- * @return true if a lease matching parameters was found.
- */
- public boolean releaseLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- @NonNull Inet4Address addr) {
- final DhcpLease currentLease = mCommittedLeases.getOrDefault(addr, null);
- if (currentLease == null) {
- mLog.w("Could not release unknown lease for " + inet4AddrToString(addr));
- return false;
- }
- if (currentLease.matchesClient(clientId, hwAddr)) {
- mCommittedLeases.remove(addr);
- mLog.log("Released lease " + currentLease);
- return true;
- }
- mLog.w(String.format("Not releasing lease %s: does not match client (cid %s, hwAddr %s)",
- currentLease, DhcpLease.clientIdToString(clientId), hwAddr));
- return false;
- }
-
- public void markLeaseDeclined(@NonNull Inet4Address addr) {
- if (mDeclinedAddrs.containsKey(addr) || !isValidAddress(addr)) {
- mLog.logf("Not marking %s as declined: already declined or not assignable",
- inet4AddrToString(addr));
- return;
- }
- final long expTime = mClock.elapsedRealtime() + mLeaseTimeMs;
- mDeclinedAddrs.put(addr, expTime);
- mLog.logf("Marked %s as declined expiring %d", inet4AddrToString(addr), expTime);
- maybeUpdateEarliestExpiration(expTime);
- }
-
- /**
- * Get the list of currently valid committed leases in the repository.
- */
- @NonNull
- public List getCommittedLeases() {
- removeExpiredLeases(mClock.elapsedRealtime());
- return new ArrayList<>(mCommittedLeases.values());
- }
-
- /**
- * Get the set of addresses that have been marked as declined in the repository.
- */
- @NonNull
- public Set getDeclinedAddresses() {
- removeExpiredLeases(mClock.elapsedRealtime());
- return new HashSet<>(mDeclinedAddrs.keySet());
- }
-
- /**
- * Given the expiration time of a new committed lease or declined address, update
- * {@link #mNextExpirationCheck} so it stays lower than or equal to the time for the first lease
- * to expire.
- */
- private void maybeUpdateEarliestExpiration(long expTime) {
- if (expTime < mNextExpirationCheck) {
- mNextExpirationCheck = expTime;
- }
- }
-
- /**
- * Remove expired entries from a map keyed by {@link Inet4Address}.
- *
- * @param tag Type of lease in the map, for logging
- * @param getExpTime Functor returning the expiration time for an object in the map.
- * Must not return null.
- * @return The lowest expiration time among entries remaining in the map
- */
- private long removeExpired(long currentTime, @NonNull Map map,
- @NonNull String tag, @NonNull Function getExpTime) {
- final Iterator> it = map.entrySet().iterator();
- long firstExpiration = EXPIRATION_NEVER;
- while (it.hasNext()) {
- final Entry lease = it.next();
- final long expTime = getExpTime.apply(lease.getValue());
- if (expTime <= currentTime) {
- mLog.logf("Removing expired %s lease for %s (expTime=%s, currentTime=%s)",
- tag, lease.getKey(), expTime, currentTime);
- it.remove();
- } else {
- firstExpiration = min(firstExpiration, expTime);
- }
- }
- return firstExpiration;
- }
-
- /**
- * Go through committed and declined leases and remove the expired ones.
- */
- private void removeExpiredLeases(long currentTime) {
- if (currentTime < mNextExpirationCheck) {
- return;
- }
-
- final long commExp = removeExpired(
- currentTime, mCommittedLeases, "committed", DhcpLease::getExpTime);
- final long declExp = removeExpired(
- currentTime, mDeclinedAddrs, "declined", Function.identity());
-
- mNextExpirationCheck = min(commExp, declExp);
- }
-
- private boolean isAvailable(@NonNull Inet4Address addr) {
- return !mReservedAddrs.contains(addr) && !mCommittedLeases.containsKey(addr);
- }
-
- /**
- * Get the 0-based index of an address in the subnet.
- *
- *
Given ordering of addresses 5.6.7.8 < 5.6.7.9 < 5.6.8.0, the index on a subnet is defined
- * so that the first address is 0, the second 1, etc. For example on a /16, 192.168.0.0 -> 0,
- * 192.168.0.1 -> 1, 192.168.1.0 -> 256
- *
- */
- private int getAddrIndex(int addr) {
- return addr & ~mSubnetMask;
- }
-
- private int getAddrByIndex(int index) {
- return mSubnetAddr | index;
- }
-
- /**
- * Get a valid address starting from the supplied one.
- *
- *
This only checks that the address is numerically valid for assignment, not whether it is
- * already in use. The return value is always inside the configured prefix, even if the supplied
- * address is not.
- *
- *
If the provided address is valid, it is returned as-is. Otherwise, the next valid
- * address (with the ordering in {@link #getAddrIndex(int)}) is returned.
- */
- private int getValidAddress(int addr) {
- final int lastByteMask = 0xff;
- int addrIndex = getAddrIndex(addr); // 0-based index of the address in the subnet
-
- // Some OSes do not handle addresses in .255 or .0 correctly: avoid those.
- final int lastByte = getAddrByIndex(addrIndex) & lastByteMask;
- if (lastByte == lastByteMask) {
- // Avoid .255 address, and .0 address that follows
- addrIndex = (addrIndex + 2) % mNumAddresses;
- } else if (lastByte == 0) {
- // Avoid .0 address
- addrIndex = (addrIndex + 1) % mNumAddresses;
- }
-
- // Do not use first or last address of range
- if (addrIndex == 0 || addrIndex == mNumAddresses - 1) {
- // Always valid and not end of range since prefixLength is at most 30 in serving params
- addrIndex = 1;
- }
- return getAddrByIndex(addrIndex);
- }
-
- /**
- * Returns whether the address is in the configured subnet and part of the assignable range.
- */
- private boolean isValidAddress(Inet4Address addr) {
- final int intAddr = inet4AddressToIntHTH(addr);
- return getValidAddress(intAddr) == intAddr;
- }
-
- private int getNextAddress(int addr) {
- final int addrIndex = getAddrIndex(addr);
- final int nextAddress = getAddrByIndex((addrIndex + 1) % mNumAddresses);
- return getValidAddress(nextAddress);
- }
-
- /**
- * Calculate a first candidate address for a client by hashing the hardware address.
- *
- *
This will be a valid address as checked by {@link #getValidAddress(int)}, but may be
- * in use.
- *
- * @return An IPv4 address encoded as 32-bit int
- */
- private int getFirstClientAddress(MacAddress hwAddr) {
- // This follows dnsmasq behavior. Advantages are: clients will often get the same
- // offers for different DISCOVER even if the lease was not yet accepted or has expired,
- // and address generation will generally not need to loop through many allocated addresses
- // until it finds a free one.
- int hash = 0;
- for (byte b : hwAddr.toByteArray()) {
- hash += b + (b << 8) + (b << 16);
- }
- // This implementation will not always result in the same IPs as dnsmasq would give out in
- // Android <= P, because it includes invalid and reserved addresses in mNumAddresses while
- // the configured ranges for dnsmasq did not.
- final int addrIndex = hash % mNumAddresses;
- return getValidAddress(getAddrByIndex(addrIndex));
- }
-
- /**
- * Create a lease that can be offered to respond to a client DISCOVER.
- *
- *
This method always succeeds and returns the lease if it does not throw. If no non-declined
- * address is available, it will try to offer the oldest declined address if valid.
- *
- * @throws OutOfAddressesException The server has no address left to offer
- */
- private DhcpLease makeNewOffer(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- long expTime, @Nullable String hostname) throws OutOfAddressesException {
- int intAddr = getFirstClientAddress(hwAddr);
- // Loop until a free address is found, or there are no more addresses.
- // There is slightly less than this many usable addresses, but some extra looping is OK
- for (int i = 0; i < mNumAddresses; i++) {
- final Inet4Address addr = intToInet4AddressHTH(intAddr);
- if (isAvailable(addr) && !mDeclinedAddrs.containsKey(addr)) {
- return new DhcpLease(clientId, hwAddr, addr, expTime, hostname);
- }
- intAddr = getNextAddress(intAddr);
- }
-
- // Try freeing DECLINEd addresses if out of addresses.
- final Iterator it = mDeclinedAddrs.keySet().iterator();
- while (it.hasNext()) {
- final Inet4Address addr = it.next();
- it.remove();
- mLog.logf("Out of addresses in address pool: dropped declined addr %s",
- inet4AddrToString(addr));
- // isValidAddress() is always verified for entries in mDeclinedAddrs.
- // However declined addresses may have been requested (typically by the machine that was
- // already using the address) after being declined.
- if (isAvailable(addr)) {
- return new DhcpLease(clientId, hwAddr, addr, expTime, hostname);
- }
- }
-
- throw new OutOfAddressesException("No address available for offer");
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpNakPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpNakPacket.java
deleted file mode 100644
index 1da0b7300559f..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpNakPacket.java
+++ /dev/null
@@ -1,63 +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 android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-NAK packet.
- */
-class DhcpNakPacket extends DhcpPacket {
- /**
- * Generates a NAK packet with the specified parameters.
- */
- DhcpNakPacket(int transId, short secs, Inet4Address relayIp, byte[] clientMac,
- boolean broadcast) {
- super(transId, secs, INADDR_ANY /* clientIp */, INADDR_ANY /* yourIp */,
- INADDR_ANY /* nextIp */, relayIp, clientMac, broadcast);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " NAK, reason " + (mMessage == null ? "(none)" : mMessage);
- }
-
- /**
- * Fills in a packet with the requested NAK attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- // Constructor does not set values for layers <= 3: use empty values
- Inet4Address destIp = INADDR_ANY;
- Inet4Address srcIp = INADDR_ANY;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result, DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated NAK packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_NAK);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- addTlv(buffer, DHCP_MESSAGE, mMessage);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpOfferPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpOfferPacket.java
deleted file mode 100644
index 0eba77e4a6828..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpOfferPacket.java
+++ /dev/null
@@ -1,79 +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 android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-OFFER packet.
- */
-class DhcpOfferPacket extends DhcpPacket {
- /**
- * The IP address of the server which sent this packet.
- */
- private final Inet4Address mSrcIp;
-
- /**
- * Generates a OFFER packet with the specified parameters.
- */
- DhcpOfferPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address relayIp, Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, serverAddress, relayIp, clientMac, broadcast);
- mSrcIp = serverAddress;
- }
-
- public String toString() {
- String s = super.toString();
- String dnsServers = ", DNS servers: ";
-
- if (mDnsServers != null) {
- for (Inet4Address dnsServer: mDnsServers) {
- dnsServers += dnsServer + " ";
- }
- }
-
- return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask +
- dnsServers + ", gateways " + mGateways +
- " lease time " + mLeaseTime + ", domain " + mDomainName;
- }
-
- /**
- * Fills in a packet with the specified OFFER attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- Inet4Address destIp = mBroadcast ? INADDR_BROADCAST : mYourIp;
- Inet4Address srcIp = mBroadcast ? INADDR_ANY : mSrcIp;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
- DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the server-generated OFFER packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_OFFER);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
-
- addCommonServerTlvs(buffer);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java
deleted file mode 100644
index a15d42381ff0f..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java
+++ /dev/null
@@ -1,1397 +0,0 @@
-package android.net.dhcp;
-
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL;
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
-
-import android.annotation.Nullable;
-import android.net.DhcpResults;
-import android.net.LinkAddress;
-import android.net.metrics.DhcpErrorEvent;
-import android.net.shared.Inet4AddressUtils;
-import android.os.Build;
-import android.os.SystemProperties;
-import android.system.OsConstants;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.UnsupportedEncodingException;
-import java.net.Inet4Address;
-import java.net.UnknownHostException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.ShortBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Defines basic data and operations needed to build and use packets for the
- * DHCP protocol. Subclasses create the specific packets used at each
- * stage of the negotiation.
- *
- * @hide
- */
-public abstract class DhcpPacket {
- protected static final String TAG = "DhcpPacket";
-
- // TODO: use NetworkStackConstants.IPV4_MIN_MTU once this class is moved to the network stack.
- private static final int IPV4_MIN_MTU = 68;
-
- // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the
- // CPU for anything shorter than 5 minutes. For sanity's sake, this must be higher than the
- // DHCP client timeout.
- public static final int MINIMUM_LEASE = 60;
- public static final int INFINITE_LEASE = (int) 0xffffffff;
-
- public static final Inet4Address INADDR_ANY = IPV4_ADDR_ANY;
- public static final Inet4Address INADDR_BROADCAST = IPV4_ADDR_ALL;
- public static final byte[] ETHER_BROADCAST = new byte[] {
- (byte) 0xff, (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff, (byte) 0xff,
- };
-
- /**
- * Packet encapsulations.
- */
- public static final int ENCAP_L2 = 0; // EthernetII header included
- public static final int ENCAP_L3 = 1; // IP/UDP header included
- public static final int ENCAP_BOOTP = 2; // BOOTP contents only
-
- /**
- * Minimum length of a DHCP packet, excluding options, in the above encapsulations.
- */
- public static final int MIN_PACKET_LENGTH_BOOTP = 236; // See diagram in RFC 2131, section 2.
- public static final int MIN_PACKET_LENGTH_L3 = MIN_PACKET_LENGTH_BOOTP + 20 + 8;
- public static final int MIN_PACKET_LENGTH_L2 = MIN_PACKET_LENGTH_L3 + 14;
-
- public static final int HWADDR_LEN = 16;
- public static final int MAX_OPTION_LEN = 255;
-
- /**
- * The minimum and maximum MTU that we are prepared to use. We set the minimum to the minimum
- * IPv6 MTU because the IPv6 stack enters unusual codepaths when the link MTU drops below 1280,
- * and does not recover if the MTU is brought above 1280 again. We set the maximum to 1500
- * because in general it is risky to assume that the hardware is able to send/receive packets
- * larger than 1500 bytes even if the network supports it.
- */
- private static final int MIN_MTU = 1280;
- private static final int MAX_MTU = 1500;
-
- /**
- * IP layer definitions.
- */
- private static final byte IP_TYPE_UDP = (byte) 0x11;
-
- /**
- * IP: Version 4, Header Length 20 bytes
- */
- private static final byte IP_VERSION_HEADER_LEN = (byte) 0x45;
-
- /**
- * IP: Flags 0, Fragment Offset 0, Don't Fragment
- */
- private static final short IP_FLAGS_OFFSET = (short) 0x4000;
-
- /**
- * IP: TOS
- */
- private static final byte IP_TOS_LOWDELAY = (byte) 0x10;
-
- /**
- * IP: TTL -- use default 64 from RFC1340
- */
- private static final byte IP_TTL = (byte) 0x40;
-
- /**
- * The client DHCP port.
- */
- static final short DHCP_CLIENT = (short) 68;
-
- /**
- * The server DHCP port.
- */
- static final short DHCP_SERVER = (short) 67;
-
- /**
- * The message op code indicating a request from a client.
- */
- protected static final byte DHCP_BOOTREQUEST = (byte) 1;
-
- /**
- * The message op code indicating a response from the server.
- */
- protected static final byte DHCP_BOOTREPLY = (byte) 2;
-
- /**
- * The code type used to identify an Ethernet MAC address in the
- * Client-ID field.
- */
- protected static final byte CLIENT_ID_ETHER = (byte) 1;
-
- /**
- * The maximum length of a packet that can be constructed.
- */
- protected static final int MAX_LENGTH = 1500;
-
- /**
- * The magic cookie that identifies this as a DHCP packet instead of BOOTP.
- */
- private static final int DHCP_MAGIC_COOKIE = 0x63825363;
-
- /**
- * DHCP Optional Type: DHCP Subnet Mask
- */
- protected static final byte DHCP_SUBNET_MASK = 1;
- protected Inet4Address mSubnetMask;
-
- /**
- * DHCP Optional Type: DHCP Router
- */
- protected static final byte DHCP_ROUTER = 3;
- protected List mGateways;
-
- /**
- * DHCP Optional Type: DHCP DNS Server
- */
- protected static final byte DHCP_DNS_SERVER = 6;
- protected List mDnsServers;
-
- /**
- * DHCP Optional Type: DHCP Host Name
- */
- protected static final byte DHCP_HOST_NAME = 12;
- protected String mHostName;
-
- /**
- * DHCP Optional Type: DHCP DOMAIN NAME
- */
- protected static final byte DHCP_DOMAIN_NAME = 15;
- protected String mDomainName;
-
- /**
- * DHCP Optional Type: DHCP Interface MTU
- */
- protected static final byte DHCP_MTU = 26;
- protected Short mMtu;
-
- /**
- * DHCP Optional Type: DHCP BROADCAST ADDRESS
- */
- protected static final byte DHCP_BROADCAST_ADDRESS = 28;
- protected Inet4Address mBroadcastAddress;
-
- /**
- * DHCP Optional Type: Vendor specific information
- */
- protected static final byte DHCP_VENDOR_INFO = 43;
- protected String mVendorInfo;
-
- /**
- * Value of the vendor specific option used to indicate that the network is metered
- */
- public static final String VENDOR_INFO_ANDROID_METERED = "ANDROID_METERED";
-
- /**
- * DHCP Optional Type: Option overload option
- */
- protected static final byte DHCP_OPTION_OVERLOAD = 52;
-
- /**
- * Possible values of the option overload option.
- */
- private static final byte OPTION_OVERLOAD_FILE = 1;
- private static final byte OPTION_OVERLOAD_SNAME = 2;
- private static final byte OPTION_OVERLOAD_BOTH = 3;
-
- /**
- * DHCP Optional Type: DHCP Requested IP Address
- */
- protected static final byte DHCP_REQUESTED_IP = 50;
- protected Inet4Address mRequestedIp;
-
- /**
- * DHCP Optional Type: DHCP Lease Time
- */
- protected static final byte DHCP_LEASE_TIME = 51;
- protected Integer mLeaseTime;
-
- /**
- * DHCP Optional Type: DHCP Message Type
- */
- protected static final byte DHCP_MESSAGE_TYPE = 53;
- // the actual type values
- protected static final byte DHCP_MESSAGE_TYPE_DISCOVER = 1;
- protected static final byte DHCP_MESSAGE_TYPE_OFFER = 2;
- protected static final byte DHCP_MESSAGE_TYPE_REQUEST = 3;
- protected static final byte DHCP_MESSAGE_TYPE_DECLINE = 4;
- protected static final byte DHCP_MESSAGE_TYPE_ACK = 5;
- protected static final byte DHCP_MESSAGE_TYPE_NAK = 6;
- protected static final byte DHCP_MESSAGE_TYPE_RELEASE = 7;
- protected static final byte DHCP_MESSAGE_TYPE_INFORM = 8;
-
- /**
- * DHCP Optional Type: DHCP Server Identifier
- */
- protected static final byte DHCP_SERVER_IDENTIFIER = 54;
- protected Inet4Address mServerIdentifier;
-
- /**
- * DHCP Optional Type: DHCP Parameter List
- */
- protected static final byte DHCP_PARAMETER_LIST = 55;
- protected byte[] mRequestedParams;
-
- /**
- * DHCP Optional Type: DHCP MESSAGE
- */
- protected static final byte DHCP_MESSAGE = 56;
- protected String mMessage;
-
- /**
- * DHCP Optional Type: Maximum DHCP Message Size
- */
- protected static final byte DHCP_MAX_MESSAGE_SIZE = 57;
- protected Short mMaxMessageSize;
-
- /**
- * DHCP Optional Type: DHCP Renewal Time Value
- */
- protected static final byte DHCP_RENEWAL_TIME = 58;
- protected Integer mT1;
-
- /**
- * DHCP Optional Type: Rebinding Time Value
- */
- protected static final byte DHCP_REBINDING_TIME = 59;
- protected Integer mT2;
-
- /**
- * DHCP Optional Type: Vendor Class Identifier
- */
- protected static final byte DHCP_VENDOR_CLASS_ID = 60;
- protected String mVendorId;
-
- /**
- * DHCP Optional Type: DHCP Client Identifier
- */
- protected static final byte DHCP_CLIENT_IDENTIFIER = 61;
- protected byte[] mClientId;
-
- /**
- * DHCP zero-length option code: pad
- */
- protected static final byte DHCP_OPTION_PAD = 0x00;
-
- /**
- * DHCP zero-length option code: end of options
- */
- protected static final byte DHCP_OPTION_END = (byte) 0xff;
-
- /**
- * The transaction identifier used in this particular DHCP negotiation
- */
- protected final int mTransId;
-
- /**
- * The seconds field in the BOOTP header. Per RFC, should be nonzero in client requests only.
- */
- protected final short mSecs;
-
- /**
- * The IP address of the client host. This address is typically
- * proposed by the client (from an earlier DHCP negotiation) or
- * supplied by the server.
- */
- protected final Inet4Address mClientIp;
- protected final Inet4Address mYourIp;
- private final Inet4Address mNextIp;
- protected final Inet4Address mRelayIp;
-
- /**
- * Does the client request a broadcast response?
- */
- protected boolean mBroadcast;
-
- /**
- * The six-octet MAC of the client.
- */
- protected final byte[] mClientMac;
-
- /**
- * The server host name from server.
- */
- protected String mServerHostName;
-
- /**
- * Asks the packet object to create a ByteBuffer serialization of
- * the packet for transmission.
- */
- public abstract ByteBuffer buildPacket(int encap, short destUdp,
- short srcUdp);
-
- /**
- * Allows the concrete class to fill in packet-type-specific details,
- * typically optional parameters at the end of the packet.
- */
- abstract void finishPacket(ByteBuffer buffer);
-
- // Set in unit tests, to ensure that the test does not break when run on different devices and
- // on different releases.
- static String testOverrideVendorId = null;
- static String testOverrideHostname = null;
-
- protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
- Inet4Address nextIp, Inet4Address relayIp,
- byte[] clientMac, boolean broadcast) {
- mTransId = transId;
- mSecs = secs;
- mClientIp = clientIp;
- mYourIp = yourIp;
- mNextIp = nextIp;
- mRelayIp = relayIp;
- mClientMac = clientMac;
- mBroadcast = broadcast;
- }
-
- /**
- * Returns the transaction ID.
- */
- public int getTransactionId() {
- return mTransId;
- }
-
- /**
- * Returns the client MAC.
- */
- public byte[] getClientMac() {
- return mClientMac;
- }
-
- // TODO: refactor DhcpClient to set clientId when constructing packets and remove
- // hasExplicitClientId logic
- /**
- * Returns whether a client ID was set in the options for this packet.
- */
- public boolean hasExplicitClientId() {
- return mClientId != null;
- }
-
- /**
- * Convenience method to return the client ID if it was set explicitly, or null otherwise.
- */
- @Nullable
- public byte[] getExplicitClientIdOrNull() {
- return hasExplicitClientId() ? getClientId() : null;
- }
-
- /**
- * Returns the client ID. If not set explicitly, this follows RFC 2132 and creates a client ID
- * based on the hardware address.
- */
- public byte[] getClientId() {
- final byte[] clientId;
- if (hasExplicitClientId()) {
- clientId = Arrays.copyOf(mClientId, mClientId.length);
- } else {
- clientId = new byte[mClientMac.length + 1];
- clientId[0] = CLIENT_ID_ETHER;
- System.arraycopy(mClientMac, 0, clientId, 1, mClientMac.length);
- }
- return clientId;
- }
-
- /**
- * Returns whether a parameter is included in the parameter request list option of this packet.
- *
- *
If there is no parameter request list option in the packet, false is returned.
- *
- * @param paramId ID of the parameter, such as {@link #DHCP_MTU} or {@link #DHCP_HOST_NAME}.
- */
- public boolean hasRequestedParam(byte paramId) {
- if (mRequestedParams == null) {
- return false;
- }
-
- for (byte reqParam : mRequestedParams) {
- if (reqParam == paramId) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Creates a new L3 packet (including IP header) containing the
- * DHCP udp packet. This method relies upon the delegated method
- * finishPacket() to insert the per-packet contents.
- */
- protected void fillInPacket(int encap, Inet4Address destIp,
- Inet4Address srcIp, short destUdp, short srcUdp, ByteBuffer buf,
- byte requestCode, boolean broadcast) {
- byte[] destIpArray = destIp.getAddress();
- byte[] srcIpArray = srcIp.getAddress();
- int ipHeaderOffset = 0;
- int ipLengthOffset = 0;
- int ipChecksumOffset = 0;
- int endIpHeader = 0;
- int udpHeaderOffset = 0;
- int udpLengthOffset = 0;
- int udpChecksumOffset = 0;
-
- buf.clear();
- buf.order(ByteOrder.BIG_ENDIAN);
-
- if (encap == ENCAP_L2) {
- buf.put(ETHER_BROADCAST);
- buf.put(mClientMac);
- buf.putShort((short) OsConstants.ETH_P_IP);
- }
-
- // if a full IP packet needs to be generated, put the IP & UDP
- // headers in place, and pre-populate with artificial values
- // needed to seed the IP checksum.
- if (encap <= ENCAP_L3) {
- ipHeaderOffset = buf.position();
- buf.put(IP_VERSION_HEADER_LEN);
- buf.put(IP_TOS_LOWDELAY); // tos: IPTOS_LOWDELAY
- ipLengthOffset = buf.position();
- buf.putShort((short)0); // length
- buf.putShort((short)0); // id
- buf.putShort(IP_FLAGS_OFFSET); // ip offset: don't fragment
- buf.put(IP_TTL); // TTL: use default 64 from RFC1340
- buf.put(IP_TYPE_UDP);
- ipChecksumOffset = buf.position();
- buf.putShort((short) 0); // checksum
-
- buf.put(srcIpArray);
- buf.put(destIpArray);
- endIpHeader = buf.position();
-
- // UDP header
- udpHeaderOffset = buf.position();
- buf.putShort(srcUdp);
- buf.putShort(destUdp);
- udpLengthOffset = buf.position();
- buf.putShort((short) 0); // length
- udpChecksumOffset = buf.position();
- buf.putShort((short) 0); // UDP checksum -- initially zero
- }
-
- // DHCP payload
- buf.put(requestCode);
- buf.put((byte) 1); // Hardware Type: Ethernet
- buf.put((byte) mClientMac.length); // Hardware Address Length
- buf.put((byte) 0); // Hop Count
- buf.putInt(mTransId); // Transaction ID
- buf.putShort(mSecs); // Elapsed Seconds
-
- if (broadcast) {
- buf.putShort((short) 0x8000); // Flags
- } else {
- buf.putShort((short) 0x0000); // Flags
- }
-
- buf.put(mClientIp.getAddress());
- buf.put(mYourIp.getAddress());
- buf.put(mNextIp.getAddress());
- buf.put(mRelayIp.getAddress());
- buf.put(mClientMac);
- buf.position(buf.position() +
- (HWADDR_LEN - mClientMac.length) // pad addr to 16 bytes
- + 64 // empty server host name (64 bytes)
- + 128); // empty boot file name (128 bytes)
- buf.putInt(DHCP_MAGIC_COOKIE); // magic number
- finishPacket(buf);
-
- // round up to an even number of octets
- if ((buf.position() & 1) == 1) {
- buf.put((byte) 0);
- }
-
- // If an IP packet is being built, the IP & UDP checksums must be
- // computed.
- if (encap <= ENCAP_L3) {
- // fix UDP header: insert length
- short udpLen = (short)(buf.position() - udpHeaderOffset);
- buf.putShort(udpLengthOffset, udpLen);
- // fix UDP header: checksum
- // checksum for UDP at udpChecksumOffset
- int udpSeed = 0;
-
- // apply IPv4 pseudo-header. Read IP address src and destination
- // values from the IP header and accumulate checksum.
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 2));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 4));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 6));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 8));
-
- // accumulate extra data for the pseudo-header
- udpSeed += IP_TYPE_UDP;
- udpSeed += udpLen;
- // and compute UDP checksum
- buf.putShort(udpChecksumOffset, (short) checksum(buf, udpSeed,
- udpHeaderOffset,
- buf.position()));
- // fix IP header: insert length
- buf.putShort(ipLengthOffset, (short)(buf.position() - ipHeaderOffset));
- // fixup IP-header checksum
- buf.putShort(ipChecksumOffset,
- (short) checksum(buf, 0, ipHeaderOffset, endIpHeader));
- }
- }
-
- /**
- * Converts a signed short value to an unsigned int value. Needed
- * because Java does not have unsigned types.
- */
- private static int intAbs(short v) {
- return v & 0xFFFF;
- }
-
- /**
- * Performs an IP checksum (used in IP header and across UDP
- * payload) on the specified portion of a ByteBuffer. The seed
- * allows the checksum to commence with a specified value.
- */
- private int checksum(ByteBuffer buf, int seed, int start, int end) {
- int sum = seed;
- int bufPosition = buf.position();
-
- // set position of original ByteBuffer, so that the ShortBuffer
- // will be correctly initialized
- buf.position(start);
- ShortBuffer shortBuf = buf.asShortBuffer();
-
- // re-set ByteBuffer position
- buf.position(bufPosition);
-
- short[] shortArray = new short[(end - start) / 2];
- shortBuf.get(shortArray);
-
- for (short s : shortArray) {
- sum += intAbs(s);
- }
-
- start += shortArray.length * 2;
-
- // see if a singleton byte remains
- if (end != start) {
- short b = buf.get(start);
-
- // make it unsigned
- if (b < 0) {
- b += 256;
- }
-
- sum += b * 256;
- }
-
- sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
- sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
- int negated = ~sum;
- return intAbs((short) negated);
- }
-
- /**
- * Adds an optional parameter containing a single byte value.
- */
- protected static void addTlv(ByteBuffer buf, byte type, byte value) {
- buf.put(type);
- buf.put((byte) 1);
- buf.put(value);
- }
-
- /**
- * Adds an optional parameter containing an array of bytes.
- *
- *
This method is a no-op if the payload argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable byte[] payload) {
- if (payload != null) {
- if (payload.length > MAX_OPTION_LEN) {
- throw new IllegalArgumentException("DHCP option too long: "
- + payload.length + " vs. " + MAX_OPTION_LEN);
- }
- buf.put(type);
- buf.put((byte) payload.length);
- buf.put(payload);
- }
- }
-
- /**
- * Adds an optional parameter containing an IP address.
- *
- *
This method is a no-op if the address argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable Inet4Address addr) {
- if (addr != null) {
- addTlv(buf, type, addr.getAddress());
- }
- }
-
- /**
- * Adds an optional parameter containing a list of IP addresses.
- *
- *
This method is a no-op if the addresses argument is null or empty.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable List addrs) {
- if (addrs == null || addrs.size() == 0) return;
-
- int optionLen = 4 * addrs.size();
- if (optionLen > MAX_OPTION_LEN) {
- throw new IllegalArgumentException("DHCP option too long: "
- + optionLen + " vs. " + MAX_OPTION_LEN);
- }
-
- buf.put(type);
- buf.put((byte)(optionLen));
-
- for (Inet4Address addr : addrs) {
- buf.put(addr.getAddress());
- }
- }
-
- /**
- * Adds an optional parameter containing a short integer.
- *
- *
This method is a no-op if the value argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable Short value) {
- if (value != null) {
- buf.put(type);
- buf.put((byte) 2);
- buf.putShort(value.shortValue());
- }
- }
-
- /**
- * Adds an optional parameter containing a simple integer.
- *
- *
This method is a no-op if the value argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable Integer value) {
- if (value != null) {
- buf.put(type);
- buf.put((byte) 4);
- buf.putInt(value.intValue());
- }
- }
-
- /**
- * Adds an optional parameter containing an ASCII string.
- *
- *
This method is a no-op if the string argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable String str) {
- if (str != null) {
- try {
- addTlv(buf, type, str.getBytes("US-ASCII"));
- } catch (UnsupportedEncodingException e) {
- throw new IllegalArgumentException("String is not US-ASCII: " + str);
- }
- }
- }
-
- /**
- * Adds the special end-of-optional-parameters indicator.
- */
- protected static void addTlvEnd(ByteBuffer buf) {
- buf.put((byte) 0xFF);
- }
-
- private String getVendorId() {
- if (testOverrideVendorId != null) return testOverrideVendorId;
- return "android-dhcp-" + Build.VERSION.RELEASE;
- }
-
- private String getHostname() {
- if (testOverrideHostname != null) return testOverrideHostname;
- return SystemProperties.get("net.hostname");
- }
-
- /**
- * Adds common client TLVs.
- *
- * TODO: Does this belong here? The alternative would be to modify all the buildXyzPacket
- * methods to take them.
- */
- protected void addCommonClientTlvs(ByteBuffer buf) {
- addTlv(buf, DHCP_MAX_MESSAGE_SIZE, (short) MAX_LENGTH);
- addTlv(buf, DHCP_VENDOR_CLASS_ID, getVendorId());
- final String hn = getHostname();
- if (!TextUtils.isEmpty(hn)) addTlv(buf, DHCP_HOST_NAME, hn);
- }
-
- protected void addCommonServerTlvs(ByteBuffer buf) {
- addTlv(buf, DHCP_LEASE_TIME, mLeaseTime);
- if (mLeaseTime != null && mLeaseTime != INFINITE_LEASE) {
- // The client should renew at 1/2 the lease-expiry interval
- addTlv(buf, DHCP_RENEWAL_TIME, (int) (Integer.toUnsignedLong(mLeaseTime) / 2));
- // Default rebinding time is set as below by RFC2131
- addTlv(buf, DHCP_REBINDING_TIME,
- (int) (Integer.toUnsignedLong(mLeaseTime) * 875L / 1000L));
- }
- addTlv(buf, DHCP_SUBNET_MASK, mSubnetMask);
- addTlv(buf, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
- addTlv(buf, DHCP_ROUTER, mGateways);
- addTlv(buf, DHCP_DNS_SERVER, mDnsServers);
- addTlv(buf, DHCP_DOMAIN_NAME, mDomainName);
- addTlv(buf, DHCP_HOST_NAME, mHostName);
- addTlv(buf, DHCP_VENDOR_INFO, mVendorInfo);
- if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
- addTlv(buf, DHCP_MTU, mMtu);
- }
- }
-
- /**
- * Converts a MAC from an array of octets to an ASCII string.
- */
- public static String macToString(byte[] mac) {
- String macAddr = "";
-
- for (int i = 0; i < mac.length; i++) {
- String hexString = "0" + Integer.toHexString(mac[i]);
-
- // substring operation grabs the last 2 digits: this
- // allows signed bytes to be converted correctly.
- macAddr += hexString.substring(hexString.length() - 2);
-
- if (i != (mac.length - 1)) {
- macAddr += ":";
- }
- }
-
- return macAddr;
- }
-
- public String toString() {
- String macAddr = macToString(mClientMac);
-
- return macAddr;
- }
-
- /**
- * Reads a four-octet value from a ByteBuffer and construct
- * an IPv4 address from that value.
- */
- private static Inet4Address readIpAddress(ByteBuffer packet) {
- Inet4Address result = null;
- byte[] ipAddr = new byte[4];
- packet.get(ipAddr);
-
- try {
- result = (Inet4Address) Inet4Address.getByAddress(ipAddr);
- } catch (UnknownHostException ex) {
- // ipAddr is numeric, so this should not be
- // triggered. However, if it is, just nullify
- result = null;
- }
-
- return result;
- }
-
- /**
- * Reads a string of specified length from the buffer.
- */
- private static String readAsciiString(ByteBuffer buf, int byteCount, boolean nullOk) {
- byte[] bytes = new byte[byteCount];
- buf.get(bytes);
- int length = bytes.length;
- if (!nullOk) {
- // Stop at the first null byte. This is because some DHCP options (e.g., the domain
- // name) are passed to netd via FrameworkListener, which refuses arguments containing
- // null bytes. We don't do this by default because vendorInfo is an opaque string which
- // could in theory contain null bytes.
- for (length = 0; length < bytes.length; length++) {
- if (bytes[length] == 0) {
- break;
- }
- }
- }
- return new String(bytes, 0, length, StandardCharsets.US_ASCII);
- }
-
- private static boolean isPacketToOrFromClient(short udpSrcPort, short udpDstPort) {
- return (udpSrcPort == DHCP_CLIENT) || (udpDstPort == DHCP_CLIENT);
- }
-
- private static boolean isPacketServerToServer(short udpSrcPort, short udpDstPort) {
- return (udpSrcPort == DHCP_SERVER) && (udpDstPort == DHCP_SERVER);
- }
-
- public static class ParseException extends Exception {
- public final int errorCode;
- public ParseException(int errorCode, String msg, Object... args) {
- super(String.format(msg, args));
- this.errorCode = errorCode;
- }
- }
-
- /**
- * Creates a concrete DhcpPacket from the supplied ByteBuffer. The
- * buffer may have an L2 encapsulation (which is the full EthernetII
- * format starting with the source-address MAC) or an L3 encapsulation
- * (which starts with the IP header).
- *
- * A subset of the optional parameters are parsed and are stored
- * in object fields.
- */
- @VisibleForTesting
- static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException
- {
- // bootp parameters
- int transactionId;
- short secs;
- Inet4Address clientIp;
- Inet4Address yourIp;
- Inet4Address nextIp;
- Inet4Address relayIp;
- byte[] clientMac;
- byte[] clientId = null;
- List dnsServers = new ArrayList<>();
- List gateways = new ArrayList<>(); // aka router
- Inet4Address serverIdentifier = null;
- Inet4Address netMask = null;
- String message = null;
- String vendorId = null;
- String vendorInfo = null;
- byte[] expectedParams = null;
- String hostName = null;
- String domainName = null;
- Inet4Address ipSrc = null;
- Inet4Address ipDst = null;
- Inet4Address bcAddr = null;
- Inet4Address requestedIp = null;
- String serverHostName;
- byte optionOverload = 0;
-
- // The following are all unsigned integers. Internally we store them as signed integers of
- // the same length because that way we're guaranteed that they can't be out of the range of
- // the unsigned field in the packet. Callers wanting to pass in an unsigned value will need
- // to cast it.
- Short mtu = null;
- Short maxMessageSize = null;
- Integer leaseTime = null;
- Integer T1 = null;
- Integer T2 = null;
-
- // dhcp options
- byte dhcpType = (byte) 0xFF;
-
- packet.order(ByteOrder.BIG_ENDIAN);
-
- // check to see if we need to parse L2, IP, and UDP encaps
- if (pktType == ENCAP_L2) {
- if (packet.remaining() < MIN_PACKET_LENGTH_L2) {
- throw new ParseException(DhcpErrorEvent.L2_TOO_SHORT,
- "L2 packet too short, %d < %d", packet.remaining(), MIN_PACKET_LENGTH_L2);
- }
-
- byte[] l2dst = new byte[6];
- byte[] l2src = new byte[6];
-
- packet.get(l2dst);
- packet.get(l2src);
-
- short l2type = packet.getShort();
-
- if (l2type != OsConstants.ETH_P_IP) {
- throw new ParseException(DhcpErrorEvent.L2_WRONG_ETH_TYPE,
- "Unexpected L2 type 0x%04x, expected 0x%04x", l2type, OsConstants.ETH_P_IP);
- }
- }
-
- if (pktType <= ENCAP_L3) {
- if (packet.remaining() < MIN_PACKET_LENGTH_L3) {
- throw new ParseException(DhcpErrorEvent.L3_TOO_SHORT,
- "L3 packet too short, %d < %d", packet.remaining(), MIN_PACKET_LENGTH_L3);
- }
-
- byte ipTypeAndLength = packet.get();
- int ipVersion = (ipTypeAndLength & 0xf0) >> 4;
- if (ipVersion != 4) {
- throw new ParseException(
- DhcpErrorEvent.L3_NOT_IPV4, "Invalid IP version %d", ipVersion);
- }
-
- // System.out.println("ipType is " + ipType);
- byte ipDiffServicesField = packet.get();
- short ipTotalLength = packet.getShort();
- short ipIdentification = packet.getShort();
- byte ipFlags = packet.get();
- byte ipFragOffset = packet.get();
- byte ipTTL = packet.get();
- byte ipProto = packet.get();
- short ipChksm = packet.getShort();
-
- ipSrc = readIpAddress(packet);
- ipDst = readIpAddress(packet);
-
- if (ipProto != IP_TYPE_UDP) {
- throw new ParseException(
- DhcpErrorEvent.L4_NOT_UDP, "Protocol not UDP: %d", ipProto);
- }
-
- // Skip options. This cannot cause us to read beyond the end of the buffer because the
- // IPv4 header cannot be more than (0x0f * 4) = 60 bytes long, and that is less than
- // MIN_PACKET_LENGTH_L3.
- int optionWords = ((ipTypeAndLength & 0x0f) - 5);
- for (int i = 0; i < optionWords; i++) {
- packet.getInt();
- }
-
- // assume UDP
- short udpSrcPort = packet.getShort();
- short udpDstPort = packet.getShort();
- short udpLen = packet.getShort();
- short udpChkSum = packet.getShort();
-
- // Only accept packets to or from the well-known client port (expressly permitting
- // packets from ports other than the well-known server port; http://b/24687559), and
- // server-to-server packets, e.g. for relays.
- if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) &&
- !isPacketServerToServer(udpSrcPort, udpDstPort)) {
- // This should almost never happen because we use SO_ATTACH_FILTER on the packet
- // socket to drop packets that don't have the right source ports. However, it's
- // possible that a packet arrives between when the socket is bound and when the
- // filter is set. http://b/26696823 .
- throw new ParseException(DhcpErrorEvent.L4_WRONG_PORT,
- "Unexpected UDP ports %d->%d", udpSrcPort, udpDstPort);
- }
- }
-
- // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length.
- if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) {
- throw new ParseException(DhcpErrorEvent.BOOTP_TOO_SHORT,
- "Invalid type or BOOTP packet too short, %d < %d",
- packet.remaining(), MIN_PACKET_LENGTH_BOOTP);
- }
-
- byte type = packet.get();
- byte hwType = packet.get();
- int addrLen = packet.get() & 0xff;
- byte hops = packet.get();
- transactionId = packet.getInt();
- secs = packet.getShort();
- short bootpFlags = packet.getShort();
- boolean broadcast = (bootpFlags & 0x8000) != 0;
- byte[] ipv4addr = new byte[4];
-
- try {
- packet.get(ipv4addr);
- clientIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- yourIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- nextIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- } catch (UnknownHostException ex) {
- throw new ParseException(DhcpErrorEvent.L3_INVALID_IP,
- "Invalid IPv4 address: %s", Arrays.toString(ipv4addr));
- }
-
- // Some DHCP servers have been known to announce invalid client hardware address values such
- // as 0xff. The legacy DHCP client accepted these becuause it does not check the length at
- // all but only checks that the interface MAC address matches the first bytes of the address
- // in the packets. We're a bit stricter: if the length is obviously invalid (i.e., bigger
- // than the size of the field), we fudge it to 6 (Ethernet). http://b/23725795
- // TODO: evaluate whether to make this test more liberal.
- if (addrLen > HWADDR_LEN) {
- addrLen = ETHER_BROADCAST.length;
- }
-
- clientMac = new byte[addrLen];
- packet.get(clientMac);
-
- // skip over address padding (16 octets allocated)
- packet.position(packet.position() + (16 - addrLen));
- serverHostName = readAsciiString(packet, 64, false);
- packet.position(packet.position() + 128);
-
- // Ensure this is a DHCP packet with a magic cookie, and not BOOTP. http://b/31850211
- if (packet.remaining() < 4) {
- throw new ParseException(DhcpErrorEvent.DHCP_NO_COOKIE, "not a DHCP message");
- }
-
- int dhcpMagicCookie = packet.getInt();
- if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) {
- throw new ParseException(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE,
- "Bad magic cookie 0x%08x, should be 0x%08x",
- dhcpMagicCookie, DHCP_MAGIC_COOKIE);
- }
-
- // parse options
- boolean notFinishedOptions = true;
-
- while ((packet.position() < packet.limit()) && notFinishedOptions) {
- final byte optionType = packet.get(); // cannot underflow because position < limit
- try {
- if (optionType == DHCP_OPTION_END) {
- notFinishedOptions = false;
- } else if (optionType == DHCP_OPTION_PAD) {
- // The pad option doesn't have a length field. Nothing to do.
- } else {
- int optionLen = packet.get() & 0xFF;
- int expectedLen = 0;
-
- switch(optionType) {
- case DHCP_SUBNET_MASK:
- netMask = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_ROUTER:
- for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
- gateways.add(readIpAddress(packet));
- }
- break;
- case DHCP_DNS_SERVER:
- for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
- dnsServers.add(readIpAddress(packet));
- }
- break;
- case DHCP_HOST_NAME:
- expectedLen = optionLen;
- hostName = readAsciiString(packet, optionLen, false);
- break;
- case DHCP_MTU:
- expectedLen = 2;
- mtu = packet.getShort();
- break;
- case DHCP_DOMAIN_NAME:
- expectedLen = optionLen;
- domainName = readAsciiString(packet, optionLen, false);
- break;
- case DHCP_BROADCAST_ADDRESS:
- bcAddr = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_REQUESTED_IP:
- requestedIp = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_LEASE_TIME:
- leaseTime = Integer.valueOf(packet.getInt());
- expectedLen = 4;
- break;
- case DHCP_MESSAGE_TYPE:
- dhcpType = packet.get();
- expectedLen = 1;
- break;
- case DHCP_SERVER_IDENTIFIER:
- serverIdentifier = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_PARAMETER_LIST:
- expectedParams = new byte[optionLen];
- packet.get(expectedParams);
- expectedLen = optionLen;
- break;
- case DHCP_MESSAGE:
- expectedLen = optionLen;
- message = readAsciiString(packet, optionLen, false);
- break;
- case DHCP_MAX_MESSAGE_SIZE:
- expectedLen = 2;
- maxMessageSize = Short.valueOf(packet.getShort());
- break;
- case DHCP_RENEWAL_TIME:
- expectedLen = 4;
- T1 = Integer.valueOf(packet.getInt());
- break;
- case DHCP_REBINDING_TIME:
- expectedLen = 4;
- T2 = Integer.valueOf(packet.getInt());
- break;
- case DHCP_VENDOR_CLASS_ID:
- expectedLen = optionLen;
- // Embedded nulls are safe as this does not get passed to netd.
- vendorId = readAsciiString(packet, optionLen, true);
- break;
- case DHCP_CLIENT_IDENTIFIER: { // Client identifier
- byte[] id = new byte[optionLen];
- packet.get(id);
- expectedLen = optionLen;
- } break;
- case DHCP_VENDOR_INFO:
- expectedLen = optionLen;
- // Embedded nulls are safe as this does not get passed to netd.
- vendorInfo = readAsciiString(packet, optionLen, true);
- break;
- case DHCP_OPTION_OVERLOAD:
- expectedLen = 1;
- optionOverload = packet.get();
- optionOverload &= OPTION_OVERLOAD_BOTH;
- break;
- default:
- // ignore any other parameters
- for (int i = 0; i < optionLen; i++) {
- expectedLen++;
- byte throwaway = packet.get();
- }
- }
-
- if (expectedLen != optionLen) {
- final int errorCode = DhcpErrorEvent.errorCodeWithOption(
- DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH, optionType);
- throw new ParseException(errorCode,
- "Invalid length %d for option %d, expected %d",
- optionLen, optionType, expectedLen);
- }
- }
- } catch (BufferUnderflowException e) {
- final int errorCode = DhcpErrorEvent.errorCodeWithOption(
- DhcpErrorEvent.BUFFER_UNDERFLOW, optionType);
- throw new ParseException(errorCode, "BufferUnderflowException");
- }
- }
-
- DhcpPacket newPacket;
-
- switch(dhcpType) {
- case (byte) 0xFF:
- throw new ParseException(DhcpErrorEvent.DHCP_NO_MSG_TYPE,
- "No DHCP message type option");
- case DHCP_MESSAGE_TYPE_DISCOVER:
- newPacket = new DhcpDiscoverPacket(transactionId, secs, relayIp, clientMac,
- broadcast, ipSrc);
- break;
- case DHCP_MESSAGE_TYPE_OFFER:
- newPacket = new DhcpOfferPacket(
- transactionId, secs, broadcast, ipSrc, relayIp, clientIp, yourIp, clientMac);
- break;
- case DHCP_MESSAGE_TYPE_REQUEST:
- newPacket = new DhcpRequestPacket(
- transactionId, secs, clientIp, relayIp, clientMac, broadcast);
- break;
- case DHCP_MESSAGE_TYPE_DECLINE:
- newPacket = new DhcpDeclinePacket(
- transactionId, secs, clientIp, yourIp, nextIp, relayIp,
- clientMac);
- break;
- case DHCP_MESSAGE_TYPE_ACK:
- newPacket = new DhcpAckPacket(
- transactionId, secs, broadcast, ipSrc, relayIp, clientIp, yourIp, clientMac);
- break;
- case DHCP_MESSAGE_TYPE_NAK:
- newPacket = new DhcpNakPacket(
- transactionId, secs, relayIp, clientMac, broadcast);
- break;
- case DHCP_MESSAGE_TYPE_RELEASE:
- if (serverIdentifier == null) {
- throw new ParseException(DhcpErrorEvent.MISC_ERROR,
- "DHCPRELEASE without server identifier");
- }
- newPacket = new DhcpReleasePacket(
- transactionId, serverIdentifier, clientIp, relayIp, clientMac);
- break;
- case DHCP_MESSAGE_TYPE_INFORM:
- newPacket = new DhcpInformPacket(
- transactionId, secs, clientIp, yourIp, nextIp, relayIp,
- clientMac);
- break;
- default:
- throw new ParseException(DhcpErrorEvent.DHCP_UNKNOWN_MSG_TYPE,
- "Unimplemented DHCP type %d", dhcpType);
- }
-
- newPacket.mBroadcastAddress = bcAddr;
- newPacket.mClientId = clientId;
- newPacket.mDnsServers = dnsServers;
- newPacket.mDomainName = domainName;
- newPacket.mGateways = gateways;
- newPacket.mHostName = hostName;
- newPacket.mLeaseTime = leaseTime;
- newPacket.mMessage = message;
- newPacket.mMtu = mtu;
- newPacket.mRequestedIp = requestedIp;
- newPacket.mRequestedParams = expectedParams;
- newPacket.mServerIdentifier = serverIdentifier;
- newPacket.mSubnetMask = netMask;
- newPacket.mMaxMessageSize = maxMessageSize;
- newPacket.mT1 = T1;
- newPacket.mT2 = T2;
- newPacket.mVendorId = vendorId;
- newPacket.mVendorInfo = vendorInfo;
- if ((optionOverload & OPTION_OVERLOAD_SNAME) == 0) {
- newPacket.mServerHostName = serverHostName;
- } else {
- newPacket.mServerHostName = "";
- }
- return newPacket;
- }
-
- /**
- * Parse a packet from an array of bytes, stopping at the given length.
- */
- public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType)
- throws ParseException {
- ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN);
- try {
- return decodeFullPacket(buffer, pktType);
- } catch (ParseException e) {
- throw e;
- } catch (Exception e) {
- throw new ParseException(DhcpErrorEvent.PARSING_ERROR, e.getMessage());
- }
- }
-
- /**
- * Construct a DhcpResults object from a DHCP reply packet.
- */
- public DhcpResults toDhcpResults() {
- Inet4Address ipAddress = mYourIp;
- if (ipAddress.equals(IPV4_ADDR_ANY)) {
- ipAddress = mClientIp;
- if (ipAddress.equals(IPV4_ADDR_ANY)) {
- return null;
- }
- }
-
- int prefixLength;
- if (mSubnetMask != null) {
- try {
- prefixLength = Inet4AddressUtils.netmaskToPrefixLength(mSubnetMask);
- } catch (IllegalArgumentException e) {
- // Non-contiguous netmask.
- return null;
- }
- } else {
- prefixLength = Inet4AddressUtils.getImplicitNetmask(ipAddress);
- }
-
- DhcpResults results = new DhcpResults();
- try {
- results.ipAddress = new LinkAddress(ipAddress, prefixLength);
- } catch (IllegalArgumentException e) {
- return null;
- }
-
- if (mGateways.size() > 0) {
- results.gateway = mGateways.get(0);
- }
-
- results.dnsServers.addAll(mDnsServers);
- results.domains = mDomainName;
- results.serverAddress = mServerIdentifier;
- results.vendorInfo = mVendorInfo;
- results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE;
- results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0;
- results.serverHostName = mServerHostName;
-
- return results;
- }
-
- /**
- * Returns the parsed lease time, in milliseconds, or 0 for infinite.
- */
- public long getLeaseTimeMillis() {
- // dhcpcd treats the lack of a lease time option as an infinite lease.
- if (mLeaseTime == null || mLeaseTime == INFINITE_LEASE) {
- return 0;
- } else if (0 <= mLeaseTime && mLeaseTime < MINIMUM_LEASE) {
- return MINIMUM_LEASE * 1000;
- } else {
- return (mLeaseTime & 0xffffffffL) * 1000;
- }
- }
-
- /**
- * Builds a DHCP-DISCOVER packet from the required specified
- * parameters.
- */
- public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
- short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams) {
- DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */,
- clientMac, broadcast, INADDR_ANY /* srcIp */);
- pkt.mRequestedParams = expectedParams;
- return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
- }
-
- /**
- * Builds a DHCP-OFFER packet from the required specified
- * parameters.
- */
- public static ByteBuffer buildOfferPacket(int encap, int transactionId,
- boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp,
- Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
- Inet4Address bcAddr, List gateways, List dnsServers,
- Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
- short mtu) {
- DhcpPacket pkt = new DhcpOfferPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
- INADDR_ANY /* clientIp */, yourIp, mac);
- pkt.mGateways = gateways;
- pkt.mDnsServers = dnsServers;
- pkt.mLeaseTime = timeout;
- pkt.mDomainName = domainName;
- pkt.mHostName = hostname;
- pkt.mServerIdentifier = dhcpServerIdentifier;
- pkt.mSubnetMask = netMask;
- pkt.mBroadcastAddress = bcAddr;
- pkt.mMtu = mtu;
- if (metered) {
- pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
- }
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-ACK packet from the required specified parameters.
- */
- public static ByteBuffer buildAckPacket(int encap, int transactionId,
- boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp,
- Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
- Inet4Address bcAddr, List gateways, List dnsServers,
- Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
- short mtu) {
- DhcpPacket pkt = new DhcpAckPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
- mac);
- pkt.mGateways = gateways;
- pkt.mDnsServers = dnsServers;
- pkt.mLeaseTime = timeout;
- pkt.mDomainName = domainName;
- pkt.mHostName = hostname;
- pkt.mSubnetMask = netMask;
- pkt.mServerIdentifier = dhcpServerIdentifier;
- pkt.mBroadcastAddress = bcAddr;
- pkt.mMtu = mtu;
- if (metered) {
- pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
- }
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-NAK packet from the required specified parameters.
- */
- public static ByteBuffer buildNakPacket(int encap, int transactionId, Inet4Address serverIpAddr,
- Inet4Address relayIp, byte[] mac, boolean broadcast, String message) {
- DhcpPacket pkt = new DhcpNakPacket(
- transactionId, (short) 0, relayIp, mac, broadcast);
- pkt.mMessage = message;
- pkt.mServerIdentifier = serverIpAddr;
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-REQUEST packet from the required specified parameters.
- */
- public static ByteBuffer buildRequestPacket(int encap,
- int transactionId, short secs, Inet4Address clientIp, boolean broadcast,
- byte[] clientMac, Inet4Address requestedIpAddress,
- Inet4Address serverIdentifier, byte[] requestedParams, String hostName) {
- DhcpPacket pkt = new DhcpRequestPacket(transactionId, secs, clientIp,
- INADDR_ANY /* relayIp */, clientMac, broadcast);
- pkt.mRequestedIp = requestedIpAddress;
- pkt.mServerIdentifier = serverIdentifier;
- pkt.mHostName = hostName;
- pkt.mRequestedParams = requestedParams;
- ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
- return result;
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
deleted file mode 100644
index 97d26c7c9c1f5..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
+++ /dev/null
@@ -1,88 +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 android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.util.FdEventsReader;
-import android.os.Handler;
-import android.system.Os;
-
-import java.io.FileDescriptor;
-import java.net.Inet4Address;
-import java.net.InetSocketAddress;
-
-/**
- * A {@link FdEventsReader} to receive and parse {@link DhcpPacket}.
- * @hide
- */
-abstract class DhcpPacketListener extends FdEventsReader {
- static final class Payload {
- protected final byte[] mBytes = new byte[DhcpPacket.MAX_LENGTH];
- protected Inet4Address mSrcAddr;
- protected int mSrcPort;
- }
-
- DhcpPacketListener(@NonNull Handler handler) {
- super(handler, new Payload());
- }
-
- @Override
- protected int recvBufSize(@NonNull Payload buffer) {
- return buffer.mBytes.length;
- }
-
- @Override
- protected final void handlePacket(@NonNull Payload recvbuf, int length) {
- if (recvbuf.mSrcAddr == null) {
- return;
- }
-
- try {
- final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.mBytes, length,
- DhcpPacket.ENCAP_BOOTP);
- onReceive(packet, recvbuf.mSrcAddr, recvbuf.mSrcPort);
- } catch (DhcpPacket.ParseException e) {
- logParseError(recvbuf.mBytes, length, e);
- }
- }
-
- @Override
- protected int readPacket(@NonNull FileDescriptor fd, @NonNull Payload packetBuffer)
- throws Exception {
- final InetSocketAddress addr = new InetSocketAddress(0);
- final int read = Os.recvfrom(
- fd, packetBuffer.mBytes, 0, packetBuffer.mBytes.length, 0 /* flags */, addr);
-
- // Buffers with null srcAddr will be dropped in handlePacket()
- packetBuffer.mSrcAddr = inet4AddrOrNull(addr);
- packetBuffer.mSrcPort = addr.getPort();
- return read;
- }
-
- @Nullable
- private static Inet4Address inet4AddrOrNull(@NonNull InetSocketAddress addr) {
- return addr.getAddress() instanceof Inet4Address
- ? (Inet4Address) addr.getAddress()
- : null;
- }
-
- protected abstract void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr,
- int srcPort);
- protected abstract void logParseError(@NonNull byte[] packet, int length,
- @NonNull DhcpPacket.ParseException e);
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpReleasePacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpReleasePacket.java
deleted file mode 100644
index 39583032c20de..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpReleasePacket.java
+++ /dev/null
@@ -1,58 +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 java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * Implements DHCP-RELEASE
- */
-class DhcpReleasePacket extends DhcpPacket {
-
- final Inet4Address mClientAddr;
-
- /**
- * Generates a RELEASE packet with the specified parameters.
- */
- public DhcpReleasePacket(int transId, Inet4Address serverId, Inet4Address clientAddr,
- Inet4Address relayIp, byte[] clientMac) {
- super(transId, (short)0, clientAddr, INADDR_ANY /* yourIp */, INADDR_ANY /* nextIp */,
- relayIp, clientMac, false /* broadcast */);
- mServerIdentifier = serverId;
- mClientAddr = clientAddr;
- }
-
-
- @Override
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- fillInPacket(encap, mServerIdentifier /* destIp */, mClientIp /* srcIp */, destUdp, srcUdp,
- result, DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- @Override
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_RELEASE);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- addCommonClientTlvs(buffer);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpRequestPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpRequestPacket.java
deleted file mode 100644
index 231d04576c28c..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpRequestPacket.java
+++ /dev/null
@@ -1,71 +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 android.net.dhcp;
-
-import android.util.Log;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-REQUEST packet.
- */
-class DhcpRequestPacket extends DhcpPacket {
- /**
- * Generates a REQUEST packet with the specified parameters.
- */
- DhcpRequestPacket(int transId, short secs, Inet4Address clientIp, Inet4Address relayIp,
- byte[] clientMac, boolean broadcast) {
- super(transId, secs, clientIp, INADDR_ANY, INADDR_ANY, relayIp, clientMac, broadcast);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " REQUEST, desired IP " + mRequestedIp + " from host '"
- + mHostName + "', param list length "
- + (mRequestedParams == null ? 0 : mRequestedParams.length);
- }
-
- /**
- * Fills in a packet with the requested REQUEST attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, INADDR_BROADCAST, INADDR_ANY, destUdp, srcUdp,
- result, DHCP_BOOTREQUEST, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated REQUEST packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_REQUEST);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- if (!INADDR_ANY.equals(mRequestedIp)) {
- addTlv(buffer, DHCP_REQUESTED_IP, mRequestedIp);
- }
- if (!INADDR_ANY.equals(mServerIdentifier)) {
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- }
- addCommonClientTlvs(buffer);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
deleted file mode 100644
index b8ab94ce38308..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
+++ /dev/null
@@ -1,655 +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.dhcp.DhcpPacket.DHCP_CLIENT;
-import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
-import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
-import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
-import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
-import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-import static android.net.shared.Inet4AddressUtils.getBroadcastAddress;
-import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_NONBLOCK;
-import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BROADCAST;
-import static android.system.OsConstants.SO_REUSEADDR;
-
-import static com.android.internal.util.TrafficStatsConstants.TAG_SYSTEM_DHCP_SERVER;
-import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL;
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
-import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
-
-import static java.lang.Integer.toUnsignedLong;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.INetworkStackStatusCallback;
-import android.net.MacAddress;
-import android.net.TrafficStats;
-import android.net.util.NetworkStackUtils;
-import android.net.util.SharedLog;
-import android.net.util.SocketUtils;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.HexDump;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-
-/**
- * A DHCPv4 server.
- *
- *
This server listens for and responds to packets on a single interface. It considers itself
- * authoritative for all leases on the subnet, which means that DHCP requests for unknown leases of
- * unknown hosts receive a reply instead of being ignored.
- *
- *
The server is single-threaded (including send/receive operations): all internal operations are
- * done on the provided {@link Looper}. Public methods are thread-safe and will schedule operations
- * on the looper asynchronously.
- * @hide
- */
-public class DhcpServer extends IDhcpServer.Stub {
- private static final String REPO_TAG = "Repository";
-
- // Lease time to transmit to client instead of a negative time in case a lease expired before
- // the server could send it (if the server process is suspended for example).
- private static final int EXPIRED_FALLBACK_LEASE_TIME_SECS = 120;
-
- private static final int CMD_START_DHCP_SERVER = 1;
- private static final int CMD_STOP_DHCP_SERVER = 2;
- private static final int CMD_UPDATE_PARAMS = 3;
-
- @NonNull
- private final HandlerThread mHandlerThread;
- @NonNull
- private final String mIfName;
- @NonNull
- private final DhcpLeaseRepository mLeaseRepo;
- @NonNull
- private final SharedLog mLog;
- @NonNull
- private final Dependencies mDeps;
- @NonNull
- private final Clock mClock;
-
- @Nullable
- private volatile ServerHandler mHandler;
-
- // Accessed only on the handler thread
- @Nullable
- private DhcpPacketListener mPacketListener;
- @Nullable
- private FileDescriptor mSocket;
- @NonNull
- private DhcpServingParams mServingParams;
-
- /**
- * Clock to be used by DhcpServer to track time for lease expiration.
- *
- *
The clock should track time as may be measured by clients obtaining a lease. It does not
- * need to be monotonous across restarts of the server as long as leases are cleared when the
- * server is stopped.
- */
- public static class Clock {
- /**
- * @see SystemClock#elapsedRealtime()
- */
- public long elapsedRealtime() {
- return SystemClock.elapsedRealtime();
- }
- }
-
- /**
- * Dependencies for the DhcpServer. Useful to be mocked in tests.
- */
- public interface Dependencies {
- /**
- * Send a packet to the specified datagram socket.
- *
- * @param fd File descriptor of the socket.
- * @param buffer Data to be sent.
- * @param dst Destination address of the packet.
- */
- void sendPacket(@NonNull FileDescriptor fd, @NonNull ByteBuffer buffer,
- @NonNull InetAddress dst) throws ErrnoException, IOException;
-
- /**
- * Create a DhcpLeaseRepository for the server.
- * @param servingParams Parameters used to serve DHCP requests.
- * @param log Log to be used by the repository.
- * @param clock Clock that the repository must use to track time.
- */
- DhcpLeaseRepository makeLeaseRepository(@NonNull DhcpServingParams servingParams,
- @NonNull SharedLog log, @NonNull Clock clock);
-
- /**
- * Create a packet listener that will send packets to be processed.
- */
- DhcpPacketListener makePacketListener();
-
- /**
- * Create a clock that the server will use to track time.
- */
- Clock makeClock();
-
- /**
- * Add an entry to the ARP cache table.
- * @param fd Datagram socket file descriptor that must use the new entry.
- */
- void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
- @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException;
-
- /**
- * Verify that the caller is allowed to call public methods on DhcpServer.
- * @throws SecurityException The caller is not allowed to call public methods on DhcpServer.
- */
- void checkCaller() throws SecurityException;
- }
-
- private class DependenciesImpl implements Dependencies {
- @Override
- public void sendPacket(@NonNull FileDescriptor fd, @NonNull ByteBuffer buffer,
- @NonNull InetAddress dst) throws ErrnoException, IOException {
- Os.sendto(fd, buffer, 0, dst, DhcpPacket.DHCP_CLIENT);
- }
-
- @Override
- public DhcpLeaseRepository makeLeaseRepository(@NonNull DhcpServingParams servingParams,
- @NonNull SharedLog log, @NonNull Clock clock) {
- return new DhcpLeaseRepository(
- DhcpServingParams.makeIpPrefix(servingParams.serverAddr),
- servingParams.excludedAddrs,
- servingParams.dhcpLeaseTimeSecs * 1000, log.forSubComponent(REPO_TAG), clock);
- }
-
- @Override
- public DhcpPacketListener makePacketListener() {
- return new PacketListener();
- }
-
- @Override
- public Clock makeClock() {
- return new Clock();
- }
-
- @Override
- public void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
- @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException {
- NetworkStackUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd);
- }
-
- @Override
- public void checkCaller() {
- checkNetworkStackCallingPermission();
- }
- }
-
- private static class MalformedPacketException extends Exception {
- MalformedPacketException(String message, Throwable t) {
- super(message, t);
- }
- }
-
- public DhcpServer(@NonNull String ifName,
- @NonNull DhcpServingParams params, @NonNull SharedLog log) {
- this(new HandlerThread(DhcpServer.class.getSimpleName() + "." + ifName),
- ifName, params, log, null);
- }
-
- @VisibleForTesting
- DhcpServer(@NonNull HandlerThread handlerThread, @NonNull String ifName,
- @NonNull DhcpServingParams params, @NonNull SharedLog log,
- @Nullable Dependencies deps) {
- if (deps == null) {
- deps = new DependenciesImpl();
- }
- mHandlerThread = handlerThread;
- mIfName = ifName;
- mServingParams = params;
- mLog = log;
- mDeps = deps;
- mClock = deps.makeClock();
- mLeaseRepo = deps.makeLeaseRepository(mServingParams, mLog, mClock);
- }
-
- /**
- * Start listening for and responding to packets.
- *
- *
It is not legal to call this method more than once; in particular the server cannot be
- * restarted after being stopped.
- */
- @Override
- public void start(@Nullable INetworkStackStatusCallback cb) {
- mDeps.checkCaller();
- mHandlerThread.start();
- mHandler = new ServerHandler(mHandlerThread.getLooper());
- sendMessage(CMD_START_DHCP_SERVER, cb);
- }
-
- /**
- * Update serving parameters. All subsequently received requests will be handled with the new
- * parameters, and current leases that are incompatible with the new parameters are dropped.
- */
- @Override
- public void updateParams(@Nullable DhcpServingParamsParcel params,
- @Nullable INetworkStackStatusCallback cb) throws RemoteException {
- mDeps.checkCaller();
- final DhcpServingParams parsedParams;
- try {
- // throws InvalidParameterException with null params
- parsedParams = DhcpServingParams.fromParcelableObject(params);
- } catch (DhcpServingParams.InvalidParameterException e) {
- mLog.e("Invalid parameters sent to DhcpServer", e);
- if (cb != null) {
- cb.onStatusAvailable(STATUS_INVALID_ARGUMENT);
- }
- return;
- }
- sendMessage(CMD_UPDATE_PARAMS, new Pair<>(parsedParams, cb));
- }
-
- /**
- * Stop listening for packets.
- *
- *
As the server is stopped asynchronously, some packets may still be processed shortly after
- * calling this method.
- */
- @Override
- public void stop(@Nullable INetworkStackStatusCallback cb) {
- mDeps.checkCaller();
- sendMessage(CMD_STOP_DHCP_SERVER, cb);
- }
-
- private void sendMessage(int what, @Nullable Object obj) {
- if (mHandler == null) {
- mLog.e("Attempting to send a command to stopped DhcpServer: " + what);
- return;
- }
- mHandler.sendMessage(mHandler.obtainMessage(what, obj));
- }
-
- private class ServerHandler extends Handler {
- ServerHandler(@NonNull Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(@NonNull Message msg) {
- final INetworkStackStatusCallback cb;
- switch (msg.what) {
- case CMD_UPDATE_PARAMS:
- final Pair pair =
- (Pair) msg.obj;
- final DhcpServingParams params = pair.first;
- mServingParams = params;
- mLeaseRepo.updateParams(
- DhcpServingParams.makeIpPrefix(mServingParams.serverAddr),
- params.excludedAddrs,
- params.dhcpLeaseTimeSecs);
-
- cb = pair.second;
- break;
- case CMD_START_DHCP_SERVER:
- mPacketListener = mDeps.makePacketListener();
- mPacketListener.start();
- cb = (INetworkStackStatusCallback) msg.obj;
- break;
- case CMD_STOP_DHCP_SERVER:
- if (mPacketListener != null) {
- mPacketListener.stop();
- mPacketListener = null;
- }
- mHandlerThread.quitSafely();
- cb = (INetworkStackStatusCallback) msg.obj;
- break;
- default:
- return;
- }
- if (cb != null) {
- try {
- cb.onStatusAvailable(STATUS_SUCCESS);
- } catch (RemoteException e) {
- mLog.e("Could not send status back to caller", e);
- }
- }
- }
- }
-
- @VisibleForTesting
- void processPacket(@NonNull DhcpPacket packet, int srcPort) {
- final String packetType = packet.getClass().getSimpleName();
- if (srcPort != DHCP_CLIENT) {
- mLog.logf("Ignored packet of type %s sent from client port %d", packetType, srcPort);
- return;
- }
-
- mLog.log("Received packet of type " + packetType);
- final Inet4Address sid = packet.mServerIdentifier;
- if (sid != null && !sid.equals(mServingParams.serverAddr.getAddress())) {
- mLog.log("Packet ignored due to wrong server identifier: " + sid);
- return;
- }
-
- try {
- if (packet instanceof DhcpDiscoverPacket) {
- processDiscover((DhcpDiscoverPacket) packet);
- } else if (packet instanceof DhcpRequestPacket) {
- processRequest((DhcpRequestPacket) packet);
- } else if (packet instanceof DhcpReleasePacket) {
- processRelease((DhcpReleasePacket) packet);
- } else {
- mLog.e("Unknown packet type: " + packet.getClass().getSimpleName());
- }
- } catch (MalformedPacketException e) {
- // Not an internal error: only logging exception message, not stacktrace
- mLog.e("Ignored malformed packet: " + e.getMessage());
- }
- }
-
- private void logIgnoredPacketInvalidSubnet(DhcpLeaseRepository.InvalidSubnetException e) {
- // Not an internal error: only logging exception message, not stacktrace
- mLog.e("Ignored packet from invalid subnet: " + e.getMessage());
- }
-
- private void processDiscover(@NonNull DhcpDiscoverPacket packet)
- throws MalformedPacketException {
- final DhcpLease lease;
- final MacAddress clientMac = getMacAddr(packet);
- try {
- lease = mLeaseRepo.getOffer(packet.getExplicitClientIdOrNull(), clientMac,
- packet.mRelayIp, packet.mRequestedIp, packet.mHostName);
- } catch (DhcpLeaseRepository.OutOfAddressesException e) {
- transmitNak(packet, "Out of addresses to offer");
- return;
- } catch (DhcpLeaseRepository.InvalidSubnetException e) {
- logIgnoredPacketInvalidSubnet(e);
- return;
- }
-
- transmitOffer(packet, lease, clientMac);
- }
-
- private void processRequest(@NonNull DhcpRequestPacket packet) throws MalformedPacketException {
- // If set, packet SID matches with this server's ID as checked in processPacket().
- final boolean sidSet = packet.mServerIdentifier != null;
- final DhcpLease lease;
- final MacAddress clientMac = getMacAddr(packet);
- try {
- lease = mLeaseRepo.requestLease(packet.getExplicitClientIdOrNull(), clientMac,
- packet.mClientIp, packet.mRelayIp, packet.mRequestedIp, sidSet,
- packet.mHostName);
- } catch (DhcpLeaseRepository.InvalidAddressException e) {
- transmitNak(packet, "Invalid requested address");
- return;
- } catch (DhcpLeaseRepository.InvalidSubnetException e) {
- logIgnoredPacketInvalidSubnet(e);
- return;
- }
-
- transmitAck(packet, lease, clientMac);
- }
-
- private void processRelease(@NonNull DhcpReleasePacket packet)
- throws MalformedPacketException {
- final byte[] clientId = packet.getExplicitClientIdOrNull();
- final MacAddress macAddr = getMacAddr(packet);
- // Don't care about success (there is no ACK/NAK); logging is already done in the repository
- mLeaseRepo.releaseLease(clientId, macAddr, packet.mClientIp);
- }
-
- private Inet4Address getAckOrOfferDst(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
- boolean broadcastFlag) {
- // Unless relayed or broadcast, send to client IP if already configured on the client, or to
- // the lease address if the client has no configured address
- if (!isEmpty(request.mRelayIp)) {
- return request.mRelayIp;
- } else if (broadcastFlag) {
- return IPV4_ADDR_ALL;
- } else if (!isEmpty(request.mClientIp)) {
- return request.mClientIp;
- } else {
- return lease.getNetAddr();
- }
- }
-
- /**
- * Determine whether the broadcast flag should be set in the BOOTP packet flags. This does not
- * apply to NAK responses, which should always have it set.
- */
- private static boolean getBroadcastFlag(@NonNull DhcpPacket request, @NonNull DhcpLease lease) {
- // No broadcast flag if the client already has a configured IP to unicast to. RFC2131 #4.1
- // has some contradictions regarding broadcast behavior if a client already has an IP
- // configured and sends a request with both ciaddr (renew/rebind) and the broadcast flag
- // set. Sending a unicast response to ciaddr matches previous behavior and is more
- // efficient.
- // If the client has no configured IP, broadcast if requested by the client or if the lease
- // address cannot be used to send a unicast reply either.
- return isEmpty(request.mClientIp) && (request.mBroadcast || isEmpty(lease.getNetAddr()));
- }
-
- /**
- * Get the hostname from a lease if non-empty and requested in the incoming request.
- * @param request The incoming request.
- * @return The hostname, or null if not requested or empty.
- */
- @Nullable
- private static String getHostnameIfRequested(@NonNull DhcpPacket request,
- @NonNull DhcpLease lease) {
- return request.hasRequestedParam(DHCP_HOST_NAME) && !TextUtils.isEmpty(lease.getHostname())
- ? lease.getHostname()
- : null;
- }
-
- private boolean transmitOffer(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
- @NonNull MacAddress clientMac) {
- final boolean broadcastFlag = getBroadcastFlag(request, lease);
- final int timeout = getLeaseTimeout(lease);
- final Inet4Address prefixMask =
- getPrefixMaskAsInet4Address(mServingParams.serverAddr.getPrefixLength());
- final Inet4Address broadcastAddr = getBroadcastAddress(
- mServingParams.getServerInet4Addr(), mServingParams.serverAddr.getPrefixLength());
- final String hostname = getHostnameIfRequested(request, lease);
- final ByteBuffer offerPacket = DhcpPacket.buildOfferPacket(
- ENCAP_BOOTP, request.mTransId, broadcastFlag, mServingParams.getServerInet4Addr(),
- request.mRelayIp, lease.getNetAddr(), request.mClientMac, timeout, prefixMask,
- broadcastAddr, new ArrayList<>(mServingParams.defaultRouters),
- new ArrayList<>(mServingParams.dnsServers),
- mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
- mServingParams.metered, (short) mServingParams.linkMtu);
-
- return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag);
- }
-
- private boolean transmitAck(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
- @NonNull MacAddress clientMac) {
- // TODO: replace DhcpPacket's build methods with real builders and use common code with
- // transmitOffer above
- final boolean broadcastFlag = getBroadcastFlag(request, lease);
- final int timeout = getLeaseTimeout(lease);
- final String hostname = getHostnameIfRequested(request, lease);
- final ByteBuffer ackPacket = DhcpPacket.buildAckPacket(ENCAP_BOOTP, request.mTransId,
- broadcastFlag, mServingParams.getServerInet4Addr(), request.mRelayIp,
- lease.getNetAddr(), request.mClientIp, request.mClientMac, timeout,
- mServingParams.getPrefixMaskAsAddress(), mServingParams.getBroadcastAddress(),
- new ArrayList<>(mServingParams.defaultRouters),
- new ArrayList<>(mServingParams.dnsServers),
- mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
- mServingParams.metered, (short) mServingParams.linkMtu);
-
- return transmitOfferOrAckPacket(ackPacket, request, lease, clientMac, broadcastFlag);
- }
-
- private boolean transmitNak(DhcpPacket request, String message) {
- mLog.w("Transmitting NAK: " + message);
- // Always set broadcast flag for NAK: client may not have a correct IP
- final ByteBuffer nakPacket = DhcpPacket.buildNakPacket(
- ENCAP_BOOTP, request.mTransId, mServingParams.getServerInet4Addr(),
- request.mRelayIp, request.mClientMac, true /* broadcast */, message);
-
- final Inet4Address dst = isEmpty(request.mRelayIp)
- ? IPV4_ADDR_ALL
- : request.mRelayIp;
- return transmitPacket(nakPacket, DhcpNakPacket.class.getSimpleName(), dst);
- }
-
- private boolean transmitOfferOrAckPacket(@NonNull ByteBuffer buf, @NonNull DhcpPacket request,
- @NonNull DhcpLease lease, @NonNull MacAddress clientMac, boolean broadcastFlag) {
- mLog.logf("Transmitting %s with lease %s", request.getClass().getSimpleName(), lease);
- // Client may not yet respond to ARP for the lease address, which may be the destination
- // address. Add an entry to the ARP cache to save future ARP probes and make sure the
- // packet reaches its destination.
- if (!addArpEntry(clientMac, lease.getNetAddr())) {
- // Logging for error already done
- return false;
- }
- final Inet4Address dst = getAckOrOfferDst(request, lease, broadcastFlag);
- return transmitPacket(buf, request.getClass().getSimpleName(), dst);
- }
-
- private boolean transmitPacket(@NonNull ByteBuffer buf, @NonNull String packetTypeTag,
- @NonNull Inet4Address dst) {
- try {
- mDeps.sendPacket(mSocket, buf, dst);
- } catch (ErrnoException | IOException e) {
- mLog.e("Can't send packet " + packetTypeTag, e);
- return false;
- }
- return true;
- }
-
- private boolean addArpEntry(@NonNull MacAddress macAddr, @NonNull Inet4Address inetAddr) {
- try {
- mDeps.addArpEntry(inetAddr, macAddr, mIfName, mSocket);
- return true;
- } catch (IOException e) {
- mLog.e("Error adding client to ARP table", e);
- return false;
- }
- }
-
- /**
- * Get the remaining lease time in seconds, starting from {@link Clock#elapsedRealtime()}.
- *
- *
This is an unsigned 32-bit integer, so it cannot be read as a standard (signed) Java int.
- * The return value is only intended to be used to populate the lease time field in a DHCP
- * response, considering that lease time is an unsigned 32-bit integer field in DHCP packets.
- *
- *
Lease expiration times are tracked internally with millisecond precision: this method
- * returns a rounded down value.
- */
- private int getLeaseTimeout(@NonNull DhcpLease lease) {
- final long remainingTimeSecs = (lease.getExpTime() - mClock.elapsedRealtime()) / 1000;
- if (remainingTimeSecs < 0) {
- mLog.e("Processing expired lease " + lease);
- return EXPIRED_FALLBACK_LEASE_TIME_SECS;
- }
-
- if (remainingTimeSecs >= toUnsignedLong(INFINITE_LEASE)) {
- return INFINITE_LEASE;
- }
-
- return (int) remainingTimeSecs;
- }
-
- /**
- * Get the client MAC address from a packet.
- *
- * @throws MalformedPacketException The address in the packet uses an unsupported format.
- */
- @NonNull
- private MacAddress getMacAddr(@NonNull DhcpPacket packet) throws MalformedPacketException {
- try {
- return MacAddress.fromBytes(packet.getClientMac());
- } catch (IllegalArgumentException e) {
- final String message = "Invalid MAC address in packet: "
- + HexDump.dumpHexString(packet.getClientMac());
- throw new MalformedPacketException(message, e);
- }
- }
-
- private static boolean isEmpty(@Nullable Inet4Address address) {
- return address == null || IPV4_ADDR_ANY.equals(address);
- }
-
- private class PacketListener extends DhcpPacketListener {
- PacketListener() {
- super(mHandler);
- }
-
- @Override
- protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr,
- int srcPort) {
- processPacket(packet, srcPort);
- }
-
- @Override
- protected void logError(@NonNull String msg, Exception e) {
- mLog.e("Error receiving packet: " + msg, e);
- }
-
- @Override
- protected void logParseError(@NonNull byte[] packet, int length,
- @NonNull DhcpPacket.ParseException e) {
- mLog.e("Error parsing packet", e);
- }
-
- @Override
- protected FileDescriptor createFd() {
- // TODO: have and use an API to set a socket tag without going through the thread tag
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(TAG_SYSTEM_DHCP_SERVER);
- try {
- mSocket = Os.socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
- SocketUtils.bindSocketToInterface(mSocket, mIfName);
- Os.setsockoptInt(mSocket, SOL_SOCKET, SO_REUSEADDR, 1);
- Os.setsockoptInt(mSocket, SOL_SOCKET, SO_BROADCAST, 1);
- Os.bind(mSocket, IPV4_ADDR_ANY, DHCP_SERVER);
-
- return mSocket;
- } catch (IOException | ErrnoException e) {
- mLog.e("Error creating UDP socket", e);
- DhcpServer.this.stop(null);
- return null;
- } finally {
- TrafficStats.setThreadStatsTag(oldTag);
- }
- }
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
deleted file mode 100644
index 230b693a809a4..0000000000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
+++ /dev/null
@@ -1,377 +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.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address;
-import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
-
-import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
-import static com.android.server.util.NetworkStackConstants.IPV4_MAX_MTU;
-import static com.android.server.util.NetworkStackConstants.IPV4_MIN_MTU;
-
-import static java.lang.Integer.toUnsignedLong;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.shared.Inet4AddressUtils;
-import android.util.ArraySet;
-
-import java.net.Inet4Address;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Parameters used by the DhcpServer to serve requests.
- *
- *
Instances are immutable. Use {@link DhcpServingParams.Builder} to instantiate.
- * @hide
- */
-public class DhcpServingParams {
- public static final int MTU_UNSET = 0;
- public static final int MIN_PREFIX_LENGTH = 16;
- public static final int MAX_PREFIX_LENGTH = 30;
-
- /** Server inet address and prefix to serve */
- @NonNull
- public final LinkAddress serverAddr;
-
- /**
- * Default routers to be advertised to DHCP clients. May be empty.
- * This set is provided by {@link DhcpServingParams.Builder} and is immutable.
- */
- @NonNull
- public final Set defaultRouters;
-
- /**
- * DNS servers to be advertised to DHCP clients. May be empty.
- * This set is provided by {@link DhcpServingParams.Builder} and is immutable.
- */
- @NonNull
- public final Set dnsServers;
-
- /**
- * Excluded addresses that the DHCP server is not allowed to assign to clients.
- * This set is provided by {@link DhcpServingParams.Builder} and is immutable.
- */
- @NonNull
- public final Set excludedAddrs;
-
- // DHCP uses uint32. Use long for clearer code, and check range when building.
- public final long dhcpLeaseTimeSecs;
- public final int linkMtu;
-
- /**
- * Indicates whether the DHCP server should send the ANDROID_METERED vendor-specific option.
- */
- public final boolean metered;
-
- /**
- * Checked exception thrown when some parameters used to build {@link DhcpServingParams} are
- * missing or invalid.
- */
- public static class InvalidParameterException extends Exception {
- public InvalidParameterException(String message) {
- super(message);
- }
- }
-
- private DhcpServingParams(@NonNull LinkAddress serverAddr,
- @NonNull Set defaultRouters,
- @NonNull Set dnsServers, @NonNull Set excludedAddrs,
- long dhcpLeaseTimeSecs, int linkMtu, boolean metered) {
- this.serverAddr = serverAddr;
- this.defaultRouters = defaultRouters;
- this.dnsServers = dnsServers;
- this.excludedAddrs = excludedAddrs;
- this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
- this.linkMtu = linkMtu;
- this.metered = metered;
- }
-
- /**
- * Create parameters from a stable AIDL-compatible parcel.
- * @throws InvalidParameterException The parameters parcelable is null or invalid.
- */
- public static DhcpServingParams fromParcelableObject(@Nullable DhcpServingParamsParcel parcel)
- throws InvalidParameterException {
- if (parcel == null) {
- throw new InvalidParameterException("Null serving parameters");
- }
- final LinkAddress serverAddr = new LinkAddress(
- intToInet4AddressHTH(parcel.serverAddr),
- parcel.serverAddrPrefixLength);
- return new Builder()
- .setServerAddr(serverAddr)
- .setDefaultRouters(toInet4AddressSet(parcel.defaultRouters))
- .setDnsServers(toInet4AddressSet(parcel.dnsServers))
- .setExcludedAddrs(toInet4AddressSet(parcel.excludedAddrs))
- .setDhcpLeaseTimeSecs(parcel.dhcpLeaseTimeSecs)
- .setLinkMtu(parcel.linkMtu)
- .setMetered(parcel.metered)
- .build();
- }
-
- private static Set toInet4AddressSet(@Nullable int[] addrs) {
- if (addrs == null) {
- return new HashSet<>(0);
- }
-
- final HashSet res = new HashSet<>();
- for (int addr : addrs) {
- res.add(intToInet4AddressHTH(addr));
- }
- return res;
- }
-
- @NonNull
- public Inet4Address getServerInet4Addr() {
- return (Inet4Address) serverAddr.getAddress();
- }
-
- /**
- * Get the served prefix mask as an IPv4 address.
- *
- *
For example, if the served prefix is 192.168.42.0/24, this will return 255.255.255.0.
- */
- @NonNull
- public Inet4Address getPrefixMaskAsAddress() {
- return getPrefixMaskAsInet4Address(serverAddr.getPrefixLength());
- }
-
- /**
- * Get the server broadcast address.
- *
- *
For example, if the server {@link LinkAddress} is 192.168.42.1/24, this will return
- * 192.168.42.255.
- */
- @NonNull
- public Inet4Address getBroadcastAddress() {
- return Inet4AddressUtils.getBroadcastAddress(
- getServerInet4Addr(), serverAddr.getPrefixLength());
- }
-
- /**
- * Utility class to create new instances of {@link DhcpServingParams} while checking validity
- * of the parameters.
- */
- public static class Builder {
- private LinkAddress mServerAddr;
- private Set mDefaultRouters;
- private Set mDnsServers;
- private Set mExcludedAddrs;
- private long mDhcpLeaseTimeSecs;
- private int mLinkMtu = MTU_UNSET;
- private boolean mMetered;
-
- /**
- * Set the server address and served prefix for the DHCP server.
- *
- *
This parameter is required.
- */
- public Builder setServerAddr(@NonNull LinkAddress serverAddr) {
- this.mServerAddr = serverAddr;
- 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 before building the {@link DhcpServingParams}.
- */
- public Builder setDefaultRouters(@NonNull Set defaultRouters) {
- this.mDefaultRouters = 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 before building the {@link DhcpServingParams}.
- */
- public Builder setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
- return setDefaultRouters(makeArraySet(defaultRouters));
- }
-
- /**
- * Convenience method to build the parameters with no default router.
- *
- *
Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address.
- */
- public Builder withNoDefaultRouter() {
- 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 before building the
- * {@link DhcpServingParams}.
- */
- public Builder setDnsServers(@NonNull Set dnsServers) {
- this.mDnsServers = 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 before
- * building the {@link DhcpServingParams}.
- */
- public Builder setDnsServers(@NonNull Inet4Address... dnsServers) {
- return setDnsServers(makeArraySet(dnsServers));
- }
-
- /**
- * Convenience method to build the parameters with no DNS server.
- *
- *
Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address.
- */
- public Builder withNoDnsServer() {
- 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 Builder setExcludedAddrs(@NonNull Set excludedAddrs) {
- this.mExcludedAddrs = 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 Builder setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
- return setExcludedAddrs(makeArraySet(excludedAddrs));
- }
-
- /**
- * Set the lease time for leases assigned by the DHCP server.
- *
- *
This parameter is required.
- */
- public Builder setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
- this.mDhcpLeaseTimeSecs = 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 Builder setLinkMtu(int linkMtu) {
- this.mLinkMtu = 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 Builder setMetered(boolean metered) {
- this.mMetered = metered;
- return this;
- }
-
- /**
- * Create a new {@link DhcpServingParams} instance based on parameters set in the builder.
- *
- *
This method has no side-effects. If it does not throw, a valid
- * {@link DhcpServingParams} is returned.
- * @return The constructed parameters.
- * @throws InvalidParameterException At least one parameter is missing or invalid.
- */
- @NonNull
- public DhcpServingParams build() throws InvalidParameterException {
- if (mServerAddr == null) {
- throw new InvalidParameterException("Missing serverAddr");
- }
- if (mDefaultRouters == null) {
- throw new InvalidParameterException("Missing defaultRouters");
- }
- if (mDnsServers == null) {
- // Empty set is OK, but enforce explicitly setting it
- throw new InvalidParameterException("Missing dnsServers");
- }
- if (mDhcpLeaseTimeSecs <= 0 || mDhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) {
- throw new InvalidParameterException("Invalid lease time: " + mDhcpLeaseTimeSecs);
- }
- if (mLinkMtu != MTU_UNSET && (mLinkMtu < IPV4_MIN_MTU || mLinkMtu > IPV4_MAX_MTU)) {
- throw new InvalidParameterException("Invalid link MTU: " + mLinkMtu);
- }
- if (!mServerAddr.isIpv4()) {
- throw new InvalidParameterException("serverAddr must be IPv4");
- }
- if (mServerAddr.getPrefixLength() < MIN_PREFIX_LENGTH
- || mServerAddr.getPrefixLength() > MAX_PREFIX_LENGTH) {
- throw new InvalidParameterException("Prefix length is not in supported range");
- }
-
- final IpPrefix prefix = makeIpPrefix(mServerAddr);
- for (Inet4Address addr : mDefaultRouters) {
- if (!prefix.contains(addr)) {
- throw new InvalidParameterException(String.format(
- "Default router %s is not in server prefix %s", addr, mServerAddr));
- }
- }
-
- final Set excl = new HashSet<>();
- if (mExcludedAddrs != null) {
- excl.addAll(mExcludedAddrs);
- }
- excl.add((Inet4Address) mServerAddr.getAddress());
- excl.addAll(mDefaultRouters);
- excl.addAll(mDnsServers);
-
- return new DhcpServingParams(mServerAddr,
- Collections.unmodifiableSet(new HashSet<>(mDefaultRouters)),
- Collections.unmodifiableSet(new HashSet<>(mDnsServers)),
- Collections.unmodifiableSet(excl),
- mDhcpLeaseTimeSecs, mLinkMtu, mMetered);
- }
- }
-
- /**
- * Utility method to create an IpPrefix with the address and prefix length of a LinkAddress.
- */
- @NonNull
- static IpPrefix makeIpPrefix(@NonNull LinkAddress addr) {
- return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
- }
-
- private static ArraySet makeArraySet(T[] elements) {
- final ArraySet set = new ArraySet<>(elements.length);
- set.addAll(Arrays.asList(elements));
- return set;
- }
-}
diff --git a/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java b/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java
deleted file mode 100644
index eb49218ebbae8..0000000000000
--- a/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java
+++ /dev/null
@@ -1,153 +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.SocketUtils.makePacketSocketAddress;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ARPHRD_ETHER;
-import static android.system.OsConstants.ETH_P_ALL;
-import static android.system.OsConstants.SOCK_NONBLOCK;
-import static android.system.OsConstants.SOCK_RAW;
-
-import android.net.util.ConnectivityPacketSummary;
-import android.net.util.InterfaceParams;
-import android.net.util.NetworkStackUtils;
-import android.net.util.PacketReader;
-import android.os.Handler;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.Log;
-
-import com.android.internal.util.HexDump;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-
-/**
- * Critical connectivity packet tracking daemon.
- *
- * Tracks ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets.
- *
- * This class's constructor, start() and stop() methods must only be called
- * from the same thread on which the passed in |log| is accessed.
- *
- * Log lines include a hexdump of the packet, which can be decoded via:
- *
- * echo -n H3XSTR1NG | sed -e 's/\([0-9A-F][0-9A-F]\)/\1 /g' -e 's/^/000000 /'
- * | text2pcap - -
- * | tcpdump -n -vv -e -r -
- *
- * @hide
- */
-public class ConnectivityPacketTracker {
- private static final String TAG = ConnectivityPacketTracker.class.getSimpleName();
- private static final boolean DBG = false;
- private static final String MARK_START = "--- START ---";
- private static final String MARK_STOP = "--- STOP ---";
- private static final String MARK_NAMED_START = "--- START (%s) ---";
- private static final String MARK_NAMED_STOP = "--- STOP (%s) ---";
-
- private final String mTag;
- private final LocalLog mLog;
- private final PacketReader mPacketListener;
- private boolean mRunning;
- private String mDisplayName;
-
- public ConnectivityPacketTracker(Handler h, InterfaceParams ifParams, LocalLog log) {
- if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
-
- mTag = TAG + "." + ifParams.name;
- mLog = log;
- mPacketListener = new PacketListener(h, ifParams);
- }
-
- public void start(String displayName) {
- mRunning = true;
- mDisplayName = displayName;
- mPacketListener.start();
- }
-
- public void stop() {
- mPacketListener.stop();
- mRunning = false;
- mDisplayName = null;
- }
-
- private final class PacketListener extends PacketReader {
- private final InterfaceParams mInterface;
-
- PacketListener(Handler h, InterfaceParams ifParams) {
- super(h, ifParams.defaultMtu);
- mInterface = ifParams;
- }
-
- @Override
- protected FileDescriptor createFd() {
- FileDescriptor s = null;
- try {
- s = Os.socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, 0);
- NetworkStackUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
- Os.bind(s, makePacketSocketAddress((short) ETH_P_ALL, mInterface.index));
- } catch (ErrnoException | IOException e) {
- logError("Failed to create packet tracking socket: ", e);
- closeFd(s);
- return null;
- }
- return s;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- final String summary = ConnectivityPacketSummary.summarize(
- mInterface.macAddr, recvbuf, length);
- if (summary == null) return;
-
- if (DBG) Log.d(mTag, summary);
- addLogEntry(summary + "\n[" + HexDump.toHexString(recvbuf, 0, length) + "]");
- }
-
- @Override
- protected void onStart() {
- final String msg = TextUtils.isEmpty(mDisplayName)
- ? MARK_START
- : String.format(MARK_NAMED_START, mDisplayName);
- mLog.log(msg);
- }
-
- @Override
- protected void onStop() {
- String msg = TextUtils.isEmpty(mDisplayName)
- ? MARK_STOP
- : String.format(MARK_NAMED_STOP, mDisplayName);
- if (!mRunning) msg += " (packet listener stopped unexpectedly)";
- mLog.log(msg);
- }
-
- @Override
- protected void logError(String msg, Exception e) {
- Log.e(mTag, msg, e);
- addLogEntry(msg + e);
- }
-
- private void addLogEntry(String entry) {
- mLog.log(entry);
- }
- }
-}
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
deleted file mode 100644
index 266b1b047a90b..0000000000000
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ /dev/null
@@ -1,1784 +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.ip;
-
-import static android.net.RouteInfo.RTN_UNICAST;
-import static android.net.shared.IpConfigurationParcelableUtil.toStableParcelable;
-
-import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.DhcpResults;
-import android.net.INetd;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NattKeepalivePacketDataParcelable;
-import android.net.NetworkStackIpMemoryStore;
-import android.net.ProvisioningConfigurationParcelable;
-import android.net.ProxyInfo;
-import android.net.RouteInfo;
-import android.net.TcpKeepalivePacketDataParcelable;
-import android.net.apf.ApfCapabilities;
-import android.net.apf.ApfFilter;
-import android.net.dhcp.DhcpClient;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.IpManagerEvent;
-import android.net.shared.InitialConfiguration;
-import android.net.shared.ProvisioningConfiguration;
-import android.net.util.InterfaceParams;
-import android.net.util.SharedLog;
-import android.os.ConditionVariable;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IState;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.WakeupMessage;
-import com.android.server.NetworkObserverRegistry;
-import com.android.server.NetworkStackService.NetworkStackServiceManager;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.net.InetAddress;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-
-/**
- * IpClient
- *
- * This class provides the interface to IP-layer provisioning and maintenance
- * functionality that can be used by transport layers like Wi-Fi, Ethernet,
- * et cetera.
- *
- * [ Lifetime ]
- * IpClient is designed to be instantiated as soon as the interface name is
- * known and can be as long-lived as the class containing it (i.e. declaring
- * it "private final" is okay).
- *
- * @hide
- */
-public class IpClient extends StateMachine {
- private static final boolean DBG = false;
-
- // For message logging.
- private static final Class[] sMessageClasses = { IpClient.class, DhcpClient.class };
- private static final SparseArray sWhatToString =
- MessageUtils.findMessageNames(sMessageClasses);
- // Two static concurrent hashmaps of interface name to logging classes.
- // One holds StateMachine logs and the other connectivity packet logs.
- private static final ConcurrentHashMap sSmLogs = new ConcurrentHashMap<>();
- private static final ConcurrentHashMap sPktLogs = new ConcurrentHashMap<>();
- private final NetworkStackIpMemoryStore mIpMemoryStore;
-
- /**
- * Dump all state machine and connectivity packet logs to the specified writer.
- * @param skippedIfaces Interfaces for which logs should not be dumped.
- */
- public static void dumpAllLogs(PrintWriter writer, Set skippedIfaces) {
- for (String ifname : sSmLogs.keySet()) {
- if (skippedIfaces.contains(ifname)) continue;
-
- writer.println(String.format("--- BEGIN %s ---", ifname));
-
- final SharedLog smLog = sSmLogs.get(ifname);
- if (smLog != null) {
- writer.println("State machine log:");
- smLog.dump(null, writer, null);
- }
-
- writer.println("");
-
- final LocalLog pktLog = sPktLogs.get(ifname);
- if (pktLog != null) {
- writer.println("Connectivity packet log:");
- pktLog.readOnlyLocalLog().dump(null, writer, null);
- }
-
- writer.println(String.format("--- END %s ---", ifname));
- }
- }
-
- // Use a wrapper class to log in order to ensure complete and detailed
- // logging. This method is lighter weight than annotations/reflection
- // and has the following benefits:
- //
- // - No invoked method can be forgotten.
- // Any new method added to IpClient.Callback must be overridden
- // here or it will never be called.
- //
- // - No invoking call site can be forgotten.
- // Centralized logging in this way means call sites don't need to
- // remember to log, and therefore no call site can be forgotten.
- //
- // - No variation in log format among call sites.
- // Encourages logging of any available arguments, and all call sites
- // are necessarily logged identically.
- //
- // NOTE: Log first because passed objects may or may not be thread-safe and
- // once passed on to the callback they may be modified by another thread.
- //
- // TODO: Find an lighter weight approach.
- public static class IpClientCallbacksWrapper {
- private static final String PREFIX = "INVOKE ";
- private final IIpClientCallbacks mCallback;
- private final SharedLog mLog;
-
- @VisibleForTesting
- protected IpClientCallbacksWrapper(IIpClientCallbacks callback, SharedLog log) {
- mCallback = callback;
- mLog = log;
- }
-
- private void log(String msg) {
- mLog.log(PREFIX + msg);
- }
-
- private void log(String msg, Throwable e) {
- mLog.e(PREFIX + msg, e);
- }
-
- public void onPreDhcpAction() {
- log("onPreDhcpAction()");
- try {
- mCallback.onPreDhcpAction();
- } catch (RemoteException e) {
- log("Failed to call onPreDhcpAction", e);
- }
- }
-
- public void onPostDhcpAction() {
- log("onPostDhcpAction()");
- try {
- mCallback.onPostDhcpAction();
- } catch (RemoteException e) {
- log("Failed to call onPostDhcpAction", e);
- }
- }
-
- public void onNewDhcpResults(DhcpResults dhcpResults) {
- log("onNewDhcpResults({" + dhcpResults + "})");
- try {
- mCallback.onNewDhcpResults(toStableParcelable(dhcpResults));
- } catch (RemoteException e) {
- log("Failed to call onNewDhcpResults", e);
- }
- }
-
- public void onProvisioningSuccess(LinkProperties newLp) {
- log("onProvisioningSuccess({" + newLp + "})");
- try {
- mCallback.onProvisioningSuccess(newLp);
- } catch (RemoteException e) {
- log("Failed to call onProvisioningSuccess", e);
- }
- }
-
- public void onProvisioningFailure(LinkProperties newLp) {
- log("onProvisioningFailure({" + newLp + "})");
- try {
- mCallback.onProvisioningFailure(newLp);
- } catch (RemoteException e) {
- log("Failed to call onProvisioningFailure", e);
- }
- }
-
- public void onLinkPropertiesChange(LinkProperties newLp) {
- log("onLinkPropertiesChange({" + newLp + "})");
- try {
- mCallback.onLinkPropertiesChange(newLp);
- } catch (RemoteException e) {
- log("Failed to call onLinkPropertiesChange", e);
- }
- }
-
- public void onReachabilityLost(String logMsg) {
- log("onReachabilityLost(" + logMsg + ")");
- try {
- mCallback.onReachabilityLost(logMsg);
- } catch (RemoteException e) {
- log("Failed to call onReachabilityLost", e);
- }
- }
-
- public void onQuit() {
- log("onQuit()");
- try {
- mCallback.onQuit();
- } catch (RemoteException e) {
- log("Failed to call onQuit", e);
- }
- }
-
- public void installPacketFilter(byte[] filter) {
- log("installPacketFilter(byte[" + filter.length + "])");
- try {
- mCallback.installPacketFilter(filter);
- } catch (RemoteException e) {
- log("Failed to call installPacketFilter", e);
- }
- }
-
- public void startReadPacketFilter() {
- log("startReadPacketFilter()");
- try {
- mCallback.startReadPacketFilter();
- } catch (RemoteException e) {
- log("Failed to call startReadPacketFilter", e);
- }
- }
-
- public void setFallbackMulticastFilter(boolean enabled) {
- log("setFallbackMulticastFilter(" + enabled + ")");
- try {
- mCallback.setFallbackMulticastFilter(enabled);
- } catch (RemoteException e) {
- log("Failed to call setFallbackMulticastFilter", e);
- }
- }
-
- public void setNeighborDiscoveryOffload(boolean enable) {
- log("setNeighborDiscoveryOffload(" + enable + ")");
- try {
- mCallback.setNeighborDiscoveryOffload(enable);
- } catch (RemoteException e) {
- log("Failed to call setNeighborDiscoveryOffload", e);
- }
- }
- }
-
- public static final String DUMP_ARG_CONFIRM = "confirm";
-
- // Below constants are picked up by MessageUtils and exempt from ProGuard optimization.
- private static final int CMD_TERMINATE_AFTER_STOP = 1;
- private static final int CMD_STOP = 2;
- private static final int CMD_START = 3;
- private static final int CMD_CONFIRM = 4;
- private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 5;
- // Triggered by NetlinkTracker to communicate netlink events.
- private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6;
- private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 7;
- private static final int CMD_UPDATE_HTTP_PROXY = 8;
- private static final int CMD_SET_MULTICAST_FILTER = 9;
- private static final int EVENT_PROVISIONING_TIMEOUT = 10;
- private static final int EVENT_DHCPACTION_TIMEOUT = 11;
- private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12;
- private static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = 13;
- private static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = 14;
- private static final int CMD_UPDATE_L2KEY_GROUPHINT = 15;
-
- // Internal commands to use instead of trying to call transitionTo() inside
- // a given State's enter() method. Calling transitionTo() from enter/exit
- // encounters a Log.wtf() that can cause trouble on eng builds.
- private static final int CMD_JUMP_STARTED_TO_RUNNING = 100;
- private static final int CMD_JUMP_RUNNING_TO_STOPPING = 101;
- private static final int CMD_JUMP_STOPPING_TO_STOPPED = 102;
-
- // IpClient shares a handler with DhcpClient: commands must not overlap
- public static final int DHCPCLIENT_CMD_BASE = 1000;
-
- private static final int MAX_LOG_RECORDS = 500;
- private static final int MAX_PACKET_RECORDS = 100;
-
- private static final boolean NO_CALLBACKS = false;
- private static final boolean SEND_CALLBACKS = true;
-
- // This must match the interface prefix in clatd.c.
- // TODO: Revert this hack once IpClient and Nat464Xlat work in concert.
- private static final String CLAT_PREFIX = "v4-";
-
- private static final int IMMEDIATE_FAILURE_DURATION = 0;
-
- private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1;
- private static final int PROV_CHANGE_LOST_PROVISIONING = 2;
- private static final int PROV_CHANGE_GAINED_PROVISIONING = 3;
- private static final int PROV_CHANGE_STILL_PROVISIONED = 4;
-
- private final State mStoppedState = new StoppedState();
- private final State mStoppingState = new StoppingState();
- private final State mStartedState = new StartedState();
- private final State mRunningState = new RunningState();
-
- private final String mTag;
- private final Context mContext;
- private final String mInterfaceName;
- private final String mClatInterfaceName;
- @VisibleForTesting
- protected final IpClientCallbacksWrapper mCallback;
- private final Dependencies mDependencies;
- private final CountDownLatch mShutdownLatch;
- private final ConnectivityManager mCm;
- private final INetd mNetd;
- private final NetworkObserverRegistry mObserverRegistry;
- private final IpClientLinkObserver mLinkObserver;
- private final WakeupMessage mProvisioningTimeoutAlarm;
- private final WakeupMessage mDhcpActionTimeoutAlarm;
- private final SharedLog mLog;
- private final LocalLog mConnectivityPacketLog;
- private final MessageHandlingLogger mMsgStateLogger;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
- private final InterfaceController mInterfaceCtrl;
-
- private InterfaceParams mInterfaceParams;
-
- /**
- * Non-final member variables accessed only from within our StateMachine.
- */
- private LinkProperties mLinkProperties;
- private android.net.shared.ProvisioningConfiguration mConfiguration;
- private IpReachabilityMonitor mIpReachabilityMonitor;
- private DhcpClient mDhcpClient;
- private DhcpResults mDhcpResults;
- private String mTcpBufferSizes;
- private ProxyInfo mHttpProxy;
- private ApfFilter mApfFilter;
- private String mL2Key; // The L2 key for this network, for writing into the memory store
- private String mGroupHint; // The group hint for this network, for writing into the memory store
- private boolean mMulticastFiltering;
- private long mStartTimeMillis;
-
- /**
- * Reading the snapshot is an asynchronous operation initiated by invoking
- * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an
- * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable
- * signals when a new snapshot is ready.
- */
- private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable();
-
- public static class Dependencies {
- /**
- * Get interface parameters for the specified interface.
- */
- public InterfaceParams getInterfaceParams(String ifname) {
- return InterfaceParams.getByName(ifname);
- }
-
- /**
- * Get a INetd connector.
- */
- public INetd getNetd(Context context) {
- return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));
- }
- }
-
- public IpClient(Context context, String ifName, IIpClientCallbacks callback,
- NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager) {
- this(context, ifName, callback, observerRegistry, nssManager, new Dependencies());
- }
-
- @VisibleForTesting
- IpClient(Context context, String ifName, IIpClientCallbacks callback,
- NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager,
- Dependencies deps) {
- super(IpClient.class.getSimpleName() + "." + ifName);
- Preconditions.checkNotNull(ifName);
- Preconditions.checkNotNull(callback);
-
- mTag = getName();
-
- mContext = context;
- mInterfaceName = ifName;
- mClatInterfaceName = CLAT_PREFIX + ifName;
- mDependencies = deps;
- mShutdownLatch = new CountDownLatch(1);
- mCm = mContext.getSystemService(ConnectivityManager.class);
- mObserverRegistry = observerRegistry;
- mIpMemoryStore =
- new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService());
-
- sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
- mLog = sSmLogs.get(mInterfaceName);
- sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS));
- mConnectivityPacketLog = sPktLogs.get(mInterfaceName);
- mMsgStateLogger = new MessageHandlingLogger();
- mCallback = new IpClientCallbacksWrapper(callback, mLog);
-
- // TODO: Consider creating, constructing, and passing in some kind of
- // InterfaceController.Dependencies class.
- mNetd = deps.getNetd(mContext);
- mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog);
-
- mLinkObserver = new IpClientLinkObserver(
- mInterfaceName,
- () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED)) {
- @Override
- public void onInterfaceAdded(String iface) {
- super.onInterfaceAdded(iface);
- if (mClatInterfaceName.equals(iface)) {
- mCallback.setNeighborDiscoveryOffload(false);
- } else if (!mInterfaceName.equals(iface)) {
- return;
- }
-
- final String msg = "interfaceAdded(" + iface + ")";
- logMsg(msg);
- }
-
- @Override
- public void onInterfaceRemoved(String iface) {
- super.onInterfaceRemoved(iface);
- // TODO: Also observe mInterfaceName going down and take some
- // kind of appropriate action.
- if (mClatInterfaceName.equals(iface)) {
- // TODO: consider sending a message to the IpClient main
- // StateMachine thread, in case "NDO enabled" state becomes
- // tied to more things that 464xlat operation.
- mCallback.setNeighborDiscoveryOffload(true);
- } else if (!mInterfaceName.equals(iface)) {
- return;
- }
-
- final String msg = "interfaceRemoved(" + iface + ")";
- logMsg(msg);
- }
-
- private void logMsg(String msg) {
- Log.d(mTag, msg);
- getHandler().post(() -> mLog.log("OBSERVED " + msg));
- }
- };
-
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mInterfaceName);
-
- mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
- mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
- mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
- mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
-
- // Anything the StateMachine may access must have been instantiated
- // before this point.
- configureAndStartStateMachine();
-
- // Anything that may send messages to the StateMachine must only be
- // configured to do so after the StateMachine has started (above).
- startStateMachineUpdaters();
- }
-
- /**
- * Make a IIpClient connector to communicate with this IpClient.
- */
- public IIpClient makeConnector() {
- return new IpClientConnector();
- }
-
- class IpClientConnector extends IIpClient.Stub {
- @Override
- public void completedPreDhcpAction() {
- checkNetworkStackCallingPermission();
- IpClient.this.completedPreDhcpAction();
- }
- @Override
- public void confirmConfiguration() {
- checkNetworkStackCallingPermission();
- IpClient.this.confirmConfiguration();
- }
- @Override
- public void readPacketFilterComplete(byte[] data) {
- checkNetworkStackCallingPermission();
- IpClient.this.readPacketFilterComplete(data);
- }
- @Override
- public void shutdown() {
- checkNetworkStackCallingPermission();
- IpClient.this.shutdown();
- }
- @Override
- public void startProvisioning(ProvisioningConfigurationParcelable req) {
- checkNetworkStackCallingPermission();
- IpClient.this.startProvisioning(ProvisioningConfiguration.fromStableParcelable(req));
- }
- @Override
- public void stop() {
- checkNetworkStackCallingPermission();
- IpClient.this.stop();
- }
- @Override
- public void setL2KeyAndGroupHint(String l2Key, String groupHint) {
- checkNetworkStackCallingPermission();
- IpClient.this.setL2KeyAndGroupHint(l2Key, groupHint);
- }
- @Override
- public void setTcpBufferSizes(String tcpBufferSizes) {
- checkNetworkStackCallingPermission();
- IpClient.this.setTcpBufferSizes(tcpBufferSizes);
- }
- @Override
- public void setHttpProxy(ProxyInfo proxyInfo) {
- checkNetworkStackCallingPermission();
- IpClient.this.setHttpProxy(proxyInfo);
- }
- @Override
- public void setMulticastFilter(boolean enabled) {
- checkNetworkStackCallingPermission();
- IpClient.this.setMulticastFilter(enabled);
- }
- @Override
- public void addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) {
- checkNetworkStackCallingPermission();
- IpClient.this.addKeepalivePacketFilter(slot, pkt);
- }
- @Override
- public void addNattKeepalivePacketFilter(int slot, NattKeepalivePacketDataParcelable pkt) {
- checkNetworkStackCallingPermission();
- IpClient.this.addNattKeepalivePacketFilter(slot, pkt);
- }
- @Override
- public void removeKeepalivePacketFilter(int slot) {
- checkNetworkStackCallingPermission();
- IpClient.this.removeKeepalivePacketFilter(slot);
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- }
-
- public String getInterfaceName() {
- return mInterfaceName;
- }
-
- private void configureAndStartStateMachine() {
- // CHECKSTYLE:OFF IndentationCheck
- addState(mStoppedState);
- addState(mStartedState);
- addState(mRunningState, mStartedState);
- addState(mStoppingState);
- // CHECKSTYLE:ON IndentationCheck
-
- setInitialState(mStoppedState);
-
- super.start();
- }
-
- private void startStateMachineUpdaters() {
- mObserverRegistry.registerObserverForNonblockingCallback(mLinkObserver);
- }
-
- private void stopStateMachineUpdaters() {
- mObserverRegistry.unregisterObserver(mLinkObserver);
- }
-
- @Override
- protected void onQuitting() {
- mCallback.onQuit();
- mShutdownLatch.countDown();
- }
-
- /**
- * Shut down this IpClient instance altogether.
- */
- public void shutdown() {
- stop();
- sendMessage(CMD_TERMINATE_AFTER_STOP);
- }
-
- /**
- * Start provisioning with the provided parameters.
- */
- public void startProvisioning(ProvisioningConfiguration req) {
- if (!req.isValid()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
- return;
- }
-
- mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName);
- if (mInterfaceParams == null) {
- logError("Failed to find InterfaceParams for " + mInterfaceName);
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
- return;
- }
-
- mCallback.setNeighborDiscoveryOffload(true);
- sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req));
- }
-
- /**
- * Stop this IpClient.
- *
- *
This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}.
- */
- public void stop() {
- sendMessage(CMD_STOP);
- }
-
- /**
- * Confirm the provisioning configuration.
- */
- public void confirmConfiguration() {
- sendMessage(CMD_CONFIRM);
- }
-
- /**
- * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be
- * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to
- * proceed.
- */
- public void completedPreDhcpAction() {
- sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
- }
-
- /**
- * Indicate that packet filter read is complete.
- */
- public void readPacketFilterComplete(byte[] data) {
- sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data);
- }
-
- /**
- * Set the TCP buffer sizes to use.
- *
- * This may be called, repeatedly, at any time before or after a call to
- * #startProvisioning(). The setting is cleared upon calling #stop().
- */
- public void setTcpBufferSizes(String tcpBufferSizes) {
- sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes);
- }
-
- /**
- * Set the L2 key and group hint for storing info into the memory store.
- */
- public void setL2KeyAndGroupHint(String l2Key, String groupHint) {
- sendMessage(CMD_UPDATE_L2KEY_GROUPHINT, new Pair<>(l2Key, groupHint));
- }
-
- /**
- * Set the HTTP Proxy configuration to use.
- *
- * This may be called, repeatedly, at any time before or after a call to
- * #startProvisioning(). The setting is cleared upon calling #stop().
- */
- public void setHttpProxy(ProxyInfo proxyInfo) {
- sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
- }
-
- /**
- * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering,
- * if not, Callback.setFallbackMulticastFilter() is called.
- */
- public void setMulticastFilter(boolean enabled) {
- sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
- }
-
- /**
- * Called by WifiStateMachine to add TCP keepalive packet filter before setting up
- * keepalive offload.
- */
- public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) {
- sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt);
- }
-
- /**
- * Called by WifiStateMachine to add NATT keepalive packet filter before setting up
- * keepalive offload.
- */
- public void addNattKeepalivePacketFilter(int slot,
- @NonNull NattKeepalivePacketDataParcelable pkt) {
- sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */ , pkt);
- }
-
- /**
- * Called by WifiStateMachine to remove keepalive packet filter after stopping keepalive
- * offload.
- */
- public void removeKeepalivePacketFilter(int slot) {
- sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF, slot, 0 /* Unused */);
- }
-
- /**
- * Dump logs of this IpClient.
- */
- public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) {
- // Execute confirmConfiguration() and take no further action.
- confirmConfiguration();
- return;
- }
-
- // Thread-unsafe access to mApfFilter but just used for debugging.
- final ApfFilter apfFilter = mApfFilter;
- final android.net.shared.ProvisioningConfiguration provisioningConfig = mConfiguration;
- final ApfCapabilities apfCapabilities = (provisioningConfig != null)
- ? provisioningConfig.mApfCapabilities : null;
-
- IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- pw.println(mTag + " APF dump:");
- pw.increaseIndent();
- if (apfFilter != null) {
- if (apfCapabilities.hasDataAccess()) {
- // Request a new snapshot, then wait for it.
- mApfDataSnapshotComplete.close();
- mCallback.startReadPacketFilter();
- if (!mApfDataSnapshotComplete.block(1000)) {
- pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT");
- }
- }
- apfFilter.dump(pw);
-
- } else {
- pw.print("No active ApfFilter; ");
- if (provisioningConfig == null) {
- pw.println("IpClient not yet started.");
- } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) {
- pw.println("Hardware does not support APF.");
- } else {
- pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities);
- }
- }
- pw.decreaseIndent();
- pw.println();
- pw.println(mTag + " current ProvisioningConfiguration:");
- pw.increaseIndent();
- pw.println(Objects.toString(provisioningConfig, "N/A"));
- pw.decreaseIndent();
-
- final IpReachabilityMonitor iprm = mIpReachabilityMonitor;
- if (iprm != null) {
- pw.println();
- pw.println(mTag + " current IpReachabilityMonitor state:");
- pw.increaseIndent();
- iprm.dump(pw);
- pw.decreaseIndent();
- }
-
- pw.println();
- pw.println(mTag + " StateMachine dump:");
- pw.increaseIndent();
- mLog.dump(fd, pw, args);
- pw.decreaseIndent();
-
- pw.println();
- pw.println(mTag + " connectivity packet log:");
- pw.println();
- pw.println("Debug with python and scapy via:");
- pw.println("shell$ python");
- pw.println(">>> from scapy import all as scapy");
- pw.println(">>> scapy.Ether(\"\".decode(\"hex\")).show2()");
- pw.println();
-
- pw.increaseIndent();
- mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
- pw.decreaseIndent();
- }
-
-
- /**
- * Internals.
- */
-
- @Override
- protected String getWhatToString(int what) {
- return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
- }
-
- @Override
- protected String getLogRecString(Message msg) {
- final String logLine = String.format(
- "%s/%d %d %d %s [%s]",
- mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index,
- msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
-
- final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
- mLog.log(richerLogLine);
- if (DBG) {
- Log.d(mTag, richerLogLine);
- }
-
- mMsgStateLogger.reset();
- return logLine;
- }
-
- @Override
- protected boolean recordLogRec(Message msg) {
- // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy,
- // and we already log any LinkProperties change that results in an
- // invocation of IpClient.Callback#onLinkPropertiesChange().
- final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED);
- if (!shouldLog) {
- mMsgStateLogger.reset();
- }
- return shouldLog;
- }
-
- private void logError(String fmt, Object... args) {
- final String msg = "ERROR " + String.format(fmt, args);
- Log.e(mTag, msg);
- mLog.log(msg);
- }
-
- // This needs to be called with care to ensure that our LinkProperties
- // are in sync with the actual LinkProperties of the interface. For example,
- // we should only call this if we know for sure that there are no IP addresses
- // assigned to the interface, etc.
- private void resetLinkProperties() {
- mLinkObserver.clearLinkProperties();
- mConfiguration = null;
- mDhcpResults = null;
- mTcpBufferSizes = "";
- mHttpProxy = null;
-
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mInterfaceName);
- }
-
- private void recordMetric(final int type) {
- // We may record error metrics prior to starting.
- // Map this to IMMEDIATE_FAILURE_DURATION.
- final long duration = (mStartTimeMillis > 0)
- ? (SystemClock.elapsedRealtime() - mStartTimeMillis)
- : IMMEDIATE_FAILURE_DURATION;
- mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration));
- }
-
- // For now: use WifiStateMachine's historical notion of provisioned.
- @VisibleForTesting
- static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) {
- // For historical reasons, we should connect even if all we have is
- // an IPv4 address and nothing else.
- if (lp.hasIpv4Address() || lp.isProvisioned()) {
- return true;
- }
- if (config == null) {
- return false;
- }
-
- // When an InitialConfiguration is specified, ignore any difference with previous
- // properties and instead check if properties observed match the desired properties.
- return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes());
- }
-
- // TODO: Investigate folding all this into the existing static function
- // LinkProperties.compareProvisioning() or some other single function that
- // takes two LinkProperties objects and returns a ProvisioningChange
- // object that is a correct and complete assessment of what changed, taking
- // account of the asymmetries described in the comments in this function.
- // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
- private int compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
- int delta;
- InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null;
- final boolean wasProvisioned = isProvisioned(oldLp, config);
- final boolean isProvisioned = isProvisioned(newLp, config);
-
- if (!wasProvisioned && isProvisioned) {
- delta = PROV_CHANGE_GAINED_PROVISIONING;
- } else if (wasProvisioned && isProvisioned) {
- delta = PROV_CHANGE_STILL_PROVISIONED;
- } else if (!wasProvisioned && !isProvisioned) {
- delta = PROV_CHANGE_STILL_NOT_PROVISIONED;
- } else {
- // (wasProvisioned && !isProvisioned)
- //
- // Note that this is true even if we lose a configuration element
- // (e.g., a default gateway) that would not be required to advance
- // into provisioned state. This is intended: if we have a default
- // router and we lose it, that's a sure sign of a problem, but if
- // we connect to a network with no IPv4 DNS servers, we consider
- // that to be a network without DNS servers and connect anyway.
- //
- // See the comment below.
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- final boolean lostIPv6 = oldLp.isIpv6Provisioned() && !newLp.isIpv6Provisioned();
- final boolean lostIPv4Address = oldLp.hasIpv4Address() && !newLp.hasIpv4Address();
- final boolean lostIPv6Router = oldLp.hasIpv6DefaultRoute() && !newLp.hasIpv6DefaultRoute();
-
- // If bad wifi avoidance is disabled, then ignore IPv6 loss of
- // provisioning. Otherwise, when a hotspot that loses Internet
- // access sends out a 0-lifetime RA to its clients, the clients
- // will disconnect and then reconnect, avoiding the bad hotspot,
- // instead of getting stuck on the bad hotspot. http://b/31827713 .
- //
- // This is incorrect because if the hotspot then regains Internet
- // access with a different prefix, TCP connections on the
- // deprecated addresses will remain stuck.
- //
- // Note that we can still be disconnected by IpReachabilityMonitor
- // if the IPv6 default gateway (but not the IPv6 DNS servers; see
- // accompanying code in IpReachabilityMonitor) is unreachable.
- final boolean ignoreIPv6ProvisioningLoss =
- mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
- && mCm.shouldAvoidBadWifi();
-
- // Additionally:
- //
- // Partial configurations (e.g., only an IPv4 address with no DNS
- // servers and no default route) are accepted as long as DHCPv4
- // succeeds. On such a network, isProvisioned() will always return
- // false, because the configuration is not complete, but we want to
- // connect anyway. It might be a disconnected network such as a
- // Chromecast or a wireless printer, for example.
- //
- // Because on such a network isProvisioned() will always return false,
- // delta will never be LOST_PROVISIONING. So check for loss of
- // provisioning here too.
- if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- // Additionally:
- //
- // If the previous link properties had a global IPv6 address and an
- // IPv6 default route then also consider the loss of that default route
- // to be a loss of provisioning. See b/27962810.
- if (oldLp.hasGlobalIpv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- return delta;
- }
-
- private void dispatchCallback(int delta, LinkProperties newLp) {
- switch (delta) {
- case PROV_CHANGE_GAINED_PROVISIONING:
- if (DBG) {
- Log.d(mTag, "onProvisioningSuccess()");
- }
- recordMetric(IpManagerEvent.PROVISIONING_OK);
- mCallback.onProvisioningSuccess(newLp);
- break;
-
- case PROV_CHANGE_LOST_PROVISIONING:
- if (DBG) {
- Log.d(mTag, "onProvisioningFailure()");
- }
- recordMetric(IpManagerEvent.PROVISIONING_FAIL);
- mCallback.onProvisioningFailure(newLp);
- break;
-
- default:
- if (DBG) {
- Log.d(mTag, "onLinkPropertiesChange()");
- }
- mCallback.onLinkPropertiesChange(newLp);
- break;
- }
- }
-
- // Updates all IpClient-related state concerned with LinkProperties.
- // Returns a ProvisioningChange for possibly notifying other interested
- // parties that are not fronted by IpClient.
- private int setLinkProperties(LinkProperties newLp) {
- if (mApfFilter != null) {
- mApfFilter.setLinkProperties(newLp);
- }
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.updateLinkProperties(newLp);
- }
-
- int delta = compareProvisioning(mLinkProperties, newLp);
- mLinkProperties = new LinkProperties(newLp);
-
- if (delta == PROV_CHANGE_GAINED_PROVISIONING) {
- // TODO: Add a proper ProvisionedState and cancel the alarm in
- // its enter() method.
- mProvisioningTimeoutAlarm.cancel();
- }
-
- return delta;
- }
-
- private LinkProperties assembleLinkProperties() {
- // [1] Create a new LinkProperties object to populate.
- LinkProperties newLp = new LinkProperties();
- newLp.setInterfaceName(mInterfaceName);
-
- // [2] Pull in data from netlink:
- // - IPv4 addresses
- // - IPv6 addresses
- // - IPv6 routes
- // - IPv6 DNS servers
- //
- // N.B.: this is fundamentally race-prone and should be fixed by
- // changing IpClientLinkObserver from a hybrid edge/level model to an
- // edge-only model, or by giving IpClient its own netlink socket(s)
- // so as to track all required information directly.
- LinkProperties netlinkLinkProperties = mLinkObserver.getLinkProperties();
- newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
- for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
- newLp.addRoute(route);
- }
- addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
-
- // [3] Add in data from DHCPv4, if available.
- //
- // mDhcpResults is never shared with any other owner so we don't have
- // to worry about concurrent modification.
- if (mDhcpResults != null) {
- final List routes =
- mDhcpResults.toStaticIpConfiguration().getRoutes(mInterfaceName);
- for (RouteInfo route : routes) {
- newLp.addRoute(route);
- }
- addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
- newLp.setDomains(mDhcpResults.domains);
-
- if (mDhcpResults.mtu != 0) {
- newLp.setMtu(mDhcpResults.mtu);
- }
- }
-
- // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
- if (!TextUtils.isEmpty(mTcpBufferSizes)) {
- newLp.setTcpBufferSizes(mTcpBufferSizes);
- }
- if (mHttpProxy != null) {
- newLp.setHttpProxy(mHttpProxy);
- }
-
- // [5] Add data from InitialConfiguration
- if (mConfiguration != null && mConfiguration.mInitialConfig != null) {
- InitialConfiguration config = mConfiguration.mInitialConfig;
- // Add InitialConfiguration routes and dns server addresses once all addresses
- // specified in the InitialConfiguration have been observed with Netlink.
- if (config.isProvisionedBy(newLp.getLinkAddresses(), null)) {
- for (IpPrefix prefix : config.directlyConnectedRoutes) {
- newLp.addRoute(new RouteInfo(prefix, null, mInterfaceName, RTN_UNICAST));
- }
- }
- addAllReachableDnsServers(newLp, config.dnsServers);
- }
- final LinkProperties oldLp = mLinkProperties;
- if (DBG) {
- Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s",
- netlinkLinkProperties, newLp, oldLp));
- }
-
- // TODO: also learn via netlink routes specified by an InitialConfiguration and specified
- // from a static IP v4 config instead of manually patching them in in steps [3] and [5].
- return newLp;
- }
-
- private static void addAllReachableDnsServers(
- LinkProperties lp, Iterable dnses) {
- // TODO: Investigate deleting this reachability check. We should be
- // able to pass everything down to netd and let netd do evaluation
- // and RFC6724-style sorting.
- for (InetAddress dns : dnses) {
- if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) {
- lp.addDnsServer(dns);
- }
- }
- }
-
- // Returns false if we have lost provisioning, true otherwise.
- private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
- final LinkProperties newLp = assembleLinkProperties();
- if (Objects.equals(newLp, mLinkProperties)) {
- return true;
- }
- final int delta = setLinkProperties(newLp);
- // Most of the attributes stored in the memory store are deduced from
- // the link properties, therefore when the properties update the memory
- // store record should be updated too.
- maybeSaveNetworkToIpMemoryStore();
- if (sendCallbacks) {
- dispatchCallback(delta, newLp);
- }
- return (delta != PROV_CHANGE_LOST_PROVISIONING);
- }
-
- private void handleIPv4Success(DhcpResults dhcpResults) {
- mDhcpResults = new DhcpResults(dhcpResults);
- final LinkProperties newLp = assembleLinkProperties();
- final int delta = setLinkProperties(newLp);
-
- if (DBG) {
- Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
- }
- mCallback.onNewDhcpResults(dhcpResults);
- maybeSaveNetworkToIpMemoryStore();
- dispatchCallback(delta, newLp);
- }
-
- private void handleIPv4Failure() {
- // TODO: Investigate deleting this clearIPv4Address() call.
- //
- // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
- // that could trigger a call to this function. If we missed handling
- // that message in StartedState for some reason we would still clear
- // any addresses upon entry to StoppedState.
- mInterfaceCtrl.clearIPv4Address();
- mDhcpResults = null;
- if (DBG) {
- Log.d(mTag, "onNewDhcpResults(null)");
- }
- mCallback.onNewDhcpResults(null);
-
- handleProvisioningFailure();
- }
-
- private void handleProvisioningFailure() {
- final LinkProperties newLp = assembleLinkProperties();
- int delta = setLinkProperties(newLp);
- // If we've gotten here and we're still not provisioned treat that as
- // a total loss of provisioning.
- //
- // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
- // there was no usable IPv6 obtained before a non-zero provisioning
- // timeout expired.
- //
- // Regardless: GAME OVER.
- if (delta == PROV_CHANGE_STILL_NOT_PROVISIONED) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- dispatchCallback(delta, newLp);
- if (delta == PROV_CHANGE_LOST_PROVISIONING) {
- transitionTo(mStoppingState);
- }
- }
-
- private void doImmediateProvisioningFailure(int failureType) {
- logError("onProvisioningFailure(): %s", failureType);
- recordMetric(failureType);
- mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
- }
-
- private boolean startIPv4() {
- // If we have a StaticIpConfiguration attempt to apply it and
- // handle the result accordingly.
- if (mConfiguration.mStaticIpConfig != null) {
- if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.getIpAddress())) {
- handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
- } else {
- return false;
- }
- } else {
- // Start DHCPv4.
- mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
- mDhcpClient.registerForPreDhcpNotification();
- mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
- }
-
- return true;
- }
-
- private boolean startIPv6() {
- return mInterfaceCtrl.setIPv6PrivacyExtensions(true)
- && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode)
- && mInterfaceCtrl.enableIPv6();
- }
-
- private boolean applyInitialConfig(InitialConfiguration config) {
- // TODO: also support specifying a static IPv4 configuration in InitialConfiguration.
- for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIpv6)) {
- if (!mInterfaceCtrl.addAddress(addr)) return false;
- }
-
- return true;
- }
-
- private boolean startIpReachabilityMonitor() {
- try {
- // TODO: Fetch these parameters from settings, and install a
- // settings observer to watch for update and re-program these
- // parameters (Q: is this level of dynamic updatability really
- // necessary or does reading from settings at startup suffice?).
- final int numSolicits = 5;
- final int interSolicitIntervalMs = 750;
- setNeighborParameters(mNetd, mInterfaceName, numSolicits, interSolicitIntervalMs);
- } catch (Exception e) {
- mLog.e("Failed to adjust neighbor parameters", e);
- // Carry on using the system defaults (currently: 3, 1000);
- }
-
- try {
- mIpReachabilityMonitor = new IpReachabilityMonitor(
- mContext,
- mInterfaceParams,
- getHandler(),
- mLog,
- new IpReachabilityMonitor.Callback() {
- @Override
- public void notifyLost(InetAddress ip, String logMsg) {
- mCallback.onReachabilityLost(logMsg);
- }
- },
- mConfiguration.mUsingMultinetworkPolicyTracker);
- } catch (IllegalArgumentException iae) {
- // Failed to start IpReachabilityMonitor. Log it and call
- // onProvisioningFailure() immediately.
- //
- // See http://b/31038971.
- logError("IpReachabilityMonitor failure: %s", iae);
- mIpReachabilityMonitor = null;
- }
-
- return (mIpReachabilityMonitor != null);
- }
-
- private void stopAllIP() {
- // We don't need to worry about routes, just addresses, because:
- // - disableIpv6() will clear autoconf IPv6 routes as well, and
- // - we don't get IPv4 routes from netlink
- // so we neither react to nor need to wait for changes in either.
-
- mInterfaceCtrl.disableIPv6();
- mInterfaceCtrl.clearAllAddresses();
- }
-
- private void maybeSaveNetworkToIpMemoryStore() {
- // TODO : implement this
- }
-
- class StoppedState extends State {
- @Override
- public void enter() {
- stopAllIP();
-
- resetLinkProperties();
- if (mStartTimeMillis > 0) {
- // Completed a life-cycle; send a final empty LinkProperties
- // (cleared in resetLinkProperties() above) and record an event.
- mCallback.onLinkPropertiesChange(new LinkProperties(mLinkProperties));
- recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
- mStartTimeMillis = 0;
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_TERMINATE_AFTER_STOP:
- stopStateMachineUpdaters();
- quit();
- break;
-
- case CMD_STOP:
- break;
-
- case CMD_START:
- mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj;
- transitionTo(mStartedState);
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_UPDATE_TCP_BUFFER_SIZES:
- mTcpBufferSizes = (String) msg.obj;
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_UPDATE_HTTP_PROXY:
- mHttpProxy = (ProxyInfo) msg.obj;
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_UPDATE_L2KEY_GROUPHINT: {
- final Pair args = (Pair) msg.obj;
- mL2Key = args.first;
- mGroupHint = args.second;
- break;
- }
-
- case CMD_SET_MULTICAST_FILTER:
- mMulticastFiltering = (boolean) msg.obj;
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- // Everything is already stopped.
- logError("Unexpected CMD_ON_QUIT (already stopped).");
- break;
-
- default:
- return NOT_HANDLED;
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- class StoppingState extends State {
- @Override
- public void enter() {
- if (mDhcpClient == null) {
- // There's no DHCPv4 for which to wait; proceed to stopped.
- deferMessage(obtainMessage(CMD_JUMP_STOPPING_TO_STOPPED));
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_STOPPING_TO_STOPPED:
- transitionTo(mStoppedState);
- break;
-
- case CMD_STOP:
- break;
-
- case DhcpClient.CMD_CLEAR_LINKADDRESS:
- mInterfaceCtrl.clearIPv4Address();
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- mDhcpClient = null;
- transitionTo(mStoppedState);
- break;
-
- default:
- deferMessage(msg);
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- class StartedState extends State {
- @Override
- public void enter() {
- mStartTimeMillis = SystemClock.elapsedRealtime();
-
- if (mConfiguration.mProvisioningTimeoutMs > 0) {
- final long alarmTime = SystemClock.elapsedRealtime()
- + mConfiguration.mProvisioningTimeoutMs;
- mProvisioningTimeoutAlarm.schedule(alarmTime);
- }
-
- if (readyToProceed()) {
- deferMessage(obtainMessage(CMD_JUMP_STARTED_TO_RUNNING));
- } else {
- // Clear all IPv4 and IPv6 before proceeding to RunningState.
- // Clean up any leftover state from an abnormal exit from
- // tethering or during an IpClient restart.
- stopAllIP();
- }
- }
-
- @Override
- public void exit() {
- mProvisioningTimeoutAlarm.cancel();
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_STARTED_TO_RUNNING:
- transitionTo(mRunningState);
- break;
-
- case CMD_STOP:
- transitionTo(mStoppingState);
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- if (readyToProceed()) {
- transitionTo(mRunningState);
- }
- break;
-
- case CMD_UPDATE_L2KEY_GROUPHINT: {
- final Pair args = (Pair) msg.obj;
- mL2Key = args.first;
- mGroupHint = args.second;
- // TODO : attributes should be saved to the memory store with
- // these new values if they differ from the previous ones.
- // If the state machine is in pure StartedState, then the values to input
- // are not known yet and should be updated when the LinkProperties are updated.
- // If the state machine is in RunningState (which is a child of StartedState)
- // then the next NUD check should be used to store the new values to avoid
- // inputting current values for what may be a different L3 network.
- break;
- }
-
- case EVENT_PROVISIONING_TIMEOUT:
- handleProvisioningFailure();
- break;
-
- default:
- // It's safe to process messages out of order because the
- // only message that can both
- // a) be received at this time and
- // b) affect provisioning state
- // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
- deferMessage(msg);
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
-
- private boolean readyToProceed() {
- return (!mLinkProperties.hasIpv4Address() && !mLinkProperties.hasGlobalIpv6Address());
- }
- }
-
- class RunningState extends State {
- private ConnectivityPacketTracker mPacketTracker;
- private boolean mDhcpActionInFlight;
-
- @Override
- public void enter() {
- ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration();
- apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
- apfConfig.multicastFilter = mMulticastFiltering;
- // Get the Configuration for ApfFilter from Context
- apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames();
- apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList();
- mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
- // TODO: investigate the effects of any multicast filtering racing/interfering with the
- // rest of this IP configuration startup.
- if (mApfFilter == null) {
- mCallback.setFallbackMulticastFilter(mMulticastFiltering);
- }
-
- mPacketTracker = createPacketTracker();
- if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
-
- if (mConfiguration.mEnableIPv6 && !startIPv6()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
- enqueueJumpToStoppingState();
- return;
- }
-
- if (mConfiguration.mEnableIPv4 && !startIPv4()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
- enqueueJumpToStoppingState();
- return;
- }
-
- final InitialConfiguration config = mConfiguration.mInitialConfig;
- if ((config != null) && !applyInitialConfig(config)) {
- // TODO introduce a new IpManagerEvent constant to distinguish this error case.
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
- enqueueJumpToStoppingState();
- return;
- }
-
- if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
- doImmediateProvisioningFailure(
- IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
- enqueueJumpToStoppingState();
- return;
- }
- }
-
- @Override
- public void exit() {
- stopDhcpAction();
-
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.stop();
- mIpReachabilityMonitor = null;
- }
-
- if (mDhcpClient != null) {
- mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
- mDhcpClient.doQuit();
- }
-
- if (mPacketTracker != null) {
- mPacketTracker.stop();
- mPacketTracker = null;
- }
-
- if (mApfFilter != null) {
- mApfFilter.shutdown();
- mApfFilter = null;
- }
-
- resetLinkProperties();
- }
-
- private void enqueueJumpToStoppingState() {
- deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING));
- }
-
- private ConnectivityPacketTracker createPacketTracker() {
- try {
- return new ConnectivityPacketTracker(
- getHandler(), mInterfaceParams, mConnectivityPacketLog);
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
-
- private void ensureDhcpAction() {
- if (!mDhcpActionInFlight) {
- mCallback.onPreDhcpAction();
- mDhcpActionInFlight = true;
- final long alarmTime = SystemClock.elapsedRealtime()
- + mConfiguration.mRequestedPreDhcpActionMs;
- mDhcpActionTimeoutAlarm.schedule(alarmTime);
- }
- }
-
- private void stopDhcpAction() {
- mDhcpActionTimeoutAlarm.cancel();
- if (mDhcpActionInFlight) {
- mCallback.onPostDhcpAction();
- mDhcpActionInFlight = false;
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_RUNNING_TO_STOPPING:
- case CMD_STOP:
- transitionTo(mStoppingState);
- break;
-
- case CMD_START:
- logError("ALERT: START received in StartedState. Please fix caller.");
- break;
-
- case CMD_CONFIRM:
- // TODO: Possibly introduce a second type of confirmation
- // that both probes (a) on-link neighbors and (b) does
- // a DHCPv4 RENEW. We used to do this on Wi-Fi framework
- // roams.
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.probeAll();
- }
- break;
-
- case EVENT_PRE_DHCP_ACTION_COMPLETE:
- // It's possible to reach here if, for example, someone
- // calls completedPreDhcpAction() after provisioning with
- // a static IP configuration.
- if (mDhcpClient != null) {
- mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
- }
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
- transitionTo(mStoppingState);
- }
- break;
-
- case CMD_UPDATE_TCP_BUFFER_SIZES:
- mTcpBufferSizes = (String) msg.obj;
- // This cannot possibly change provisioning state.
- handleLinkPropertiesUpdate(SEND_CALLBACKS);
- break;
-
- case CMD_UPDATE_HTTP_PROXY:
- mHttpProxy = (ProxyInfo) msg.obj;
- // This cannot possibly change provisioning state.
- handleLinkPropertiesUpdate(SEND_CALLBACKS);
- break;
-
- case CMD_SET_MULTICAST_FILTER: {
- mMulticastFiltering = (boolean) msg.obj;
- if (mApfFilter != null) {
- mApfFilter.setMulticastFilter(mMulticastFiltering);
- } else {
- mCallback.setFallbackMulticastFilter(mMulticastFiltering);
- }
- break;
- }
-
- case EVENT_READ_PACKET_FILTER_COMPLETE: {
- if (mApfFilter != null) {
- mApfFilter.setDataSnapshot((byte[]) msg.obj);
- }
- mApfDataSnapshotComplete.open();
- break;
- }
-
- case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: {
- final int slot = msg.arg1;
-
- if (mApfFilter != null) {
- if (msg.obj instanceof NattKeepalivePacketDataParcelable) {
- mApfFilter.addNattKeepalivePacketFilter(slot,
- (NattKeepalivePacketDataParcelable) msg.obj);
- } else if (msg.obj instanceof TcpKeepalivePacketDataParcelable) {
- mApfFilter.addTcpKeepalivePacketFilter(slot,
- (TcpKeepalivePacketDataParcelable) msg.obj);
- }
- }
- break;
- }
-
- case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF: {
- final int slot = msg.arg1;
- if (mApfFilter != null) {
- mApfFilter.removeKeepalivePacketFilter(slot);
- }
- break;
- }
-
- case EVENT_DHCPACTION_TIMEOUT:
- stopDhcpAction();
- break;
-
- case DhcpClient.CMD_PRE_DHCP_ACTION:
- if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
- ensureDhcpAction();
- } else {
- sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
- }
- break;
-
- case DhcpClient.CMD_CLEAR_LINKADDRESS:
- mInterfaceCtrl.clearIPv4Address();
- break;
-
- case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
- final LinkAddress ipAddress = (LinkAddress) msg.obj;
- if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
- mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
- } else {
- logError("Failed to set IPv4 address.");
- dispatchCallback(PROV_CHANGE_LOST_PROVISIONING,
- new LinkProperties(mLinkProperties));
- transitionTo(mStoppingState);
- }
- break;
- }
-
- // This message is only received when:
- //
- // a) initial address acquisition succeeds,
- // b) renew succeeds or is NAK'd,
- // c) rebind succeeds or is NAK'd, or
- // c) the lease expires,
- //
- // but never when initial address acquisition fails. The latter
- // condition is now governed by the provisioning timeout.
- case DhcpClient.CMD_POST_DHCP_ACTION:
- stopDhcpAction();
-
- switch (msg.arg1) {
- case DhcpClient.DHCP_SUCCESS:
- handleIPv4Success((DhcpResults) msg.obj);
- break;
- case DhcpClient.DHCP_FAILURE:
- handleIPv4Failure();
- break;
- default:
- logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1);
- }
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- // DHCPv4 quit early for some reason.
- logError("Unexpected CMD_ON_QUIT.");
- mDhcpClient = null;
- break;
-
- default:
- return NOT_HANDLED;
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- private static class MessageHandlingLogger {
- public String processedInState;
- public String receivedInState;
-
- public void reset() {
- processedInState = null;
- receivedInState = null;
- }
-
- public void handled(State processedIn, IState receivedIn) {
- processedInState = processedIn.getClass().getSimpleName();
- receivedInState = receivedIn.getName();
- }
-
- public String toString() {
- return String.format("rcvd_in=%s, proc_in=%s",
- receivedInState, processedInState);
- }
- }
-
- private static void setNeighborParameters(
- INetd netd, String ifName, int numSolicits, int interSolicitIntervalMs)
- throws RemoteException, IllegalArgumentException {
- Preconditions.checkNotNull(netd);
- Preconditions.checkArgument(!TextUtils.isEmpty(ifName));
- Preconditions.checkArgument(numSolicits > 0);
- Preconditions.checkArgument(interSolicitIntervalMs > 0);
-
- for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) {
- netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms",
- Integer.toString(interSolicitIntervalMs));
- netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit",
- Integer.toString(numSolicits));
- }
- }
-
- // TODO: extract out into CollectionUtils.
- static boolean any(Iterable coll, Predicate fn) {
- for (T t : coll) {
- if (fn.test(t)) {
- return true;
- }
- }
- return false;
- }
-
- static boolean all(Iterable coll, Predicate fn) {
- return !any(coll, not(fn));
- }
-
- static Predicate not(Predicate fn) {
- return (t) -> !fn.test(t);
- }
-
- static String join(String delimiter, Collection coll) {
- return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter));
- }
-
- static T find(Iterable coll, Predicate fn) {
- for (T t: coll) {
- if (fn.test(t)) {
- return t;
- }
- }
- return null;
- }
-
- static List findAll(Collection coll, Predicate fn) {
- return coll.stream().filter(fn).collect(Collectors.toList());
- }
-}
diff --git a/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java b/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java
deleted file mode 100644
index 8ad99aa0399af..0000000000000
--- a/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import android.net.InetAddresses;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.RouteInfo;
-import android.util.Log;
-
-import com.android.server.NetworkObserver;
-
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Keeps track of link configuration received from Netd.
- *
- * An instance of this class is constructed by passing in an interface name and a callback. The
- * owner is then responsible for registering the tracker with NetworkObserverRegistry. When the
- * class receives update notifications, it applies the update to its local LinkProperties, and if
- * something has changed, notifies its owner of the update via the callback.
- *
- * The owner can then call {@code getLinkProperties()} in order to find out
- * what changed. If in the meantime the LinkProperties stored here have changed,
- * this class will return the current LinkProperties. Because each change
- * triggers an update callback after the change is made, the owner may get more
- * callbacks than strictly necessary (some of which may be no-ops), but will not
- * be out of sync once all callbacks have been processed.
- *
- * Threading model:
- *
- * - The owner of this class is expected to create it, register it, and call
- * getLinkProperties or clearLinkProperties on its thread.
- * - Most of the methods in the class are implementing NetworkObserver and are called
- * on the handler used to register the observer.
- * - All accesses to mLinkProperties must be synchronized(this). All the other
- * member variables are immutable once the object is constructed.
- *
- * @hide
- */
-public class IpClientLinkObserver implements NetworkObserver {
- private final String mTag;
-
- /**
- * Callback used by {@link IpClientLinkObserver} to send update notifications.
- */
- public interface Callback {
- /**
- * Called when some properties of the link were updated.
- */
- void update();
- }
-
- private final String mInterfaceName;
- private final Callback mCallback;
- private final LinkProperties mLinkProperties;
- private DnsServerRepository mDnsServerRepository;
-
- private static final boolean DBG = false;
-
- public IpClientLinkObserver(String iface, Callback callback) {
- mTag = "NetlinkTracker/" + iface;
- mInterfaceName = iface;
- mCallback = callback;
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mInterfaceName);
- mDnsServerRepository = new DnsServerRepository();
- }
-
- private void maybeLog(String operation, String iface, LinkAddress address) {
- if (DBG) {
- Log.d(mTag, operation + ": " + address + " on " + iface
- + " flags " + address.getFlags() + " scope " + address.getScope());
- }
- }
-
- private void maybeLog(String operation, Object o) {
- if (DBG) {
- Log.d(mTag, operation + ": " + o.toString());
- }
- }
-
- @Override
- public void onInterfaceRemoved(String iface) {
- maybeLog("interfaceRemoved", iface);
- if (mInterfaceName.equals(iface)) {
- // Our interface was removed. Clear our LinkProperties and tell our owner that they are
- // now empty. Note that from the moment that the interface is removed, any further
- // interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd
- // code that parses them will not be able to resolve the ifindex to an interface name.
- clearLinkProperties();
- mCallback.update();
- }
- }
-
- @Override
- public void onInterfaceAddressUpdated(LinkAddress address, String iface) {
- if (mInterfaceName.equals(iface)) {
- maybeLog("addressUpdated", iface, address);
- boolean changed;
- synchronized (this) {
- changed = mLinkProperties.addLinkAddress(address);
- }
- if (changed) {
- mCallback.update();
- }
- }
- }
-
- @Override
- public void onInterfaceAddressRemoved(LinkAddress address, String iface) {
- if (mInterfaceName.equals(iface)) {
- maybeLog("addressRemoved", iface, address);
- boolean changed;
- synchronized (this) {
- changed = mLinkProperties.removeLinkAddress(address);
- }
- if (changed) {
- mCallback.update();
- }
- }
- }
-
- @Override
- public void onRouteUpdated(RouteInfo route) {
- if (mInterfaceName.equals(route.getInterface())) {
- maybeLog("routeUpdated", route);
- boolean changed;
- synchronized (this) {
- changed = mLinkProperties.addRoute(route);
- }
- if (changed) {
- mCallback.update();
- }
- }
- }
-
- @Override
- public void onRouteRemoved(RouteInfo route) {
- if (mInterfaceName.equals(route.getInterface())) {
- maybeLog("routeRemoved", route);
- boolean changed;
- synchronized (this) {
- changed = mLinkProperties.removeRoute(route);
- }
- if (changed) {
- mCallback.update();
- }
- }
- }
-
- @Override
- public void onInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) {
- if (mInterfaceName.equals(iface)) {
- maybeLog("interfaceDnsServerInfo", Arrays.toString(addresses));
- boolean changed = mDnsServerRepository.addServers(lifetime, addresses);
- if (changed) {
- synchronized (this) {
- mDnsServerRepository.setDnsServersOn(mLinkProperties);
- }
- mCallback.update();
- }
- }
- }
-
- /**
- * Returns a copy of this object's LinkProperties.
- */
- public synchronized LinkProperties getLinkProperties() {
- return new LinkProperties(mLinkProperties);
- }
-
- /**
- * Reset this object's LinkProperties.
- */
- public synchronized void clearLinkProperties() {
- // Clear the repository before clearing mLinkProperties. That way, if a clear() happens
- // while interfaceDnsServerInfo() is being called, we'll end up with no DNS servers in
- // mLinkProperties, as desired.
- mDnsServerRepository = new DnsServerRepository();
- mLinkProperties.clear();
- mLinkProperties.setInterfaceName(mInterfaceName);
- }
-
- /**
- * Tracks DNS server updates received from Netlink.
- *
- * The network may announce an arbitrary number of DNS servers in Router Advertisements at any
- * time. Each announcement has a lifetime; when the lifetime expires, the servers should not be
- * used any more. In this way, the network can gracefully migrate clients from one set of DNS
- * servers to another. Announcements can both raise and lower the lifetime, and an announcement
- * can expire servers by announcing them with a lifetime of zero.
- *
- * Typically the system will only use a small number (2 or 3; {@code NUM_CURRENT_SERVERS}) of
- * DNS servers at any given time. These are referred to as the current servers. In case all the
- * current servers expire, the class also keeps track of a larger (but limited) number of
- * servers that are promoted to current servers when the current ones expire. In order to
- * minimize updates to the rest of the system (and potentially expensive cache flushes) this
- * class attempts to keep the list of current servers constant where possible. More
- * specifically, the list of current servers is only updated if a new server is learned and
- * there are not yet {@code NUM_CURRENT_SERVERS} current servers, or if one or more of the
- * current servers expires or is pushed out of the set. Therefore, the current servers will not
- * necessarily be the ones with the highest lifetime, but the ones learned first.
- *
- * This is by design: if instead the class always preferred the servers with the highest
- * lifetime, a (misconfigured?) network where two or more routers announce more than
- * {@code NUM_CURRENT_SERVERS} unique servers would cause persistent oscillations.
- *
- * TODO: Currently servers are only expired when a new DNS update is received.
- * Update them using timers, or possibly on every notification received by NetlinkTracker.
- *
- * Threading model: run by NetlinkTracker. Methods are synchronized(this) just in case netlink
- * notifications are sent by multiple threads. If future threads use alarms to expire, those
- * alarms must also be synchronized(this).
- *
- */
- private static class DnsServerRepository {
-
- /** How many DNS servers we will use. 3 is suggested by RFC 6106. */
- static final int NUM_CURRENT_SERVERS = 3;
-
- /** How many DNS servers we'll keep track of, in total. */
- static final int NUM_SERVERS = 12;
-
- /** Stores up to {@code NUM_CURRENT_SERVERS} DNS servers we're currently using. */
- private Set mCurrentServers;
-
- public static final String TAG = "DnsServerRepository";
-
- /**
- * Stores all the DNS servers we know about, for use when the current servers expire.
- * Always sorted in order of decreasing expiry. The elements in this list are also the
- * values of mIndex, and may be elements in mCurrentServers.
- */
- private ArrayList mAllServers;
-
- /**
- * Indexes the servers so we can update their lifetimes more quickly in the common case
- * where servers are not being added, but only being refreshed.
- */
- private HashMap mIndex;
-
- DnsServerRepository() {
- mCurrentServers = new HashSet<>();
- mAllServers = new ArrayList<>(NUM_SERVERS);
- mIndex = new HashMap<>(NUM_SERVERS);
- }
-
- /** Sets the DNS servers of the provided LinkProperties object to the current servers. */
- public synchronized void setDnsServersOn(LinkProperties lp) {
- lp.setDnsServers(mCurrentServers);
- }
-
- /**
- * Notifies the class of new DNS server information.
- * @param lifetime the time in seconds that the DNS servers are valid.
- * @param addresses the string representations of the IP addresses of DNS servers to use.
- */
- public synchronized boolean addServers(long lifetime, String[] addresses) {
- // The lifetime is actually an unsigned 32-bit number, but Java doesn't have unsigned.
- // Technically 0xffffffff (the maximum) is special and means "forever", but 2^32 seconds
- // (136 years) is close enough.
- long now = System.currentTimeMillis();
- long expiry = now + 1000 * lifetime;
-
- // Go through the list of servers. For each one, update the entry if one exists, and
- // create one if it doesn't.
- for (String addressString : addresses) {
- InetAddress address;
- try {
- address = InetAddresses.parseNumericAddress(addressString);
- } catch (IllegalArgumentException ex) {
- continue;
- }
-
- if (!updateExistingEntry(address, expiry)) {
- // There was no entry for this server. Create one, unless it's already expired
- // (i.e., if the lifetime is zero; it cannot be < 0 because it's unsigned).
- if (expiry > now) {
- DnsServerEntry entry = new DnsServerEntry(address, expiry);
- mAllServers.add(entry);
- mIndex.put(address, entry);
- }
- }
- }
-
- // Sort the servers by expiry.
- Collections.sort(mAllServers);
-
- // Prune excess entries and update the current server list.
- return updateCurrentServers();
- }
-
- private synchronized boolean updateExistingEntry(InetAddress address, long expiry) {
- DnsServerEntry existing = mIndex.get(address);
- if (existing != null) {
- existing.expiry = expiry;
- return true;
- }
- return false;
- }
-
- private synchronized boolean updateCurrentServers() {
- long now = System.currentTimeMillis();
- boolean changed = false;
-
- // Prune excess or expired entries.
- for (int i = mAllServers.size() - 1; i >= 0; i--) {
- if (i >= NUM_SERVERS || mAllServers.get(i).expiry < now) {
- DnsServerEntry removed = mAllServers.remove(i);
- mIndex.remove(removed.address);
- changed |= mCurrentServers.remove(removed.address);
- } else {
- break;
- }
- }
-
- // Add servers to the current set, in order of decreasing lifetime, until it has enough.
- // Prefer existing servers over new servers in order to minimize updates to the rest of
- // the system and avoid persistent oscillations.
- for (DnsServerEntry entry : mAllServers) {
- if (mCurrentServers.size() < NUM_CURRENT_SERVERS) {
- changed |= mCurrentServers.add(entry.address);
- } else {
- break;
- }
- }
- return changed;
- }
- }
-
- /**
- * Represents a DNS server entry with an expiry time.
- *
- * Implements Comparable so DNS server entries can be sorted by lifetime, longest-lived first.
- * The ordering of entries with the same lifetime is unspecified, because given two servers with
- * identical lifetimes, we don't care which one we use, and only comparing the lifetime is much
- * faster than comparing the IP address as well.
- *
- * Note: this class has a natural ordering that is inconsistent with equals.
- */
- private static class DnsServerEntry implements Comparable {
- /** The IP address of the DNS server. */
- public final InetAddress address;
- /** The time until which the DNS server may be used. A Java millisecond time as might be
- * returned by currentTimeMillis(). */
- public long expiry;
-
- DnsServerEntry(InetAddress address, long expiry) throws IllegalArgumentException {
- this.address = address;
- this.expiry = expiry;
- }
-
- public int compareTo(DnsServerEntry other) {
- return Long.compare(other.expiry, this.expiry);
- }
- }
-}
diff --git a/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java b/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java
deleted file mode 100644
index 6ae9a2b7f19bc..0000000000000
--- a/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java
+++ /dev/null
@@ -1,247 +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.ip;
-
-import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
-import static android.net.netlink.NetlinkConstants.hexify;
-import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
-import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
-import static android.system.OsConstants.AF_NETLINK;
-import static android.system.OsConstants.NETLINK_ROUTE;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_NONBLOCK;
-
-import android.net.MacAddress;
-import android.net.netlink.NetlinkErrorMessage;
-import android.net.netlink.NetlinkMessage;
-import android.net.netlink.NetlinkSocket;
-import android.net.netlink.RtNetlinkNeighborMessage;
-import android.net.netlink.StructNdMsg;
-import android.net.util.NetworkStackUtils;
-import android.net.util.PacketReader;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.net.InetAddress;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.StringJoiner;
-
-
-/**
- * IpNeighborMonitor.
- *
- * Monitors the kernel rtnetlink neighbor notifications and presents to callers
- * NeighborEvents describing each event. Callers can provide a consumer instance
- * to both filter (e.g. by interface index and IP address) and handle the
- * generated NeighborEvents.
- *
- * @hide
- */
-public class IpNeighborMonitor extends PacketReader {
- private static final String TAG = IpNeighborMonitor.class.getSimpleName();
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
-
- /**
- * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
- * for the given IP address on the specified interface index.
- *
- * @return 0 if the request was successfully passed to the kernel; otherwise return
- * a non-zero error code.
- */
- public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) {
- final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
- if (DBG) { Log.d(TAG, msgSnippet); }
-
- final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
- 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
-
- try {
- NetlinkSocket.sendOneShotKernelMessage(NETLINK_ROUTE, msg);
- } catch (ErrnoException e) {
- Log.e(TAG, "Error " + msgSnippet + ": " + e);
- return -e.errno;
- }
-
- return 0;
- }
-
- public static class NeighborEvent {
- final long elapsedMs;
- final short msgType;
- final int ifindex;
- final InetAddress ip;
- final short nudState;
- final MacAddress macAddr;
-
- public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
- short nudState, MacAddress macAddr) {
- this.elapsedMs = elapsedMs;
- this.msgType = msgType;
- this.ifindex = ifindex;
- this.ip = ip;
- this.nudState = nudState;
- this.macAddr = macAddr;
- }
-
- boolean isConnected() {
- return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
- }
-
- boolean isValid() {
- return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
- }
-
- @Override
- public String toString() {
- final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
- return j.add("@" + elapsedMs)
- .add(stringForNlMsgType(msgType))
- .add("if=" + ifindex)
- .add(ip.getHostAddress())
- .add(StructNdMsg.stringForNudState(nudState))
- .add("[" + macAddr + "]")
- .toString();
- }
- }
-
- public interface NeighborEventConsumer {
- // Every neighbor event received on the netlink socket is passed in
- // here. Subclasses should filter for events of interest.
- public void accept(NeighborEvent event);
- }
-
- private final SharedLog mLog;
- private final NeighborEventConsumer mConsumer;
-
- public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
- super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
- mLog = log.forSubComponent(TAG);
- mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
- }
-
- @Override
- protected FileDescriptor createFd() {
- FileDescriptor fd = null;
-
- try {
- fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE);
- Os.bind(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH));
- NetlinkSocket.connectToKernel(fd);
-
- if (VDBG) {
- final SocketAddress nlAddr = Os.getsockname(fd);
- Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
- }
- } catch (ErrnoException|SocketException e) {
- logError("Failed to create rtnetlink socket", e);
- NetworkStackUtils.closeSocketQuietly(fd);
- return null;
- }
-
- return fd;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- final long whenMs = SystemClock.elapsedRealtime();
-
- final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
- byteBuffer.order(ByteOrder.nativeOrder());
-
- parseNetlinkMessageBuffer(byteBuffer, whenMs);
- }
-
- private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
- while (byteBuffer.remaining() > 0) {
- final int position = byteBuffer.position();
- final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
- if (nlMsg == null || nlMsg.getHeader() == null) {
- byteBuffer.position(position);
- mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
- break;
- }
-
- final int srcPortId = nlMsg.getHeader().nlmsg_pid;
- if (srcPortId != 0) {
- mLog.e("non-kernel source portId: " + Integer.toUnsignedLong(srcPortId));
- break;
- }
-
- if (nlMsg instanceof NetlinkErrorMessage) {
- mLog.e("netlink error: " + nlMsg);
- continue;
- } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
- mLog.i("non-rtnetlink neighbor msg: " + nlMsg);
- continue;
- }
-
- evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
- }
- }
-
- private void evaluateRtNetlinkNeighborMessage(
- RtNetlinkNeighborMessage neighMsg, long whenMs) {
- final short msgType = neighMsg.getHeader().nlmsg_type;
- final StructNdMsg ndMsg = neighMsg.getNdHeader();
- if (ndMsg == null) {
- mLog.e("RtNetlinkNeighborMessage without ND message header!");
- return;
- }
-
- final int ifindex = ndMsg.ndm_ifindex;
- final InetAddress destination = neighMsg.getDestination();
- final short nudState =
- (msgType == RTM_DELNEIGH)
- ? StructNdMsg.NUD_NONE
- : ndMsg.ndm_state;
-
- final NeighborEvent event = new NeighborEvent(
- whenMs, msgType, ifindex, destination, nudState,
- getMacAddress(neighMsg.getLinkLayerAddress()));
-
- if (VDBG) {
- Log.d(TAG, neighMsg.toString());
- }
- if (DBG) {
- Log.d(TAG, event.toString());
- }
-
- mConsumer.accept(event);
- }
-
- private static MacAddress getMacAddress(byte[] linkLayerAddress) {
- if (linkLayerAddress != null) {
- try {
- return MacAddress.fromBytes(linkLayerAddress);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
- }
- }
-
- return null;
- }
-}
diff --git a/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java b/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java
deleted file mode 100644
index c19a24eb7fb67..0000000000000
--- a/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2015 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.metrics.IpReachabilityEvent.NUD_FAILED;
-import static android.net.metrics.IpReachabilityEvent.NUD_FAILED_ORGANIC;
-import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST;
-import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST_ORGANIC;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.RouteInfo;
-import android.net.ip.IpNeighborMonitor.NeighborEvent;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.IpReachabilityEvent;
-import android.net.netlink.StructNdMsg;
-import android.net.util.InterfaceParams;
-import android.net.util.SharedLog;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.PrintWriter;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * IpReachabilityMonitor.
- *
- * Monitors on-link IP reachability and notifies callers whenever any on-link
- * addresses of interest appear to have become unresponsive.
- *
- * This code does not concern itself with "why" a neighbour might have become
- * unreachable. Instead, it primarily reacts to the kernel's notion of IP
- * reachability for each of the neighbours we know to be critically important
- * to normal network connectivity. As such, it is often "just the messenger":
- * the neighbours about which it warns are already deemed by the kernel to have
- * become unreachable.
- *
- *
- * How it works:
- *
- * 1. The "on-link neighbours of interest" found in a given LinkProperties
- * instance are added to a "watch list" via #updateLinkProperties().
- * This usually means all default gateways and any on-link DNS servers.
- *
- * 2. We listen continuously for netlink neighbour messages (RTM_NEWNEIGH,
- * RTM_DELNEIGH), watching only for neighbours in the watch list.
- *
- * - A neighbour going into NUD_REACHABLE, NUD_STALE, NUD_DELAY, and
- * even NUD_PROBE is perfectly normal; we merely record the new state.
- *
- * - A neighbour's entry may be deleted (RTM_DELNEIGH), for example due
- * to garbage collection. This is not necessarily of immediate
- * concern; we record the neighbour as moving to NUD_NONE.
- *
- * - A neighbour transitioning to NUD_FAILED (for any reason) is
- * critically important and is handled as described below in #4.
- *
- * 3. All on-link neighbours in the watch list can be forcibly "probed" by
- * calling #probeAll(). This should be called whenever it is important to
- * verify that critical neighbours on the link are still reachable, e.g.
- * when roaming between BSSIDs.
- *
- * - The kernel will send unicast ARP requests for IPv4 neighbours and
- * unicast NS packets for IPv6 neighbours. The expected replies will
- * likely be unicast.
- *
- * - The forced probing is done holding a wakelock. The kernel may,
- * however, initiate probing of a neighbor on its own, i.e. whenever
- * a neighbour has expired from NUD_DELAY.
- *
- * - The kernel sends:
- *
- * /proc/sys/net/ipv{4,6}/neigh//ucast_solicit
- *
- * number of probes (usually 3) every:
- *
- * /proc/sys/net/ipv{4,6}/neigh//retrans_time_ms
- *
- * number of milliseconds (usually 1000ms). This normally results in
- * 3 unicast packets, 1 per second.
- *
- * - If no response is received to any of the probe packets, the kernel
- * marks the neighbour as being in state NUD_FAILED, and the listening
- * process in #2 will learn of it.
- *
- * 4. We call the supplied Callback#notifyLost() function if the loss of a
- * neighbour in NUD_FAILED would cause IPv4 or IPv6 configuration to
- * become incomplete (a loss of provisioning).
- *
- * - For example, losing all our IPv4 on-link DNS servers (or losing
- * our only IPv6 default gateway) constitutes a loss of IPv4 (IPv6)
- * provisioning; Callback#notifyLost() would be called.
- *
- * - Since it can be non-trivial to reacquire certain IP provisioning
- * state it may be best for the link to disconnect completely and
- * reconnect afresh.
- *
- * Accessing an instance of this class from multiple threads is NOT safe.
- *
- * @hide
- */
-public class IpReachabilityMonitor {
- private static final String TAG = "IpReachabilityMonitor";
- private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
-
- public interface Callback {
- // This callback function must execute as quickly as possible as it is
- // run on the same thread that listens to kernel neighbor updates.
- //
- // TODO: refactor to something like notifyProvisioningLost(String msg).
- public void notifyLost(InetAddress ip, String logMsg);
- }
-
- /**
- * Encapsulates IpReachabilityMonitor depencencies on systems that hinder unit testing.
- * TODO: consider also wrapping MultinetworkPolicyTracker in this interface.
- */
- interface Dependencies {
- void acquireWakeLock(long durationMs);
-
- static Dependencies makeDefault(Context context, String iface) {
- final String lockName = TAG + "." + iface;
- final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- final WakeLock lock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockName);
-
- return new Dependencies() {
- public void acquireWakeLock(long durationMs) {
- lock.acquire(durationMs);
- }
- };
- }
- }
-
- private final InterfaceParams mInterfaceParams;
- private final IpNeighborMonitor mIpNeighborMonitor;
- private final SharedLog mLog;
- private final Callback mCallback;
- private final Dependencies mDependencies;
- private final boolean mUsingMultinetworkPolicyTracker;
- private final ConnectivityManager mCm;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
- private LinkProperties mLinkProperties = new LinkProperties();
- private Map mNeighborWatchList = new HashMap<>();
- // Time in milliseconds of the last forced probe request.
- private volatile long mLastProbeTimeMs;
-
- public IpReachabilityMonitor(
- Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
- boolean usingMultinetworkPolicyTracker) {
- this(context, ifParams, h, log, callback, usingMultinetworkPolicyTracker,
- Dependencies.makeDefault(context, ifParams.name));
- }
-
- @VisibleForTesting
- IpReachabilityMonitor(Context context, InterfaceParams ifParams, Handler h, SharedLog log,
- Callback callback, boolean usingMultinetworkPolicyTracker, Dependencies dependencies) {
- if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
-
- mInterfaceParams = ifParams;
- mLog = log.forSubComponent(TAG);
- mCallback = callback;
- mUsingMultinetworkPolicyTracker = usingMultinetworkPolicyTracker;
- mCm = context.getSystemService(ConnectivityManager.class);
- mDependencies = dependencies;
-
- mIpNeighborMonitor = new IpNeighborMonitor(h, mLog,
- (NeighborEvent event) -> {
- if (mInterfaceParams.index != event.ifindex) return;
- if (!mNeighborWatchList.containsKey(event.ip)) return;
-
- final NeighborEvent prev = mNeighborWatchList.put(event.ip, event);
-
- // TODO: Consider what to do with other states that are not within
- // NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE).
- if (event.nudState == StructNdMsg.NUD_FAILED) {
- mLog.w("ALERT neighbor went from: " + prev + " to: " + event);
- handleNeighborLost(event);
- }
- });
- mIpNeighborMonitor.start();
- }
-
- public void stop() {
- mIpNeighborMonitor.stop();
- clearLinkProperties();
- }
-
- public void dump(PrintWriter pw) {
- if (Looper.myLooper() == mIpNeighborMonitor.getHandler().getLooper()) {
- pw.println(describeWatchList("\n"));
- return;
- }
-
- final ConditionVariable cv = new ConditionVariable(false);
- mIpNeighborMonitor.getHandler().post(() -> {
- pw.println(describeWatchList("\n"));
- cv.open();
- });
-
- if (!cv.block(1000)) {
- pw.println("Timed out waiting for IpReachabilityMonitor dump");
- }
- }
-
- private String describeWatchList() { return describeWatchList(" "); }
-
- private String describeWatchList(String sep) {
- final StringBuilder sb = new StringBuilder();
- sb.append("iface{" + mInterfaceParams + "}," + sep);
- sb.append("ntable=[" + sep);
- String delimiter = "";
- for (Map.Entry entry : mNeighborWatchList.entrySet()) {
- sb.append(delimiter).append(entry.getKey().getHostAddress() + "/" + entry.getValue());
- delimiter = "," + sep;
- }
- sb.append("]");
- return sb.toString();
- }
-
- private static boolean isOnLink(List routes, InetAddress ip) {
- for (RouteInfo route : routes) {
- if (!route.hasGateway() && route.matches(ip)) {
- return true;
- }
- }
- return false;
- }
-
- public void updateLinkProperties(LinkProperties lp) {
- if (!mInterfaceParams.name.equals(lp.getInterfaceName())) {
- // TODO: figure out whether / how to cope with interface changes.
- Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() +
- "' does not match: " + mInterfaceParams.name);
- return;
- }
-
- mLinkProperties = new LinkProperties(lp);
- Map newNeighborWatchList = new HashMap<>();
-
- final List routes = mLinkProperties.getRoutes();
- for (RouteInfo route : routes) {
- if (route.hasGateway()) {
- InetAddress gw = route.getGateway();
- if (isOnLink(routes, gw)) {
- newNeighborWatchList.put(gw, mNeighborWatchList.getOrDefault(gw, null));
- }
- }
- }
-
- for (InetAddress dns : lp.getDnsServers()) {
- if (isOnLink(routes, dns)) {
- newNeighborWatchList.put(dns, mNeighborWatchList.getOrDefault(dns, null));
- }
- }
-
- mNeighborWatchList = newNeighborWatchList;
- if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); }
- }
-
- public void clearLinkProperties() {
- mLinkProperties.clear();
- mNeighborWatchList.clear();
- if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); }
- }
-
- private void handleNeighborLost(NeighborEvent event) {
- final LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
-
- InetAddress ip = null;
- for (Map.Entry entry : mNeighborWatchList.entrySet()) {
- // TODO: Consider using NeighborEvent#isValid() here; it's more
- // strict but may interact badly if other entries are somehow in
- // NUD_INCOMPLETE (say, during network attach).
- if (entry.getValue().nudState != StructNdMsg.NUD_FAILED) continue;
-
- ip = entry.getKey();
- for (RouteInfo route : mLinkProperties.getRoutes()) {
- if (ip.equals(route.getGateway())) {
- whatIfLp.removeRoute(route);
- }
- }
-
- if (avoidingBadLinks() || !(ip instanceof Inet6Address)) {
- // We should do this unconditionally, but alas we cannot: b/31827713.
- whatIfLp.removeDnsServer(ip);
- }
- }
-
- final boolean lostProvisioning =
- (mLinkProperties.isIpv4Provisioned() && !whatIfLp.isIpv4Provisioned())
- || (mLinkProperties.isIpv6Provisioned() && !whatIfLp.isIpv6Provisioned());
-
- if (lostProvisioning) {
- final String logMsg = "FAILURE: LOST_PROVISIONING, " + event;
- Log.w(TAG, logMsg);
- if (mCallback != null) {
- // TODO: remove |ip| when the callback signature no longer has
- // an InetAddress argument.
- mCallback.notifyLost(ip, logMsg);
- }
- }
- logNudFailed(lostProvisioning);
- }
-
- private boolean avoidingBadLinks() {
- return !mUsingMultinetworkPolicyTracker || mCm.shouldAvoidBadWifi();
- }
-
- public void probeAll() {
- final List ipProbeList = new ArrayList<>(mNeighborWatchList.keySet());
-
- if (!ipProbeList.isEmpty()) {
- // Keep the CPU awake long enough to allow all ARP/ND
- // probes a reasonable chance at success. See b/23197666.
- //
- // The wakelock we use is (by default) refcounted, and this version
- // of acquire(timeout) queues a release message to keep acquisitions
- // and releases balanced.
- mDependencies.acquireWakeLock(getProbeWakeLockDuration());
- }
-
- for (InetAddress ip : ipProbeList) {
- final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceParams.index, ip);
- mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
- ip.getHostAddress(), rval));
- logEvent(IpReachabilityEvent.PROBE, rval);
- }
- mLastProbeTimeMs = SystemClock.elapsedRealtime();
- }
-
- private static long getProbeWakeLockDuration() {
- // Ideally, this would be computed by examining the values of:
- //
- // /proc/sys/net/ipv[46]/neigh//ucast_solicit
- //
- // and:
- //
- // /proc/sys/net/ipv[46]/neigh//retrans_time_ms
- //
- // For now, just make some assumptions.
- final long numUnicastProbes = 3;
- final long retransTimeMs = 1000;
- final long gracePeriodMs = 500;
- return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
- }
-
- private void logEvent(int probeType, int errorCode) {
- int eventType = probeType | (errorCode & 0xff);
- mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
- }
-
- private void logNudFailed(boolean lostProvisioning) {
- long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs;
- boolean isFromProbe = (duration < getProbeWakeLockDuration());
- int eventType = nudFailureEventType(isFromProbe, lostProvisioning);
- mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
- }
-
- /**
- * Returns the NUD failure event type code corresponding to the given conditions.
- */
- private static int nudFailureEventType(boolean isFromProbe, boolean isProvisioningLost) {
- if (isFromProbe) {
- return isProvisioningLost ? PROVISIONING_LOST : NUD_FAILED;
- } else {
- return isProvisioningLost ? PROVISIONING_LOST_ORGANIC : NUD_FAILED_ORGANIC;
- }
- }
-}
diff --git a/packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java b/packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java
deleted file mode 100644
index 08c3f60eff3d6..0000000000000
--- a/packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java
+++ /dev/null
@@ -1,435 +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.util;
-
-import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.IPPROTO_UDP;
-
-import static com.android.server.util.NetworkStackConstants.ARP_HWTYPE_ETHER;
-import static com.android.server.util.NetworkStackConstants.ARP_PAYLOAD_LEN;
-import static com.android.server.util.NetworkStackConstants.ARP_REPLY;
-import static com.android.server.util.NetworkStackConstants.ARP_REQUEST;
-import static com.android.server.util.NetworkStackConstants.DHCP4_CLIENT_PORT;
-import static com.android.server.util.NetworkStackConstants.ETHER_ADDR_LEN;
-import static com.android.server.util.NetworkStackConstants.ETHER_DST_ADDR_OFFSET;
-import static com.android.server.util.NetworkStackConstants.ETHER_HEADER_LEN;
-import static com.android.server.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET;
-import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_ARP;
-import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV4;
-import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV6;
-import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_OFFSET;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_HEADER_MIN_LEN;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_MIN_LENGTH;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_MTU;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_LEN;
-import static com.android.server.util.NetworkStackConstants.IPV4_DST_ADDR_OFFSET;
-import static com.android.server.util.NetworkStackConstants.IPV4_FLAGS_OFFSET;
-import static com.android.server.util.NetworkStackConstants.IPV4_FRAGMENT_MASK;
-import static com.android.server.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
-import static com.android.server.util.NetworkStackConstants.IPV4_IHL_MASK;
-import static com.android.server.util.NetworkStackConstants.IPV4_PROTOCOL_OFFSET;
-import static com.android.server.util.NetworkStackConstants.IPV4_SRC_ADDR_OFFSET;
-import static com.android.server.util.NetworkStackConstants.IPV6_ADDR_LEN;
-import static com.android.server.util.NetworkStackConstants.IPV6_HEADER_LEN;
-import static com.android.server.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET;
-import static com.android.server.util.NetworkStackConstants.IPV6_SRC_ADDR_OFFSET;
-import static com.android.server.util.NetworkStackConstants.UDP_HEADER_LEN;
-
-import android.net.MacAddress;
-import android.net.dhcp.DhcpPacket;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.StringJoiner;
-
-
-/**
- * Critical connectivity packet summarizing class.
- *
- * Outputs short descriptions of ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets.
- *
- * @hide
- */
-public class ConnectivityPacketSummary {
- private static final String TAG = ConnectivityPacketSummary.class.getSimpleName();
-
- private final byte[] mHwAddr;
- private final byte[] mBytes;
- private final int mLength;
- private final ByteBuffer mPacket;
- private final String mSummary;
-
- public static String summarize(MacAddress hwaddr, byte[] buffer) {
- return summarize(hwaddr, buffer, buffer.length);
- }
-
- // Methods called herein perform some but by no means all error checking.
- // They may throw runtime exceptions on malformed packets.
- public static String summarize(MacAddress macAddr, byte[] buffer, int length) {
- if ((macAddr == null) || (buffer == null)) return null;
- length = Math.min(length, buffer.length);
- return (new ConnectivityPacketSummary(macAddr, buffer, length)).toString();
- }
-
- private ConnectivityPacketSummary(MacAddress macAddr, byte[] buffer, int length) {
- mHwAddr = macAddr.toByteArray();
- mBytes = buffer;
- mLength = Math.min(length, mBytes.length);
- mPacket = ByteBuffer.wrap(mBytes, 0, mLength);
- mPacket.order(ByteOrder.BIG_ENDIAN);
-
- final StringJoiner sj = new StringJoiner(" ");
- // TODO: support other link-layers, or even no link-layer header.
- parseEther(sj);
- mSummary = sj.toString();
- }
-
- public String toString() {
- return mSummary;
- }
-
- private void parseEther(StringJoiner sj) {
- if (mPacket.remaining() < ETHER_HEADER_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(ETHER_SRC_ADDR_OFFSET);
- final ByteBuffer srcMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
- sj.add(ByteBuffer.wrap(mHwAddr).equals(srcMac) ? "TX" : "RX");
- sj.add(getMacAddressString(srcMac));
-
- mPacket.position(ETHER_DST_ADDR_OFFSET);
- final ByteBuffer dstMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
- sj.add(">").add(getMacAddressString(dstMac));
-
- mPacket.position(ETHER_TYPE_OFFSET);
- final int etherType = asUint(mPacket.getShort());
- switch (etherType) {
- case ETHER_TYPE_ARP:
- sj.add("arp");
- parseARP(sj);
- break;
- case ETHER_TYPE_IPV4:
- sj.add("ipv4");
- parseIPv4(sj);
- break;
- case ETHER_TYPE_IPV6:
- sj.add("ipv6");
- parseIPv6(sj);
- break;
- default:
- // Unknown ether type.
- sj.add("ethtype").add(asString(etherType));
- break;
- }
- }
-
- private void parseARP(StringJoiner sj) {
- if (mPacket.remaining() < ARP_PAYLOAD_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- if (asUint(mPacket.getShort()) != ARP_HWTYPE_ETHER ||
- asUint(mPacket.getShort()) != ETHER_TYPE_IPV4 ||
- asUint(mPacket.get()) != ETHER_ADDR_LEN ||
- asUint(mPacket.get()) != IPV4_ADDR_LEN) {
- sj.add("unexpected header");
- return;
- }
-
- final int opCode = asUint(mPacket.getShort());
-
- final String senderHwAddr = getMacAddressString(mPacket);
- final String senderIPv4 = getIPv4AddressString(mPacket);
- getMacAddressString(mPacket); // target hardware address, unused
- final String targetIPv4 = getIPv4AddressString(mPacket);
-
- if (opCode == ARP_REQUEST) {
- sj.add("who-has").add(targetIPv4);
- } else if (opCode == ARP_REPLY) {
- sj.add("reply").add(senderIPv4).add(senderHwAddr);
- } else {
- sj.add("unknown opcode").add(asString(opCode));
- }
- }
-
- private void parseIPv4(StringJoiner sj) {
- if (!mPacket.hasRemaining()) {
- sj.add("runt");
- return;
- }
-
- final int startOfIpLayer = mPacket.position();
- final int ipv4HeaderLength = (mPacket.get(startOfIpLayer) & IPV4_IHL_MASK) * 4;
- if (mPacket.remaining() < ipv4HeaderLength ||
- mPacket.remaining() < IPV4_HEADER_MIN_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
- final int startOfTransportLayer = startOfIpLayer + ipv4HeaderLength;
-
- mPacket.position(startOfIpLayer + IPV4_FLAGS_OFFSET);
- final int flagsAndFragment = asUint(mPacket.getShort());
- final boolean isFragment = (flagsAndFragment & IPV4_FRAGMENT_MASK) != 0;
-
- mPacket.position(startOfIpLayer + IPV4_PROTOCOL_OFFSET);
- final int protocol = asUint(mPacket.get());
-
- mPacket.position(startOfIpLayer + IPV4_SRC_ADDR_OFFSET);
- final String srcAddr = getIPv4AddressString(mPacket);
-
- mPacket.position(startOfIpLayer + IPV4_DST_ADDR_OFFSET);
- final String dstAddr = getIPv4AddressString(mPacket);
-
- sj.add(srcAddr).add(">").add(dstAddr);
-
- mPacket.position(startOfTransportLayer);
- if (protocol == IPPROTO_UDP) {
- sj.add("udp");
- if (isFragment) sj.add("fragment");
- else parseUDP(sj);
- } else {
- sj.add("proto").add(asString(protocol));
- if (isFragment) sj.add("fragment");
- }
- }
-
- private void parseIPv6(StringJoiner sj) {
- if (mPacket.remaining() < IPV6_HEADER_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- final int startOfIpLayer = mPacket.position();
-
- mPacket.position(startOfIpLayer + IPV6_PROTOCOL_OFFSET);
- final int protocol = asUint(mPacket.get());
-
- mPacket.position(startOfIpLayer + IPV6_SRC_ADDR_OFFSET);
- final String srcAddr = getIPv6AddressString(mPacket);
- final String dstAddr = getIPv6AddressString(mPacket);
-
- sj.add(srcAddr).add(">").add(dstAddr);
-
- mPacket.position(startOfIpLayer + IPV6_HEADER_LEN);
- if (protocol == IPPROTO_ICMPV6) {
- sj.add("icmp6");
- parseICMPv6(sj);
- } else {
- sj.add("proto").add(asString(protocol));
- }
- }
-
- private void parseICMPv6(StringJoiner sj) {
- if (mPacket.remaining() < ICMPV6_HEADER_MIN_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- final int icmp6Type = asUint(mPacket.get());
- final int icmp6Code = asUint(mPacket.get());
- mPacket.getShort(); // checksum, unused
-
- switch (icmp6Type) {
- case ICMPV6_ROUTER_SOLICITATION:
- sj.add("rs");
- parseICMPv6RouterSolicitation(sj);
- break;
- case ICMPV6_ROUTER_ADVERTISEMENT:
- sj.add("ra");
- parseICMPv6RouterAdvertisement(sj);
- break;
- case ICMPV6_NEIGHBOR_SOLICITATION:
- sj.add("ns");
- parseICMPv6NeighborMessage(sj);
- break;
- case ICMPV6_NEIGHBOR_ADVERTISEMENT:
- sj.add("na");
- parseICMPv6NeighborMessage(sj);
- break;
- default:
- sj.add("type").add(asString(icmp6Type));
- sj.add("code").add(asString(icmp6Code));
- break;
- }
- }
-
- private void parseICMPv6RouterSolicitation(StringJoiner sj) {
- final int RESERVED = 4;
- if (mPacket.remaining() < RESERVED) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(mPacket.position() + RESERVED);
- parseICMPv6NeighborDiscoveryOptions(sj);
- }
-
- private void parseICMPv6RouterAdvertisement(StringJoiner sj) {
- final int FLAGS_AND_TIMERS = 3 * 4;
- if (mPacket.remaining() < FLAGS_AND_TIMERS) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(mPacket.position() + FLAGS_AND_TIMERS);
- parseICMPv6NeighborDiscoveryOptions(sj);
- }
-
- private void parseICMPv6NeighborMessage(StringJoiner sj) {
- final int RESERVED = 4;
- final int minReq = RESERVED + IPV6_ADDR_LEN;
- if (mPacket.remaining() < minReq) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(mPacket.position() + RESERVED);
- sj.add(getIPv6AddressString(mPacket));
- parseICMPv6NeighborDiscoveryOptions(sj);
- }
-
- private void parseICMPv6NeighborDiscoveryOptions(StringJoiner sj) {
- // All ND options are TLV, where T is one byte and L is one byte equal
- // to the length of T + L + V in units of 8 octets.
- while (mPacket.remaining() >= ICMPV6_ND_OPTION_MIN_LENGTH) {
- final int ndType = asUint(mPacket.get());
- final int ndLength = asUint(mPacket.get());
- final int ndBytes = ndLength * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR - 2;
- if (ndBytes < 0 || ndBytes > mPacket.remaining()) {
- sj.add("");
- break;
- }
- final int position = mPacket.position();
-
- switch (ndType) {
- case ICMPV6_ND_OPTION_SLLA:
- sj.add("slla");
- sj.add(getMacAddressString(mPacket));
- break;
- case ICMPV6_ND_OPTION_TLLA:
- sj.add("tlla");
- sj.add(getMacAddressString(mPacket));
- break;
- case ICMPV6_ND_OPTION_MTU:
- sj.add("mtu");
- final short reserved = mPacket.getShort();
- sj.add(asString(mPacket.getInt()));
- break;
- default:
- // Skip.
- break;
- }
-
- mPacket.position(position + ndBytes);
- }
- }
-
- private void parseUDP(StringJoiner sj) {
- if (mPacket.remaining() < UDP_HEADER_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- final int previous = mPacket.position();
- final int srcPort = asUint(mPacket.getShort());
- final int dstPort = asUint(mPacket.getShort());
- sj.add(asString(srcPort)).add(">").add(asString(dstPort));
-
- mPacket.position(previous + UDP_HEADER_LEN);
- if (srcPort == DHCP4_CLIENT_PORT || dstPort == DHCP4_CLIENT_PORT) {
- sj.add("dhcp4");
- parseDHCPv4(sj);
- }
- }
-
- private void parseDHCPv4(StringJoiner sj) {
- final DhcpPacket dhcpPacket;
- try {
- dhcpPacket = DhcpPacket.decodeFullPacket(mBytes, mLength, DhcpPacket.ENCAP_L2);
- sj.add(dhcpPacket.toString());
- } catch (DhcpPacket.ParseException e) {
- sj.add("parse error: " + e);
- }
- }
-
- private static String getIPv4AddressString(ByteBuffer ipv4) {
- return getIpAddressString(ipv4, IPV4_ADDR_LEN);
- }
-
- private static String getIPv6AddressString(ByteBuffer ipv6) {
- return getIpAddressString(ipv6, IPV6_ADDR_LEN);
- }
-
- private static String getIpAddressString(ByteBuffer ip, int byteLength) {
- if (ip == null || ip.remaining() < byteLength) return "invalid";
-
- byte[] bytes = new byte[byteLength];
- ip.get(bytes, 0, byteLength);
- try {
- InetAddress addr = InetAddress.getByAddress(bytes);
- return addr.getHostAddress();
- } catch (UnknownHostException uhe) {
- return "unknown";
- }
- }
-
- private static String getMacAddressString(ByteBuffer mac) {
- if (mac == null || mac.remaining() < ETHER_ADDR_LEN) return "invalid";
-
- byte[] bytes = new byte[ETHER_ADDR_LEN];
- mac.get(bytes, 0, bytes.length);
- Object[] printableBytes = new Object[bytes.length];
- int i = 0;
- for (byte b : bytes) printableBytes[i++] = new Byte(b);
-
- final String MAC48_FORMAT = "%02x:%02x:%02x:%02x:%02x:%02x";
- return String.format(MAC48_FORMAT, printableBytes);
- }
-
- /**
- * Convenience method to convert an int to a String.
- */
- public static String asString(int i) {
- return Integer.toString(i);
- }
-
- /**
- * Convenience method to read a byte as an unsigned int.
- */
- public static int asUint(byte b) {
- return (b & 0xff);
- }
-
- /**
- * Convenience method to read a short as an unsigned int.
- */
- public static int asUint(short s) {
- return (s & 0xffff);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/util/DataStallUtils.java b/packages/NetworkStack/src/android/net/util/DataStallUtils.java
deleted file mode 100644
index b6dbeb19ad1df..0000000000000
--- a/packages/NetworkStack/src/android/net/util/DataStallUtils.java
+++ /dev/null
@@ -1,72 +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;
-
-/**
- * Collection of utilities for data stall.
- */
-public class DataStallUtils {
- /**
- * Detect data stall via using dns timeout counts.
- */
- public static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
- // Default configuration values for data stall detection.
- public static final int DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = 5;
- public static final int DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS = 60 * 1000;
- public static final int DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS = 30 * 60 * 1000;
- /**
- * The threshold value for the number of consecutive dns timeout events received to be a
- * signal of data stall. The number of consecutive timeouts needs to be {@code >=} this
- * threshold to be considered a data stall. Set the value to {@code <= 0} to disable. Note
- * that the value should be {@code > 0} if the DNS data stall detection is enabled.
- *
- */
- public static final String CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD =
- "data_stall_consecutive_dns_timeout_threshold";
-
- /**
- * The minimal time interval in milliseconds for data stall reevaluation.
- *
- */
- public static final String CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL =
- "data_stall_min_evaluate_interval";
-
- /**
- * DNS timeouts older than this timeout (in milliseconds) are not considered for detecting
- * a data stall.
- *
- */
- public static final String CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD =
- "data_stall_valid_dns_time_threshold";
-
- /**
- * Which data stall detection signal to use. This is a bitmask constructed by bitwise-or-ing
- * (i.e. {@code |}) the DATA_STALL_EVALUATION_TYPE_* values.
- *
- * Type: int
- * Valid values:
- * {@link #DATA_STALL_EVALUATION_TYPE_DNS} : Use dns as a signal.
- */
- public static final String CONFIG_DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type";
- public static final int DEFAULT_DATA_STALL_EVALUATION_TYPES = DATA_STALL_EVALUATION_TYPE_DNS;
- // The default number of DNS events kept of the log kept for dns signal evaluation. Each event
- // is represented by a {@link com.android.server.connectivity.NetworkMonitor#DnsResult} objects.
- // It's also the size of array of {@link com.android.server.connectivity.nano.DnsEvent} kept in
- // metrics. Note that increasing the size may cause statsd log buffer bust. Need to check the
- // design in statsd when you try to increase the size.
- public static final int DEFAULT_DNS_LOG_SIZE = 20;
-}
diff --git a/packages/NetworkStack/src/android/net/util/FdEventsReader.java b/packages/NetworkStack/src/android/net/util/FdEventsReader.java
deleted file mode 100644
index 1380ea720d400..0000000000000
--- a/packages/NetworkStack/src/android/net/util/FdEventsReader.java
+++ /dev/null
@@ -1,264 +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.util;
-
-import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
-import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.MessageQueue;
-import android.system.ErrnoException;
-import android.system.OsConstants;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-
-/**
- * This class encapsulates the mechanics of registering a file descriptor
- * with a thread's Looper and handling read events (and errors).
- *
- * Subclasses MUST implement createFd() and SHOULD override handlePacket(). They MAY override
- * onStop() and onStart().
- *
- * Subclasses can expect a call life-cycle like the following:
- *
- * [1] when a client calls start(), createFd() is called, followed by the onStart() hook if all
- * goes well. Implementations may override onStart() for additional initialization.
- *
- * [2] yield, waiting for read event or error notification:
- *
- * [a] readPacket() && handlePacket()
- *
- * [b] if (no error):
- * goto 2
- * else:
- * goto 3
- *
- * [3] when a client calls stop(), the onStop() hook is called (unless already stopped or never
- * started). Implementations may override onStop() for additional cleanup.
- *
- * The packet receive buffer is recycled on every read call, so subclasses
- * should make any copies they would like inside their handlePacket()
- * implementation.
- *
- * All public methods MUST only be called from the same thread with which
- * the Handler constructor argument is associated.
- *
- * @param