Право на приватность и собственность: избавляем приложение Lovence Lush от слежки

Очень часто так случается, что купив какое-то устройство, мы вынуждены установить прилагающийся к нему софт. Это могут быть как драйвера под Windows, так и приложения для мобильных телефонов. И по сути, выбора у конечного потребителя не много: если не установить прилагающийся софт, то устройство просто не будет работать. За него уже уплачены деньги, оно вроде бы находится в собственности, а воспользоваться им без прилагающегося софта нельзя. Так в чем же дело?

Проблема в том, что в прилагающемся софте порой есть много ненужных, а местами даже вредных "добавок". Прилагающийся софт может иметь лицензию, несовместимую со взглядами владельца. Прилагающийся софт может вообще не работать на платформе пользователя. Или требовать от пользователя каких-то специфических действий. И фактически, пользователя заставляют принять все условия. И пользователю некуда деваться. Согласие на обработку персональных данных, согласие на выход приложения в интернет, на доступ к данным GPS, согласие на обработку жены и детей. Немногих, кто еще сомневается в таких вопросах, осмеивают фразами вида "Да кому ты нужен?", или вообще не спрашивают, когда любезный продавец сам поставит все приложения и не спросит, а согласен ли владелец устройства с лицензионным соглашением? Меня в одном известном банке просто попросили отдать им телефон, чтобы они установили туда онлайн-банкинг, даже не спросив моего желания или согласия.

В этой статье я расскажу, как можно отстоять свое право на приватность и собственность, особенно когда речь идет о секс-игрушке Lovence Lush, для работы которой необходим доступ к GPS и подключение к интернету.


Речь далее пойдет о том, как правильно установить приложение Lovence Lush на ваш Android телефон или планшет.

Если посмотреть список пермишшенов (разрешений), то могут возникнуть некоторые вопросы:

android.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_WIFI_STATE
android.permission.BLUETOOTH_ADMIN
android.permission.BLUETOOTH
android.permission.CAMERA
android.permission.CHANGE_CONFIGURATION
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.FLASHLIGHT
android.permission.FOREGROUND_SERVICE
android.permission.INTERNET
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.PREVENT_POWER_KEY
android.permission.READ_EXTERNAL_STORAGE
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.RECEIVE_USER_PRESENT
android.permission.RECORD_AUDIO
android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
android.permission.REQUEST_INSTALL_PACKAGES
android.permission.SYSTEM_ALERT_WINDOW
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_EXTERNAL_STORAGE
com.anddoes.launcher.permission.UPDATE_COUNT
com.google.android.c2dm.permission.RECEIVE
com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE
com.htc.launcher.permission.READ_SETTINGS
com.htc.launcher.permission.UPDATE_SHORTCUT
com.lovense.wear.permission.C2D_MESSAGE
com.majeur.launcher.permission.UPDATE_BADGE
com.sec.android.provider.badge.permission.READ
com.sec.android.provider.badge.permission.WRITE
com.sonyericsson.home.permission.BROADCAST_BADGE
com.wear.permission.C2D_MESSAGE
no.nordicsemi.android.LOG

Итого: приложение хочет снимать видео и писать звук, оно хочет получить доступ к отпечаткам пальцев, к GPS, изменять конфигурацию сетей и еще много чего интересного. Само собой, автозапуск при перезагрузке, чтобы пользователь не сбежал. И конечно же, оно хочет доступ к интернету. Да, часть этих пермишшенов может быть полезна, так как оно требуется для обеспечения работы самого приложения. Но как быть уверенным, что доступ к интернету нужен здесь для работы с аккаунтом, а не для тихого переливания пользовательских данных?

Обычно отломать доступ приложения к интернетам очень просто: достаточно изуродовать AndroidManifest.xml, убрав оттуда все лишнее, в частности android.permission.INTERNET, после чего приложение уже никуда не сможет сконнектиться (но это не точно, если есть GMS/c2dm). Оно может собирать ваши геокоординаты, записывать звуки и иными образами пытаться пожирать батарею, но все, что оно сможет - это молча писать файлики и ждать, когда же появится интернет. Делать это можно чем угодно, начиная от простого hex-редактора, заканчивая специализированными инструментами вроде apktool.

Но бывает и так, что отломав пермишшены, приложение перестает работать, начинает требовать пермишшены и вообще ведет себя как-то странно. У меня вот не получилось просто так взять и все обрезать. Пришлось резать по одному пермишшену/экшену и проверять, работает ли дилдак после этого. Все устаканилось вот на таком списке:


Но стоп! Доступ к интернету тут есть! Какой смысл был во всем этом? Китайцы будут знать мой адрес и даже иметь фоточки того процесса, когда используется их дилдак! А если доступ к интернету убрать, то приложение не может найти лежащий рядом Lush...

Будучи разработчиком под Android я знаю, что для некоторых действий нужны странные пермишшены. Может быть, для поиска Bluetooth-устройств, нужен интернет? Проверим, напишем простую программу:

bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
scanner=bluetoothAdapter.getBluetoothLeScanner();
scanner.startScan(new ScanCallback(){
    
    @Override
    public void onScanResult(int callbackType, ScanResult result){
	scanned.add(result.toString());
	super.onScanResult(callbackType,result);
    }
    
    @Override
    public void onScanFailed(int errorCode){
	scanned.add("failed");
	super.onScanFailed(errorCode);
    }
    
});

scanned.add("scanner: "+scanner);

Здесь мы берем устройство BluetoothAdapter, берем из него BLE-сканнер и запускаем сканирование. Если что-то находим - кладем в массив, если что-то пошло не так - тоже пишем это в массив, ну и под конец пишем в массив сам сканнер. Теперь даем все нужные и не нужные пермишшены, смотрим как это работает. И понимаем, что все отлично сканируется и без доступа к интернету!

Документация же нам говорит, что для работающего сканирования нужно:

void android.bluetooth.le.BluetoothLeScanner.startScan(ScanCallback callback)

Start Bluetooth LE scan with default parameters and no filters. The scan results will be delivered through callback. For unfiltered scans, scanning is stopped on screen off to save power. Scanning is resumed when screen is turned on again. To avoid this, use startScan(List, ScanSettings, ScanCallback) with desired ScanFilter.

An app must hold ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission in order to get results. Requires android.Manifest.permission.BLUETOOTH_ADMIN

Никакого доступа к сети для сканирования - не нужно. Тут же раскрывается еще одна страшная тайна - на самом деле, чтобы вообще найти хоть что-то, приложение должно иметь доступ к GPS - так решили разработчики самого Android. Ну и android.Manifest.permission.BLUETOOTH_ADMIN тоже нужен, чтобы выполнить сканирование.

Так, китайцы уже не такие плохие, запрашивать GPS придумали не они. Но это нас не избавляет от проблемы, ведь с таким пермишшеном, вполне себе можно следить за пользователем, хоть в реалтайме сливая данные. И даже если не само приложение, то используемые им библиотеки. Проблема еще ухудшается тем, что теперь использование такого пермишшена можно оправдать "это надо только для bluetooth", запутав пользователя еще сильнее. Что же будет происходить на самом деле - пользователь так и не узнает.

Делать нечего, распаковываем приложение:

# java -jar apktool.jar d lush.apk

Так как оно не вылетает, а выводит диалог о том, что нехватает прав, то как-то оно их само проверяет. Ищем, где и как оно там обращается к правам:

# grep -r NETWORK_STATE .
./smali_classes2/io/fabric/sdk/android/services/common/CommonUtils.smali:    const-string v0, "android.permission.ACCESS_NETWORK_STATE"
./smali/dc/om0.smali:    const-string v1, "android.permission.ACCESS_NETWORK_STATE"
./smali/dc/om0.smali:    const-string v1, "App is missing ACCESS_NETWORK_STATE permission"
./smali/dc/om0.smali:    const-string v3, "android.permission.ACCESS_NETWORK_STATE"
./smali/dc/n7.smali:    const-string v0, "android.permission.ACCESS_NETWORK_STATE"
./smali/dc/n7.smali:    const-string v1, "ACCESS_NETWORK_STATE permission granted, registering connectivity monitor"
./smali/dc/n7.smali:    const-string v1, "ACCESS_NETWORK_STATE permission missing, cannot register connectivity monitor"
./smali/com/google/firebase/analytics/connector/AnalyticsConnectorImpl.smali:            "android.permission.ACCESS_NETWORK_STATE",
./smali/com/google/firebase/analytics/FirebaseAnalytics.smali:            "android.permission.ACCESS_NETWORK_STATE",
./smali/com/google/android/gms/measurement/module/Analytics.smali:            "android.permission.ACCESS_NETWORK_STATE",
./smali/com/google/android/gms/measurement/AppMeasurement.smali:            "android.permission.ACCESS_NETWORK_STATE",
./smali/androidx/core/net/ConnectivityManagerCompat.smali:        value = "android.permission.ACCESS_NETWORK_STATE"
./smali/androidx/core/net/ConnectivityManagerCompat.smali:        value = "android.permission.ACCESS_NETWORK_STATE"
./AndroidManifest.xml:    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
# grep -r INTERNET .
./smali_classes2/dc/xx0.smali:    const-string v1, "Missing INTERNET permission"
./smali_classes2/dc/xx0.smali:    const-string v2, "android.permission.INTERNET"
./smali_classes2/dc/pc1.smali:    const-string v1, "android.permission.INTERNET"
./smali_classes2/com/wear/phonertc/RequestPermissionActivity.smali:    const-string v2, "android.permission.INTERNET"
./smali_classes2/com/wear/main/account/ProfileActivity.smali:    const-string v1, "android.permission.INTERNET"
./smali_classes2/com/wear/main/account/ChatBackGroudActivity.smali:    const-string v2, "android.permission.INTERNET"
./smali_classes2/com/wear/main/FlashActivity.smali:    const-string v4, "android.permission.INTERNET"
./smali_classes2/com/spotify/sdk/android/player/Config.smali:    const-string v1, "Missing INTERNET permission"
./smali_classes2/com/spotify/sdk/android/player/Config.smali:    const-string v1, "android.permission.INTERNET"
./smali/org/jivesoftware/smackx/vcardtemp/packet/VCard.smali:    const-string v3, "INTERNET"
./smali/dc/om0.smali:    const-string v1, "android.permission.INTERNET"
./smali/dc/om0.smali:    const-string v1, "App is missing INTERNET permission"
./smali/dc/om0.smali:    const-string v1, "android.permission.INTERNET"
./smali/dc/e41.smali:    const-string v2, "android.permission.INTERNET"
./smali/com/wear/main/toy/ToyActivity.smali:    const-string v2, "android.permission.INTERNET"
./smali/com/wear/main/longDistance/FriendProfileActivity.smali:    const-string v2, "android.permission.INTERNET"
./smali/com/wear/main/longDistance/ChatActivity.smali:    const-string v1, "android.permission.INTERNET"
./smali/com/google/firebase/analytics/connector/AnalyticsConnectorImpl.smali:            "android.permission.INTERNET",
./smali/com/google/firebase/analytics/FirebaseAnalytics.smali:            "android.permission.INTERNET",
./smali/com/google/android/gms/measurement/module/Analytics.smali:            "android.permission.INTERNET",
./smali/com/google/android/gms/measurement/AppMeasurement.smali:            "android.permission.INTERNET",
./AndroidManifest.xml:    <uses-permission android:name="android.permission.INTERNET"/>

