Чому ваш Android-застосунок не може додати системний профіль VPN
VpnService будує тунель, яким володіє ваш застосунок; він не може додавати або змінювати записи PPTP, L2TP чи IPsec телефона. Пройдіть межу й оберіть правильний шлях.
Ви натискаєте рядок VPN у Налаштуваннях, бачите профіль, установлений кимось іншим, і починаєте шукати виклик, який його додає. Такого виклику немає. Застосунок може відкрити власний тунель через VpnService, але записи в цьому списку належать платформі та тому клієнту, який їх створив. Точне знання того, де проходить межа, економить тиждень гонитви за прихованими класами.
Почніть з того, що показує екран налаштувань
Список VPN — це реєстр профілів, установлених системою або клієнтом виробника: PPTP, L2TP/IPsec, IKEv2 і рядки, які вштовхнув застосунок OEM чи MDM. Кожен рядок несе адресу сервера, облікові дані та реалізацію протоколу, що живе поза вашим процесом. Коли користувач натискає там «Підключитися», тунель піднімає клієнт платформи, а не ваш код. Тому рядок переживає перезавантаження і тому видалення вашого застосунку ніколи його не прибирає.
З боку користувача список виглядає як поверхня API, місце, де додавання VPN має бути на відстані одного інтенту. Це не так. Єдині публічні точки входу, які його торкаються, — інтенти навігації, а все, що в нього пише, належить платформі або привілейованому компоненту.
Практичний наслідок: якщо текст підтримки каже користувачам додати профіль у Налаштуваннях, ваш застосунок узагалі не є частиною цього сценарію.
Що насправді дає VpnService
VpnService і VpnService.Builder описують один тунель, яким володіє ваш застосунок. Ви додаєте адреси, маршрути, DNS-сервери й MTU, обмежуєте трафік через addAllowedApplication() або addDisallowedApplication(), а потім establish() повертає ParcelFileDescriptor для інтерфейсу TUN. З цього моменту застосунок читає IP-пакети й пише відповіді, тобто протокол — ваш: реалізувати його чи вбудувати: OpenVPN, WireGuard, власний транспорт.
Ніщо в цій послідовності не говорить PPTP, L2TP/IPsec чи IKEv2. У Builder немає перемикача, який сказав би використовувати системний клієнт L2TP, і немає зворотного виклику, що дозволив би керувати профілем, яким платформа вже володіє. Це і є справжня межа, і це межа проєктування, а не прогалина в дозволах.
Тож застосунок цілком може бути VPN-клієнтом на Android. Просто він не може бути менеджером для клієнтів, що постачаються з телефоном.
Діалог згоди — не прикраса
Оскільки ваш тунель може перехоплювати трафік інших застосунків, система вимагає, щоб користувач явно його схвалив. prepare() повертає Intent, який ви зобов'язані запустити, схвалення прив'язане до вашого пакета, а поки тунель активний, у рядку стану горить постійний індикатор. Другий VPN при піднятті деактивує перший, адже Android тримає один активний VPN одночасно.
Це правило одного активного збиває людей у тестуванні. Ви піднімаєте свій тунель, поки підключений корпоративний клієнт, і робите висновок, що ваш код зламано, а платформа просто перемкнулася з іншого клієнта. Схвалення можна відкликати пізніше, і prepare() може повернути null на пристроях, де немає активності згоди, тому establish() усе одно має обробляти збій.
Приховані API та інтенти налаштувань
Старіші відповіді вказують на класи com.android.internal та інтерфейси AIDL, які використовує вбудований клієнт. Їх немає в SDK, вони змінюються між випусками без попередження, а магазини застосунків вважають такий доступ ризиком. Отримання root теж не робить їх стабільними, бо root і доступ до прихованих API — не пов'язані проблеми.
Підтримувана навігаційна підказка — Settings.ACTION_VPN_SETTINGS, чиє значення android.net.vpn.SETTINGS. Розв'яжіть її перед запуском, бо деякі збірки OEM не постачають цю активність, і переходьте до письмових кроків, якщо розв'язання не вдалося. Вона веде користувача до екрана й нічого не налаштовує.
Шляхи власника пристрою та MDM — це той виняток, який зазвичай мають на увазі. DevicePolicyManager може призначити пакет постійного VPN, а керовані конфігурації можуть попередньо завантажити налаштування для клієнта, який їх підтримує, але це все ще не публічний SDK, що пише профіль PPTP.
Пройдіть рішення з боку користувача
Якщо ваш продукт має переносити трафік, плануйте VpnService плюс справжній стек протоколів і закладайте час на сценарій згоди та правило одного VPN. Якщо мета — підключитися до наявного корпоративного профілю, ваша робота — це навігація: глибоке посилання на екран налаштувань, документування полів і надання тунелювання клієнту платформи. Якщо застосунку треба лише дістатися до приватної кінцевої точки, власний тунель йому взагалі не потрібен.
Запишіть це рішення до того, як писати код, бо три шляхи після першого спринту майже нічого не мають спільного.
Остання перевірка перед стартом: у вимозі сказано, що підключається наш застосунок чи що підключається пристрій? Перше — це VpnService, друге — профіль, який ви не можете створити. Правильно сформулювати це речення дешевше за будь-який рефакторинг.