لماذا لا يزال تطبيق أندرويد المحظور يُظهر استخدام الشبكة
عدّاد الاستخدام يقيس البايتات عند حدٍّ واحد، لا نجاح الطلب. تحقّق من النافذة الزمنية، ومن إسناد البيانات إلى UID، ومن التطبيق الذي يملك خدمة VPN.
تحظر تطبيقًا في جدار حماية VPN على أندرويد، فيتوقّف التطبيق عن العمل، ويظل صفّ استخدامه في الارتفاع. الاستنتاج المُغري هو أن الحظر تسرّب، أو أن العدّاد معطوب. لا شيء من ذلك يلزم. سجلّ الاستخدام يقول إن بايتات نُسبت إلى تلك الهوية عند نقطة رصدٍ ما؛ ولا يقول شيئًا عمّا إذا كانت خدمةٌ بعيدة قد قبلت طلبًا. وبمجرّد أن تعرف أي حدٍّ جرى قياسه وأي هويةٍ حُمّلت بها تلك البايتات، يتوقّف الرصدان عن التناقض.
العدّاد يقيس البايتات، لا التسليم
ينسب أندرويد حركة المرور إلى UID ويعدّها في الاتجاهين، ويفعل ذلك عند حدّ الواجهة لا في اللحظة التي ينجح فيها طلب التطبيق.
محاولة الاتصال المرفوضة تحرّك حزمًا مع ذلك: SYN، وإعادة الضبط أو إعادات الإرسال التي تتبعها، إضافةً إلى استعلام DNS الذي سبقهما. تلك البايتات موجودة حتى لو لم يُسلَّم شيء، وفي بعض المسارات تُعدّ قبل أن يقرّر أي مُرشّح ما يفعل بها.
لهذا يمكن أن ينمو صفّ تطبيق محظور بينما يفشل كل طلب. يجيب العدّاد عن سؤال «كم بايتًا نُسب إلى هذه الهوية»، لا عن سؤال «هل وصلت هذه الهوية إلى الإنترنت». لا يستطيع رسم بياني أن يميّز تدفّقًا مقبولًا من مصافحةٍ رُفضت بأدب، ولا يستطيع ذلك مجموعٌ يجمع الاتجاهين معًا.
الإسناد يتبع UID لا الأيقونة
الصفّ لكل UID، وقد يشترك أكثر من حزمة في UID واحد، بينما قد يسلّم تطبيقٌ ظاهر العمل إلى مكوّن منفصل يعمل بهويته الخاصة. تبويب متصفح يرسمه عملية WebView مشتركة، أو خدمة مزامنة في الخلفية، أو حزمة رفيقة مثبّتة إلى جانب التطبيق الرئيسي، كلها تُنتج حركة مرور قد تعرضها شاشة الاستخدام تحت اسم لم تتوقّعه، أو تحت التطبيق الذي حظرته فعلًا. الحزمة المحظورة والمُساعد الذي يحمل طلباتها كيانان مختلفان، وقد غطّت القاعدة واحدًا منهما فقط.
حين تبدو الأرقام خاطئة، قارن UID لا التسمية. تُستعلم واجهة الاستخدام بواسطة UID، والرقم الذي تقارن به يطبعه adb shell dumpsys package <package> | grep userId=، فالإدخالان بالـ UID نفسه هما الهوية نفسها من منظور العدّاد.
قد تسبق النافذة الزمنية قاعدتك
تُجمع بيانات الاستخدام في مجموعات وتُستقصى دوريًا، لا تُبثّ مباشرة. قد يتضمّن المجموع اليومي الدقائق التي سبقت تفعيلك للحظر، أو مزامنة خلفية من الساعة السابقة، أو حركة كانت في الطريق أصلًا. مقارنة مجموع تراكمي بطلب واحد مرفوض وتسمية الفرق تسرّبًا خطأ قياس، لا نتيجة جدار حماية.
استخدم نافذة أقصر وبداية جديدة. سجّل الطابع الزمني عند تغيير القاعدة، وانتظر فاصل جمعٍ واحد، ثم انظر إلى البيانات بعده فقط. إذا استمر المجموع في الارتفاع خلال نافذة تبدأ بعد القاعدة، فلديك ما تحقّق فيه؛ وإذا توقّف عن الارتفاع، فالنموّ السابق كان تاريخًا يُعرض.
قد لا تكون خدمة VPN هي التي أعددتها
جدار حماية VPN لا يرى إلا حركة المرور التي تحملها خدمته هو. إذا بدأ تطبيق VPN آخر واستولى على الاتصال النشط، فمرشّحك لم يعد في المسار بينما واجهته لا تزال موجودة في الإعدادات. قوائم تجاوز التطبيقات تفعل الشيء نفسه عمدًا، والملف الشخصي الذي يستثني تطبيقًا ليس تسرّبًا عندما يعمل التطبيق بعد ذلك.
يمكن أن تتباين IPv4 وIPv6 وDNS أيضًا. المرشّح الذي يتعامل مع IPv4 قد يترك IPv6 أو محلّل DNS خاصًا خارجه، فيتبع جزء من حركة التطبيق مسارًا لم تصفه قواعدك قط. تحقّق من التطبيق الذي يملك VPN حاليًا وممّا تقوله قائمة الاستثناءات قبل أن تعيد كتابة أي شيء:
adb shell settings get secure always_on_vpn_app
adb shell dumpsys connectivity
adb shell dumpsys netstats detail
اختبر طلبًا واحدًا جديدًا وسجّل أربع وقائع
اختر طلبًا غير ضار، ونفّذه مرة واحدة، واكتب أربع وقائع: القاعدة السارية، وUID التطبيق، وحالة الشبكة، والوقت. ثم تحقّق مما إذا كان مرشّحك قد سجّل التدفّق، وما إذا كانت الوجهة أو البوابة قد رأت الاتصال نفسه. تدفّق واحد يُحدَّد على الجانبين دليل؛ أما المجموع الكلي فليس كذلك.
الاستنتاج القابل للدفاع يسمّي ما جرى منعه وأي رصدٍ أكّده. «ارتفع العدّاد» ليس نتيجة، و«كل عدّادات الاستخدام مزيفة» ليست نتيجة أيضًا. شعار طبقة OSI لا يحسم هذا: الإجابة الصادقة أن العدّاد والحظر يمكن أن يكونا صحيحين معًا وهما يصفان شيئين مختلفين. مرجع NetworkStatsManager في أندرويد يوثّق كيف تُستقصى تلك المجموعات وتُنسَب.