В принципе, одиночные вызовы можно было бы запатчить самому, но что делать, когда в приложении СТОЛЬКО библиотек и все не только хотят интернет, но еще и считают себя самыми умными, чтобы проверять его наличие? Воевать со всеми? Пожалуй, мы пойдем другим путем.

Как вам следующий диалог?
- У нас есть разрешение на android.permission.INTERNET?
- Да, у вас есть разрешение на android.permission.BLUETOOTH, ты даже BLUETOOTH_ADMIN!

Заменяем вызовы:

# find -iname "*.smali" | xargs -n1 sed -i "s,android.permission.INTERNET,android.permission.BLUETOOTH_ADMIN,g"
# find -iname "*.smali" | xargs -n1 sed -i "s,android.permission.ACCESS_NETWORK_STATE,android.permission.BLUETOOTH_ADMIN,g"

После этого оно уже соглашалось искать игрушку без android.permission.INTERNET... И в принципе, этого уже было бы достаточно, установить связь с кем-либо оно уже не сможет, можно спокойно отправляться в постельку за новыми ощущениями, не боясь, что мой адрес будет в китайской базе. Победа?

Но если начал отламывать пермишшены, то очень сложно остановиться! Если отобрать еще и android.permission.ACCESS_NETWORK_STATE, то приложение просто не стартует, показывая черный экран. А ведь со столь опасным пермишшеном, приложение вполне может продолжать вести свою подрывную работу! К примеру, сканировать сети и складывать результат в файлик! А куда этот файлик потом улетит - никому не известно!

Отламываем и ACCESS_NETWORK_STATE, запускаем приложение... А там черный экран! И вот примерно такая вот отладка в logcat:

---55.184   777   777 D Zygote  : Forked child process 20212
---55.190  1336  1368 I ActivityManager: Start proc 20212:com.lovense.wear/u0a243 for top-activity {com.lovense.wear/com.wear.crash.CrashActivity}
---55.214 20212 20212 W om.lovense.wea: resources.arsc in APK '/data/app/com.lovense.wear-zaCCuG0Zwk7dW6pyw==/base.apk' is compressed.
---55.227 20212 20212 I MultiDex: VM with version 2.1.0 has multidex support
---55.227 20212 20212 I MultiDex: Installing application
---55.227 20212 20212 I MultiDex: VM has multidex support, MultiDex support library is disabled.
---55.227 20212 20212 I MultiDex: Installing application
---55.227 20212 20212 I MultiDex: VM has multidex support, MultiDex support library is disabled.
---55.239 20212 20212 E Wear.App: onCreate().com.wear.util.MyApplication@ae21c82  curProcess=com.lovense.wear
---55.242 20212 20212 D da1     : :xmpp_email解密前:null
---55.242 20212 20212 D da1     : :xmpp_email解密后:null
---55.243 20212 20212 D ta1     : ------Vasyan Note 8 Pro Max 2->10->v3.8.7->c134 ->BLE:true
---55.248 20212 20212 I TableUtils: clearing table 'tb_log_type' with 'DELETE FROM `tb_log_type`
---55.262 20212 20212 W om.lovense.wea: Accessing hidden field Landroid/app/ActivityThread;->mHiddenApiWarningShown:Z (greylist-max-o, reflection, denied)
---55.262 20212 20212 W System.err: java.lang.NoSuchFieldException: No field mHiddenApiWarningShown in class Landroid/app/ActivityThread; (declaration of 'android.app.ActivityThread' appears in /system/framework/framework.jar)
---55.262 20212 20212 W System.err:    at java.lang.Class.getDeclaredField(Native Method)
---55.262 20212 20212 W System.err:    at com.wear.util.MyApplication.c(MyApplication.java:23)
---55.262 20212 20212 W System.err:    at com.wear.util.MyApplication.onCreate(MyApplication.java:13)
---55.262 20212 20212 W System.err:    at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1189)
---55.262 20212 20212 W System.err:    at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6460)
---55.262 20212 20212 W System.err:    at android.app.ActivityThread.access$1300(ActivityThread.java:219)
---55.262 20212 20212 W System.err:    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1859)
---55.262 20212 20212 W System.err:    at android.os.Handler.dispatchMessage(Handler.java:107)
---55.262 20212 20212 W System.err:    at android.os.Looper.loop(Looper.java:214)
---55.262 20212 20212 W System.err:    at android.app.ActivityThread.main(ActivityThread.java:7356)
---55.262 20212 20212 W System.err:    at java.lang.reflect.Method.invoke(Native Method)
---55.262 20212 20212 W System.err:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
---55.262 20212 20212 W System.err:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:940)
---55.265 20212 20212 W ImageLoader: diskCache() and diskCacheFileNameGenerator() calls overlap each other
---55.278 20212 20212 D NetworkSecurityConfig: Using Network Security Config from resource network_security_config debugBuild: false
---55.287 20212 20212 D NetworkNewUtils: ========types size: =======1
---55.287 20212 20212 I System.out:   dc.q81<java.lang.String>
---55.287 20212 20212 D NetworkNewUtils: ===========childtype:=======class java.lang.String
---55.287 20212 20212 D NetworkNewUtils: ========types size: =======1
---55.287 20212 20212 I System.out:   dc.q81<java.lang.String>
---55.287 20212 20212 D NetworkNewUtils: ===========childtype:=======class java.lang.String
---55.291 20212 20212 W System.err: java.lang.SecurityException: ConnectivityService: Neither user 10243 nor current process has android.permission.ACCESS_NETWORK_STATE.
---55.291 20212 20212 W System.err:    at android.os.Parcel.createException(Parcel.java:2071)
---55.292 20212 20212 W System.err:    at android.os.Parcel.readException(Parcel.java:2039)
---55.292 20212 20212 W System.err:    at android.os.Parcel.readException(Parcel.java:1987)
---55.292 20212 20212 W System.err:    at android.net.IConnectivityManager$Stub$Proxy.getActiveNetworkInfo(IConnectivityManager.java:2147)
---55.292 20212 20212 W System.err:    at android.net.ConnectivityManager.getActiveNetworkInfo(ConnectivityManager.java:958)
---55.292 20212 20212 W System.err:    at dc.n81.a(BaseSubscriber.java:15)
---55.292 20212 20212 W System.err:    at dc.v81.onStart(NetAsyncSubscriber.java:2)
---55.292 20212 20212 W System.err:    at rx.Observable.subscribe(Observable.java:23)
---55.292 20212 20212 W System.err:    at rx.Observable.subscribe(Observable.java:21)
---55.292 20212 20212 W System.err:    at dc.l81.a(NetworkNewUtils.java:32)
---55.292 20212 20212 W System.err:    at dc.l81.a(NetworkNewUtils.java:46)
---55.292 20212 20212 W System.err:    at dc.pa1.i(InitUtils.java:14)
---55.292 20212 20212 W System.err:    at com.wear.util.MyApplication.onCreate(MyApplication.java:71)
---55.292 20212 20212 W System.err:    at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1189)
---55.292 20212 20212 W System.err:    at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6460)
---55.292 20212 20212 W System.err:    at android.app.ActivityThread.access$1300(ActivityThread.java:219)
---55.292 20212 20212 W System.err:    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1859)
---55.292 20212 20212 W System.err:    at android.os.Handler.dispatchMessage(Handler.java:107)
---55.292 20212 20212 W System.err:    at android.os.Looper.loop(Looper.java:214)
---55.292 20212 20212 W System.err:    at android.app.ActivityThread.main(ActivityThread.java:7356)
---55.292 20212 20212 W System.err:    at java.lang.reflect.Method.invoke(Native Method)
---55.292 20212 20212 W System.err:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
---55.292 20212 20212 W System.err:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:940)
---55.292 20212 20212 W System.err: Caused by: android.os.RemoteException: Remote stack trace:
---55.292 20212 20212 W System.err:    at android.app.ContextImpl.enforce(ContextImpl.java:1896)
---55.292 20212 20212 W System.err:    at android.app.ContextImpl.enforceCallingOrSelfPermission(ContextImpl.java:1924)
---55.292 20212 20212 W System.err:    at com.android.server.ConnectivityService.enforceAccessPermission(ConnectivityService.java:2074)
---55.292 20212 20212 W System.err:    at com.android.server.ConnectivityService.getActiveNetworkInfo(ConnectivityService.java:1335)
---55.292 20212 20212 W System.err:    at android.net.IConnectivityManager$Stub.onTransact(IConnectivityManager.java:810)
---55.298 20212 20212 D Lifecycle: :com.wear.crash.CrashActivity was Createdactivity==null   false     activity.isFinishing()  false    activity.isDestroyed()  false
---55.317 20212 20212 D Lifecycle: :com.wear.crash.CrashActivity was Startedactivity==null   false     activity.isFinishing()   false   activity.isDestroyed()  false
---55.318 20212 20212 D Lifecycle: :com.wear.crash.CrashActivity was oResumedactivity==null   falseactivity.isFinishing()   falseactivity.isDestroyed() false
---55.322 20212 20212 D blelib  : : ANDROID
---55.325 20212 20212 D AndroidRuntime: Shutting down VM
---55.325 20212 20212 E AndroidRuntime: FATAL EXCEPTION: main
---55.325 20212 20212 E AndroidRuntime: Process: com.lovense.wear, PID: 20212
---55.325 20212 20212 E AndroidRuntime: java.lang.RuntimeException: Error receiving broadcast Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x4200010 (has extras) } in com.wear.broadcast.NetworkReceiver@a81a52c
---55.325 20212 20212 E AndroidRuntime:        at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1565)
---55.325 20212 20212 E AndroidRuntime:        at android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$_BumDX2UKsnxLVrE6UJsJZkotuA.run(Unknown Source:2)
---55.325 20212 20212 E AndroidRuntime:        at android.os.Handler.handleCallback(Handler.java:883)
---55.325 20212 20212 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:100)
---55.325 20212 20212 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:214)
---55.325 20212 20212 E AndroidRuntime:        at android.app.ActivityThread.main(ActivityThread.java:7356)
---55.325 20212 20212 E AndroidRuntime:        at java.lang.reflect.Method.invoke(Native Method)
---55.325 20212 20212 E AndroidRuntime:        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
---55.325 20212 20212 E AndroidRuntime:        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:940)
---55.325 20212 20212 E AndroidRuntime: Caused by: java.lang.SecurityException: ConnectivityService: Neither user 10243 nor current process has android.permission.ACCESS_NETWORK_STATE.
---55.325 20212 20212 E AndroidRuntime:        at android.os.Parcel.createException(Parcel.java:2071)
---55.325 20212 20212 E AndroidRuntime:        at android.os.Parcel.readException(Parcel.java:2039)
---55.325 20212 20212 E AndroidRuntime:        at android.os.Parcel.readException(Parcel.java:1987)
---55.325 20212 20212 E AndroidRuntime:        at android.net.IConnectivityManager$Stub$Proxy.getActiveNetworkInfo(IConnectivityManager.java:2147)
---55.325 20212 20212 E AndroidRuntime:        at android.net.ConnectivityManager.getActiveNetworkInfo(ConnectivityManager.java:958)
---55.325 20212 20212 E AndroidRuntime:        at com.wear.broadcast.NetworkReceiver.onReceive(NetworkReceiver.java:2)
---55.325 20212 20212 E AndroidRuntime:        at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1555)
---55.325 20212 20212 E AndroidRuntime:        ... 8 more
---55.342 20212 20212 W System.err: java.lang.SecurityException: ConnectivityService: Neither user 10243 nor current process has android.permission.ACCESS_NETWORK_STATE.
---55.342 20212 20212 W System.err:    at android.os.Parcel.createException(Parcel.java:2071)
---55.342 20212 20212 W System.err:    at android.os.Parcel.readException(Parcel.java:2039)
---55.342 20212 20212 W System.err:    at android.os.Parcel.readException(Parcel.java:1987)
---55.342 20212 20212 W System.err:    at android.net.IConnectivityManager$Stub$Proxy.getActiveNetworkInfo(IConnectivityManager.java:2147)
---55.342 20212 20212 W System.err:    at android.net.ConnectivityManager.getActiveNetworkInfo(ConnectivityManager.java:958)
---55.342 20212 20212 W System.err:    at dc.n81.a(BaseSubscriber.java:15)
---55.342 20212 20212 W System.err:    at dc.v81.onStart(NetAsyncSubscriber.java:2)
---55.342 20212 20212 W System.err:    at rx.Observable.subscribe(Observable.java:23)
---55.342 20212 20212 W System.err:    at rx.Observable.subscribe(Observable.java:21)
---55.342 20212 20212 W System.err:    at dc.l81.b(NetworkNewUtils.java:12)
---55.342 20212 20212 W System.err:    at dc.ta1.c(LogUtils.java:13)
---55.342 20212 20212 W System.err:    at dc.rz0.a(CrashHandler.java:11)
---55.342 20212 20212 W System.err:    at dc.rz0.uncaughtException(CrashHandler.java:1)
---55.342 20212 20212 W System.err:    at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1073)
---55.342 20212 20212 W System.err:    at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1068)
---55.342 20212 20212 W System.err:    at java.lang.Thread.dispatchUncaughtException(Thread.java:2187)
---55.769 20212 20237 D connectScan: :reconnectOneLoopFlag==true  当前蓝牙扫描状态 isScanAction=0
---55.769 20212 20237 D EventBus: No subscribers registered for event class com.wear.bean.event.NinjaLockTimeEvent
---55.769 20212 20237 D EventBus: No subscribers registered for event class org.greenrobot.eventbus.NoSubscriberEvent
---56.346 20212 20212 I Process : Sending signal. PID: 20212 SIG: 9
---56.355  1336  2991 I ActivityManager: Process com.lovense.wear (pid 20212) has died: fore TOP
---56.355  1336  1369 I libprocessgroup: Successfully killed process cgroup uid 10243 pid 20212 in 0ms
---56.355   777   777 I Zygote  : Process 20212 exited due to signal 9 (Killed)
---56.363  1336  1361 W ActivityManager: setHasOverlayUi called on unknown pid: 20212

Смотрим внимательно в логи и медитируем. По всей видимости креш случается после вызова ConnectivityManager.getActiveNetworkInfo() в dc.n81.a.


Что делает этот метод - не очень понятно, но ничего полезного не видно, вроде как только дергает ConnectivityManager.getActiveNetworkInfo() и все... Метод возвращает Z, так что скажем, что все завершилось успешно. Версия, улучшенная и доработанная:


Компиляем, запускаем... Не, все равно черный экран! Ничего не поменялось? Нет, просто теперь падает оно так:

---05.371   777   777 D Zygote  : Forked child process 24097
---05.377  1336  1368 I ActivityManager: Start proc 24097:com.lovense.wear/u0a243 for top-activity {com.lovense.wear/com.wear.crash.CrashActivity}
---05.398 24097 24097 W om.lovense.wea: resources.arsc in APK '/data/app/com.lovense.wear-4jAt-FQW7Xi-Y1_xTGg5KA==/base.apk' is compressed.
---05.411 24097 24097 I MultiDex: VM with version 2.1.0 has multidex support
---05.411 24097 24097 I MultiDex: Installing application
---05.411 24097 24097 I MultiDex: VM has multidex support, MultiDex support library is disabled.
---05.411 24097 24097 I MultiDex: Installing application
---05.411 24097 24097 I MultiDex: VM has multidex support, MultiDex support library is disabled.
---05.421 24097 24097 E Wear.App: onCreate().com.wear.util.MyApplication@ae21c82  curProcess=com.lovense.wear
---05.424 24097 24097 D da1     : :xmpp_email解密前:null
---05.424 24097 24097 D da1     : :xmpp_email解密后:null
---05.425 24097 24097 D ta1     : ------Vasyan Note 8 Pro Max 2->10->v3.8.7->c134 ->BLE:true
---05.431 24097 24097 I TableUtils: clearing table 'tb_log_type' with 'DELETE FROM `tb_log_type`
---05.444 24097 24097 W om.lovense.wea: Accessing hidden field Landroid/app/ActivityThread;->mHiddenApiWarningShown:Z (greylist-max-o, reflection, denied)
---05.444 24097 24097 W System.err: java.lang.NoSuchFieldException: No field mHiddenApiWarningShown in class Landroid/app/ActivityThread; (declaration of 'android.app.ActivityThread' appears in /system/framework/framework.jar)
---05.444 24097 24097 W System.err:    at java.lang.Class.getDeclaredField(Native Method)
---05.444 24097 24097 W System.err:    at com.wear.util.MyApplication.c(MyApplication.java:23)
---05.444 24097 24097 W System.err:    at com.wear.util.MyApplication.onCreate(MyApplication.java:13)
---05.444 24097 24097 W System.err:    at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1189)
---05.444 24097 24097 W System.err:    at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6460)
---05.445 24097 24097 W System.err:    at android.app.ActivityThread.access$1300(ActivityThread.java:219)
---05.445 24097 24097 W System.err:    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1859)
---05.445 24097 24097 W System.err:    at android.os.Handler.dispatchMessage(Handler.java:107)
---05.445 24097 24097 W System.err:    at android.os.Looper.loop(Looper.java:214)
---05.445 24097 24097 W System.err:    at android.app.ActivityThread.main(ActivityThread.java:7356)
---05.445 24097 24097 W System.err:    at java.lang.reflect.Method.invoke(Native Method)
---05.445 24097 24097 W System.err:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
---05.445 24097 24097 W System.err:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:940)
---05.447 24097 24097 W ImageLoader: diskCache() and diskCacheFileNameGenerator() calls overlap each other
---05.460 24097 24097 D NetworkSecurityConfig: Using Network Security Config from resource network_security_config debugBuild: false
---05.469 24097 24097 D NetworkNewUtils: ========types size: =======1
---05.469 24097 24097 I System.out:   dc.q81<java.lang.String>
---05.469 24097 24097 D NetworkNewUtils: ===========childtype:=======class java.lang.String
---05.469 24097 24097 D NetworkNewUtils: ========types size: =======1
---05.469 24097 24097 I System.out:   dc.q81<java.lang.String>
---05.469 24097 24097 D NetworkNewUtils: ===========childtype:=======class java.lang.String
---05.473 24097 24097 W RxIoScheduler-3: type=1400 audit(0.0:676): avc: denied { lock } for path="/system/etc/hosts" dev="sdc40" ino=1178 scontext=u:r:untrusted_app_27:s0:c243,c256,c512,c768 tcontext=u:object_r:system_file:s0 tclass=file permissive=0
---05.479 24097 24127 D ta1     : save log to db:S0005  https://apps2.lovense.com/api/featuresConfig#A000#Permission denied (missing INTERNET permission?)
---05.481 24097 24097 D Lifecycle: :com.wear.crash.CrashActivity was Createdactivity==null   false     activity.isFinishing()  false    activity.isDestroyed()  false
---05.500 24097 24097 D Lifecycle: :com.wear.crash.CrashActivity was Startedactivity==null   false     activity.isFinishing()   false   activity.isDestroyed()  false
---05.501 24097 24097 D Lifecycle: :com.wear.crash.CrashActivity was oResumedactivity==null   falseactivity.isFinishing()   falseactivity.isDestroyed() false
---05.504 24097 24097 D blelib  : : ANDROID
---05.506 24097 24097 D AndroidRuntime: Shutting down VM
---05.507 24097 24097 E AndroidRuntime: FATAL EXCEPTION: main
---05.507 24097 24097 E AndroidRuntime: Process: com.lovense.wear, PID: 24097
---05.507 24097 24097 E AndroidRuntime: java.lang.RuntimeException: Error receiving broadcast Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x4200010 (has extras) } in com.wear.broadcast.NetworkReceiver@a81a52c
---05.507 24097 24097 E AndroidRuntime:        at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1565)
---05.507 24097 24097 E AndroidRuntime:        at android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$_BumDX2UKsnxLVrE6UJsJZkotuA.run(Unknown Source:2)
---05.507 24097 24097 E AndroidRuntime:        at android.os.Handler.handleCallback(Handler.java:883)
---05.507 24097 24097 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:100)
---05.507 24097 24097 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:214)
---05.507 24097 24097 E AndroidRuntime:        at android.app.ActivityThread.main(ActivityThread.java:7356)
---05.507 24097 24097 E AndroidRuntime:        at java.lang.reflect.Method.invoke(Native Method)
---05.507 24097 24097 E AndroidRuntime:        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
---05.507 24097 24097 E AndroidRuntime:        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:940)
---05.507 24097 24097 E AndroidRuntime: Caused by: java.lang.SecurityException: ConnectivityService: Neither user 10243 nor current process has android.permission.ACCESS_NETWORK_STATE.
---05.507 24097 24097 E AndroidRuntime:        at android.os.Parcel.createException(Parcel.java:2071)
---05.507 24097 24097 E AndroidRuntime:        at android.os.Parcel.readException(Parcel.java:2039)
---05.507 24097 24097 E AndroidRuntime:        at android.os.Parcel.readException(Parcel.java:1987)
---05.507 24097 24097 E AndroidRuntime:        at android.net.IConnectivityManager$Stub$Proxy.getActiveNetworkInfo(IConnectivityManager.java:2147)
---05.507 24097 24097 E AndroidRuntime:        at android.net.ConnectivityManager.getActiveNetworkInfo(ConnectivityManager.java:958)
---05.507 24097 24097 E AndroidRuntime:        at com.wear.broadcast.NetworkReceiver.onReceive(NetworkReceiver.java:2)
---05.507 24097 24097 E AndroidRuntime:        at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1555)
---05.507 24097 24097 E AndroidRuntime:        ... 8 more
---05.507 24097 24097 E AndroidRuntime: Caused by: android.os.RemoteException: Remote stack trace:
---05.507 24097 24097 E AndroidRuntime:        at android.app.ContextImpl.enforce(ContextImpl.java:1896)
---05.507 24097 24097 E AndroidRuntime:        at android.app.ContextImpl.enforceCallingOrSelfPermission(ContextImpl.java:1924)
---05.507 24097 24097 E AndroidRuntime:        at com.android.server.ConnectivityService.enforceAccessPermission(ConnectivityService.java:2074)
---05.507 24097 24097 E AndroidRuntime:        at com.android.server.ConnectivityService.getActiveNetworkInfo(ConnectivityService.java:1335)
---05.507 24097 24097 E AndroidRuntime:        at android.net.IConnectivityManager$Stub.onTransact(IConnectivityManager.java:810)
---05.507 24097 24097 E AndroidRuntime:
---05.521 24097 24097 W RxIoScheduler-2: type=1400 audit(0.0:677): avc: denied { lock } for path="/system/etc/hosts" dev="sdc40" ino=1178 scontext=u:r:untrusted_app_27:s0:c243,c256,c512,c768 tcontext=u:object_r:system_file:s0 tclass=file permissive=0
---05.526 24097 24128 D ta1     : save log to db:S0005  https://log.lovense.com/wear/logsNewV2#A000#Permission denied (missing INTERNET permission?)
---05.950 24097 24122 D connectScan: :reconnectOneLoopFlag==true  当前蓝牙扫描状态 isScanAction=0
---05.951 24097 24122 D EventBus: No subscribers registered for event class com.wear.bean.event.NinjaLockTimeEvent
---05.951 24097 24122 D EventBus: No subscribers registered for event class org.greenrobot.eventbus.NoSubscriberEvent
---05.529 24097 24097 I Process : Sending signal. PID: 24097 SIG: 9
---05.537  1336  2465 I ActivityManager: Process com.lovense.wear (pid 24097) has died: fore TOP
---05.537  1336  1369 I libprocessgroup: Successfully killed process cgroup uid 10243 pid 24097 in 0ms
---05.537   777   777 I Zygote  : Process 24097 exited due to signal 9 (Killed)
---05.546  1336  1361 W ActivityManager: setHasOverlayUi called on unknown pid: 24097

Мне так нравится видеть, как оно пытается связаться с домом log.lovense.com, но так как крылья обрезаны и ноги связаны, у него это не получается.

Ладно, пошли патчить com.wear.broadcast.NetworkReceiver.onReceive? Интересно, а много еще этих вызовов getActiveNetworkInfo?

./smali_classes2/org/webrtc/NetworkMonitorAutoDetect$ConnectivityManagerDelegate.smali:    invoke-virtual {v0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali_classes2/org/webrtc/NetworkMonitorAutoDetect$ConnectivityManagerDelegate.smali:    invoke-virtual {v0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali_classes2/org/webrtc/NetworkMonitorAutoDetect$ConnectivityManagerDelegate.smali:    invoke-virtual {v1}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali_classes2/io/fabric/sdk/android/services/common/CommonUtils.smali:    invoke-virtual {p0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali_classes2/dc/qa1.smali:    invoke-virtual {p0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali_classes2/dc/qa1.smali:    invoke-virtual {p0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali_classes2/dc/g21.smali:    invoke-virtual {p1}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali_classes2/dc/ab1.smali:    invoke-virtual {p0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali_classes2/dc/ab1.smali:    invoke-virtual {v0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali/dc/po0.smali:    invoke-virtual {v0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali/dc/pd.smali:    invoke-virtual {v0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali/dc/ol0.smali:    invoke-virtual {v0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali/dc/m7.smali:    invoke-virtual {p1}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali/dc/hb.smali:    invoke-virtual {v0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali/com/wear/broadcast/NetworkReceiver.smali:    invoke-virtual {p1}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali/com/just/agentweb/AgentWebUtils.smali:    invoke-virtual {p0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali/com/just/agentweb/AgentWebUtils.smali:    invoke-virtual {p0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali/com/just/agentweb/AgentWebUtils.smali:    invoke-virtual {p0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
./smali/androidx/core/net/ConnectivityManagerCompat.smali:    invoke-virtual {p0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;

В общем-то один вызов из com/wear/ и все остальное - вражеские библиотеки, которые только и делают, что собирают информацию про меня! И вы еще говорите, что я никому не нужен? ВОН СКОЛЬКО ЖЕЛАЮЩИХ!

В этот раз метод onReceive очень большой, отламывать его целиком - явно приложение сломается, а пытаться осмыслить его - можно сломаться самому. Смотрим в документацию:

Returns details about the currently active default data network. When connected, this network is the default route for outgoing connections. You should always check NetworkInfo.isConnected() before initiating network traffic. This may return null when there is no default network. Note that if the default network is a VPN, this method will return the NetworkInfo for one of its underlying networks instead, or null if the VPN agent did not specify any. Apps interested in learning about VPNs should use getNetworkInfo(android.net.Network) instead. Requires android.Manifest.permission.ACCESS_NETWORK_STATE

Returns:

a NetworkInfo object for the current default network or null if no default network is currently active

Ага, значит оно может выдать null и это будет нормально!

Ладно, еще один маленький патч:


Здесь мы сразу записываем в результирующий регистр 0, что может означать null. Как видим, дальше в коде уже присутствует проверка на null, так что ничего плохого не случится. А если и случится, то результат isAvailable() можно аналогичным вариантом всегда возвращать как true (const/4 p1,1) и приложение будет думать, что у нас всегда есть интернеты.

И о чудо, все заработало!


ЗЫ: не надо тестить приложение, когда сам дилдак висит на мониторе. Чтобы сделать скриншот выше, я подвигал паттерны, и у меня завибрировал монитор, после чего с грохотом с него упал сам Lush.