├── .dockerignore ├── .github └── workflows │ └── build-apk.yml ├── .gitignore ├── .yamllint ├── Dockerfile ├── README.md ├── app ├── build.gradle ├── libs │ └── protobuf-java-3.6.0.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── uk │ │ └── co │ │ └── borconi │ │ └── emil │ │ └── aagateway │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ │ └── uk │ │ │ └── co │ │ │ └── borconi │ │ │ └── emil │ │ │ └── aagateway │ │ │ ├── HackerService.java │ │ │ ├── MainActivity.java │ │ │ ├── PowerConnectionReceiver.java │ │ │ └── Preferences.java │ ├── jniLibs │ │ ├── armeabi │ │ │ └── libhu_jni.so │ │ └── x86 │ │ │ └── libhu_jni.so │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── aawifi.png │ │ ├── hu_icon_256.png │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── white.xml │ │ └── xml │ │ └── accessory_filter.xml │ └── test │ └── java │ └── uk │ └── co │ └── borconi │ └── emil │ └── aagateway │ └── ExampleUnitTest.java ├── build.gradle ├── build_in_docker.sh ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | app/build 3 | build 4 | -------------------------------------------------------------------------------- /.github/workflows/build-apk.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Build APK 3 | 4 | on: 5 | push: 6 | branches: 7 | - master 8 | tags: 9 | - v* 10 | pull_request: 11 | branches: 12 | - master 13 | 14 | jobs: 15 | tests: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: "YamlLint" 20 | run: | 21 | docker run \ 22 | --rm \ 23 | -w /repo \ 24 | -v $(pwd):/repo \ 25 | -t \ 26 | alpine:3.13 /bin/sh -c " \ 27 | apk add --no-cache py-pip python3 bash \ 28 | && pip3 install yamllint \ 29 | && yamllint -s . \ 30 | " 31 | - name: "ShellCheck" 32 | run: | 33 | docker run \ 34 | --rm \ 35 | -w /repo \ 36 | -v $(pwd):/repo \ 37 | -t \ 38 | alpine:3.13 /bin/sh -c " \ 39 | apk add --no-cache shellcheck bash \ 40 | && shellcheck $(find . -type f -name "*.sh" | tr '\n' ' ') 41 | " 42 | build: 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@v2 46 | - name: "Build APK" 47 | run: ./build_in_docker.sh 48 | - name: Release 49 | uses: softprops/action-gh-release@v1 50 | if: startsWith(github.ref, 'refs/tags/') 51 | with: 52 | tag_name: ${{ github.ref }} 53 | name: Release ${{ github.ref }} 54 | draft: true 55 | files: | 56 | ./app/build/outputs/apk/debug/* 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # You don't need to add this in secrets it's by default. 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | *.iml 3 | app/*.iml 4 | 5 | # Built application files 6 | *.apk 7 | *.ap_ 8 | 9 | # Files for the ART/Dalvik VM 10 | *.dex 11 | 12 | # Java class files 13 | *.class 14 | 15 | # Generated files 16 | bin/ 17 | gen/ 18 | out/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/workspace.xml 42 | .idea/tasks.xml 43 | .idea/gradle.xml 44 | .idea/assetWizardSettings.xml 45 | .idea/dictionaries 46 | .idea/libraries 47 | .idea/caches 48 | 49 | # Keystore files 50 | # Uncomment the following line if you do not want to check your keystore files in. 51 | #*.jks 52 | 53 | # External native build folder generated in Android Studio 2.2 and later 54 | .externalNativeBuild 55 | 56 | # Google Services (e.g. APIs or Firebase) 57 | google-services.json 58 | 59 | # Freeline 60 | freeline.py 61 | freeline/ 62 | freeline_project_description.json 63 | 64 | # fastlane 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots 68 | fastlane/test_output 69 | fastlane/readme.md 70 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | line-length: disable 6 | truthy: disable 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | 5 | # https://developer.android.com/studio/index.html#command-tools 6 | 7 | WORKDIR /opt/android 8 | 9 | RUN apt-get update \ 10 | && apt-get install -y \ 11 | curl \ 12 | unzip \ 13 | openjdk-8-jdk 14 | 15 | # jdk 8, 11, 13 ,16 16 | # && apt-get install -y android-sdk 17 | 18 | ENV ANDROID_HOME=/opt/android 19 | ENV PATH=$ANDROID_HOME/cmdline-tools/tools/bin/:$PATH 20 | ENV PATH=$ANDROID_HOME/emulator/:$PATH 21 | ENV PATH=$ANDROID_HOME/platform-tools/:$PATH 22 | 23 | RUN curl -L $(curl -sL https://developer.android.com/studio/index.html\#command-tools | grep "zip" | grep "linux" | grep "commandline" | grep "href" | cut -d'"' -f2) -O \ 24 | && unzip $(ls | grep zip) \ 25 | && rm -rf $(ls | grep zip) \ 26 | && mkdir tools \ 27 | && mv cmdline-tools/* tools \ 28 | && mv tools cmdline-tools/ \ 29 | && yes | sdkmanager --licenses 30 | 31 | # ENV GRADLE_OPTS=-Djava.io.tmpdir=/repo/ 32 | # export GRADLE_OPTS=-Djava.io.tmpdir=/repo/ 33 | 34 | # ./gradlew assemble 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AAGateWay 2 | 3 | A modified version of [AAGateWay](https://github.com/borconi/AAGateWay) that requires [AAstarter](https://github.com/olivluca/AAstarter) on the master phone. 4 | 5 | I only tested it on my car (with a MIB 2 headuint) and my phones. The master is a Xiaomi Redmi Note 4, the slave is a Motorola Moto E 2015 (surnia), both running 6 | LineagesOs 17.1 (android 10). 7 | 8 | The MIB 2 I have is quite peculiar: when it cannot establish a connection it will briefly remove power on the usb connector and that will upset 9 | the apk released by Emil, hence the need to write this one. 10 | 11 | 12 | The steps to make it work are: 13 | 14 | 1. open the app and ensure it is using the default options _LISTENING MODE_ and _USE IPV4_. 15 | 1. setup a hotspot on the slave. 16 | 1. configure the master to connect to said hotspot and to keep using it even if it has no Internet. 17 | 1. Install this app on the slave and AAstarter on the master. 18 | 1. Start AAStarter on the master and grant it the required permissions. 19 | 1. Connect the slave to the headunit. 20 | 1. When prompted confirm that you want to use AAGateway as the default application. 21 | 1. Wait for the master to be connected to the slave's hotspot. 22 | 1. Push the button to connect to the phone on the headunit. 23 | 24 | It usually needs 2 o 3 (or more) tries before successfully establishing a connection. 25 | None of the devices needs to be rooted. 26 | 27 | The principle of operation is: 28 | 29 | * when the headunit starts it, AAGateway will send a trigger on udp port 4455 to AAstarter on the master (actually it will send it to every connected station but 30 | 1. only the master should be connected to this hotspot 31 | 1. only the master will reply 32 | * AAstarter will start Android Auto telling it to connect back to the slave. 33 | * When the slave successfully initializes the connection with both partners (the headunit and the slave) it will start moving data between them. 34 | 35 | If you set the _CONNECTING MODE_ option, then the app will work just like the original AAGateWay (it will connect to the headunit server that 36 | you'll have to manually start on the master) but with the hotspot on the slave. 37 | 38 | With the _USE IPV4_ option the app will only try ipv4 addresses to connect to the master, while with _USE IPV6_ it will only try ipv6 addresses. 39 | 40 | 41 | ========================= 42 | 43 | 44 | below is the original README: 45 | 46 | # AAGateWay 47 | 48 | A super simple app which allows the connection to Android Auto over Wifi. It requires an Android Auto compatible car in the first place. 49 | 50 | # License 51 | 52 | You are free to use the code for personal use in any shape or form you want, and implement any modification you wish, however you are stictly forbiden in creating and publishing app with the same or similar purposer, regardless if the app is free or comrecial. If you wish to use the code in building and releaseing your own app, please seek written approval before proceeding. 53 | 54 | # Copyright 55 | Emil Borconi-Szedressy (C) 2017 - Wakefield - United Kingdom 56 | 57 | # Requirements 58 | 59 | * Android Studio 3.4.1 or higher 60 | * Gradle 5.1.1 61 | * Android API 27 62 | 63 | 64 | # Build 65 | 66 | ``` 67 | $> ./gradlew assemble 68 | ``` 69 | 70 | This will generate an `apk` file inside build directory `./app/build/outputs/apk/debug` 71 | 72 | # Install in debug device 73 | 74 | ``` 75 | $> ./gradlew installDebug 76 | ``` 77 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | 4 | android { 5 | compileSdkVersion 27 6 | defaultConfig { 7 | applicationId "uk.co.borconi.emil.aagateway" 8 | minSdkVersion 14 9 | targetSdkVersion 27 10 | versionCode 1 11 | versionName "1.0.3 Alpha" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | multiDexEnabled true 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | implementation fileTree(include: ['*.jar'], dir: 'libs') 25 | implementation 'com.android.support:appcompat-v7:27.1.1+' 26 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 27 | testImplementation 'junit:junit:4.12' 28 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 29 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 30 | implementation files('libs/protobuf-java-3.6.0.jar') 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/libs/protobuf-java-3.6.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeeq/aagateway/02e89d112572f0612c6c66e22e8f24a1ff7dcb01/app/libs/protobuf-java-3.6.0.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/androidTest/java/uk/co/borconi/emil/aagateway/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.borconi.emil.aagateway; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("uk.co.borconi.emil.aagateway", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeeq/aagateway/02e89d112572f0612c6c66e22e8f24a1ff7dcb01/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/uk/co/borconi/emil/aagateway/HackerService.java: -------------------------------------------------------------------------------- 1 | package uk.co.borconi.emil.aagateway; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationChannel; 5 | import android.app.NotificationManager; 6 | import android.app.Service; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.content.SharedPreferences; 10 | import android.graphics.Color; 11 | import android.hardware.usb.UsbAccessory; 12 | import android.hardware.usb.UsbManager; 13 | import android.net.ConnectivityManager; 14 | import android.net.DhcpInfo; 15 | import android.net.wifi.WifiManager; 16 | import android.os.Binder; 17 | import android.os.Build; 18 | import android.os.IBinder; 19 | import android.os.Looper; 20 | import android.os.ParcelFileDescriptor; 21 | import android.preference.PreferenceManager; 22 | import android.util.Log; 23 | 24 | import java.io.BufferedReader; 25 | import java.io.DataInputStream; 26 | import java.io.InputStreamReader; 27 | import java.io.FileDescriptor; 28 | import java.io.FileInputStream; 29 | import java.io.FileOutputStream; 30 | import java.io.IOException; 31 | import java.io.OutputStream; 32 | import java.net.DatagramPacket; 33 | import java.net.DatagramSocket; 34 | import java.net.ServerSocket; 35 | import java.net.Socket; 36 | import java.net.InetAddress; 37 | import java.net.InetSocketAddress; 38 | import java.util.Arrays; 39 | 40 | 41 | 42 | import static android.app.NotificationManager.IMPORTANCE_HIGH; 43 | 44 | 45 | 46 | /** 47 | * Created by Emil on 25/03/2018. 48 | */ 49 | 50 | public class HackerService extends Service { 51 | private static final String TAG = "AAGateWay"; 52 | private NotificationManager mNotificationManager; 53 | private Intent notificationIntent; 54 | private final IBinder mBinder = new LocalBinder(); 55 | private UsbAccessory mAccessory; 56 | private UsbManager mUsbManager; 57 | private ParcelFileDescriptor mFileDescriptor; 58 | private FileDescriptor fd; 59 | private FileOutputStream phoneOutputStream; 60 | private FileInputStream phoneInputStream; 61 | 62 | private static OutputStream socketoutput; 63 | private static DataInputStream socketinput; 64 | private static Socket socket; 65 | private boolean running=false; 66 | private boolean localCompleted,usbCompleted; 67 | private boolean listening; 68 | private boolean ignoreipv6; 69 | byte [] readbuffer=new byte[16384]; 70 | private Thread tcpreader; 71 | private Thread usbreader; 72 | 73 | @Override 74 | public IBinder onBind(Intent intent) { 75 | return mBinder; 76 | } 77 | public class LocalBinder extends Binder { 78 | HackerService getService() { 79 | return HackerService.this; 80 | } 81 | } 82 | 83 | @Override 84 | public void onCreate() { 85 | super.onCreate(); 86 | 87 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); 88 | listening=preferences.getBoolean(Preferences.LISTENING_MODE, true); 89 | ignoreipv6=preferences.getBoolean(Preferences.IGNORE_IPV6, true); 90 | String CHANNEL_ONE_ID = "uk.co.borconi.emil.aagateway"; 91 | String CHANNEL_ONE_NAME = "Channel One"; 92 | NotificationChannel notificationChannel = null; 93 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { 94 | notificationChannel = new NotificationChannel(CHANNEL_ONE_ID, 95 | CHANNEL_ONE_NAME, IMPORTANCE_HIGH); 96 | notificationChannel.enableLights(true); 97 | notificationChannel.setLightColor(Color.RED); 98 | notificationChannel.setShowBadge(true); 99 | notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); 100 | NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 101 | manager.createNotificationChannel(notificationChannel); 102 | } 103 | 104 | 105 | mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 106 | Notification.Builder mynotification = new Notification.Builder(this) 107 | .setContentTitle("Android Auto GateWay") 108 | .setContentText("Running....") 109 | .setSmallIcon(R.drawable.aawifi) 110 | .setTicker(""); 111 | if (Build.VERSION.SDK_INT>=26) 112 | mynotification.setChannelId(CHANNEL_ONE_ID); 113 | 114 | startForeground(1, mynotification.build()); 115 | 116 | } 117 | 118 | @Override 119 | public int onStartCommand(Intent intent, int flags, int startId) { 120 | 121 | if (running) { 122 | Log.d(TAG,"Service already running"); 123 | return START_STICKY; 124 | } 125 | Log.d(TAG,"Service Started"); 126 | super.onStartCommand(intent, flags, startId); 127 | mAccessory = (UsbAccessory) intent.getParcelableExtra("accessory"); 128 | mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE); 129 | mFileDescriptor = mUsbManager.openAccessory(mAccessory); 130 | if (mFileDescriptor != null) { 131 | fd = mFileDescriptor.getFileDescriptor(); 132 | phoneInputStream = new FileInputStream(fd); 133 | phoneOutputStream = new FileOutputStream(fd); 134 | usbCompleted=false; 135 | } else { 136 | Log.e(TAG, "Cannot open usb accessory "+mAccessory.toString()); 137 | stopSelf(); 138 | return START_STICKY; 139 | } 140 | 141 | //Manually start AA. 142 | running=true; 143 | localCompleted = false; 144 | usbCompleted = false; 145 | usbreader = new Thread(new usbpollthread()); 146 | tcpreader = new Thread(new tcppollthread()); 147 | usbreader.start(); 148 | tcpreader.start(); 149 | 150 | return START_STICKY; 151 | } 152 | 153 | class tcppollthread implements Runnable { 154 | private ServerSocket serversocket=null; 155 | 156 | public void run() { 157 | Log.d(TAG,"tcp - run"); 158 | if (listening) 159 | Log.d(TAG, "tcp - listening mode"); 160 | else 161 | Log.d(TAG, "tcp - connection mode"); 162 | if (ignoreipv6) 163 | Log.d(TAG, "tcp - use ipv4 addresses"); 164 | else 165 | Log.d(TAG, "tcp - use ipv6 addresses"); 166 | 167 | //connect or accept connection from the phone 168 | try { 169 | 170 | if (listening) { 171 | serversocket = new ServerSocket(5288, 5); 172 | serversocket.setSoTimeout(5000); //die early, die young 173 | serversocket.setReuseAddress(true); 174 | Log.d(TAG, "tcp - listening"); 175 | } 176 | //get the address of the clients connected to this hotspot 177 | String[] command = {"ip", "neigh", "show", "dev", "wlan0"}; 178 | Process p = Runtime.getRuntime().exec(command); 179 | BufferedReader br = new BufferedReader( 180 | new InputStreamReader(p.getInputStream())); 181 | String line; 182 | String phoneaddr = null; 183 | byte[] trigbuf = new byte[]{'S'}; 184 | DatagramSocket trigger = new DatagramSocket(); 185 | InetAddress addr; 186 | while ((line = br.readLine()) != null) { 187 | Log.d(TAG, "tcp - ip neigh output " + line); 188 | String[] splitted = line.split(" +"); 189 | if ((splitted == null) || (splitted.length < 1)) { 190 | Log.d(TAG, "tcp - not splitted?!"); 191 | continue; 192 | } 193 | boolean isipv6 = splitted[0].contains(":"); 194 | if (ignoreipv6 && isipv6) { 195 | Log.d(TAG, "tcp - IPV6, ignoring"); 196 | continue; 197 | } 198 | if (!ignoreipv6 && !isipv6) { 199 | Log.d(TAG, "tcp - IPV4, ignoring"); 200 | continue; 201 | } 202 | addr = InetAddress.getByName(splitted[0]); 203 | if (listening) { 204 | //send to every address, only the phone with AAStarter will try to connect back 205 | Log.d(TAG, "tcp - sending trigger to " + splitted[0]); 206 | DatagramPacket trigpacket = new DatagramPacket(trigbuf, trigbuf.length, addr, 4455); 207 | trigger.send(trigpacket); 208 | } else { 209 | if (addr.isReachable(300)) { 210 | Log.d(TAG, "tcp - reachable " + splitted[0]); 211 | phoneaddr = splitted[0]; 212 | break; 213 | } 214 | Log.d(TAG, "tcp - not reachable " + splitted[0]); 215 | } 216 | } 217 | if (listening) { 218 | socket = serversocket.accept(); 219 | Log.d(TAG, "tcp - phone has connected back"); 220 | socket.setSoTimeout(5000); 221 | } else { 222 | if (phoneaddr == null) { 223 | //no address found 224 | Log.e(TAG, "tcp - no active station found"); 225 | running = false; 226 | stopSelf(); 227 | } else { 228 | Log.d(TAG, "tcp - connecting to phone " + phoneaddr); 229 | socket = new Socket(); 230 | socket.setSoTimeout(5000); 231 | socket.connect(new InetSocketAddress(phoneaddr, 5277), 500); 232 | Log.d(TAG, "tcp - connected"); 233 | } 234 | } 235 | 236 | //at this point running could be false in non listening mode and no address found 237 | if (running) { 238 | socketoutput = socket.getOutputStream(); 239 | socketinput = new DataInputStream(socket.getInputStream()); 240 | socketoutput.write(new byte[]{0, 3, 0, 6, 0, 1, 0, 1, 0, 2}); 241 | socketoutput.flush(); 242 | byte[] recv = new byte[12]; 243 | socketinput.read(recv); 244 | Log.d(TAG, "tcp - recv from phone " + bytesToHex(recv)); 245 | localCompleted = true; 246 | } 247 | } catch (Exception e) { 248 | Log.e(TAG, "tcp - error opening phone " + e.getMessage()); 249 | running = false; 250 | stopSelf(); 251 | } 252 | 253 | //wait for usb initialization 254 | if (!usbCompleted && running) 255 | Log.d(TAG, "tcp - waiting for usb"); 256 | while (!usbCompleted && running) { 257 | try { 258 | Thread.sleep(10); 259 | } catch (InterruptedException e) { 260 | Log.e(TAG, "tcp - error sleeping "+e.getMessage()); 261 | } 262 | } 263 | 264 | //Looper.prepare(); 265 | while (running) 266 | { 267 | try { 268 | 269 | getLocalmessage(false); 270 | 271 | } catch (Exception e) { 272 | Log.e(TAG,"tcp - in main loop "+e.getMessage()); 273 | running = false; 274 | stopSelf(); 275 | } 276 | } 277 | 278 | if (serversocket != null) { 279 | try { 280 | serversocket.close(); 281 | } catch (IOException e) { 282 | Log.e(TAG, "tcp - closing server socket "+e.getMessage()); 283 | } 284 | } 285 | Log.d(TAG,"tcp - end"); 286 | stopSelf(); 287 | } 288 | 289 | } 290 | 291 | class usbpollthread implements Runnable { 292 | 293 | 294 | public void run() { 295 | 296 | Log.d(TAG,"usb - run"); 297 | 298 | //Looper.prepare(); 299 | 300 | 301 | byte buf [] = new byte[16384]; 302 | int x; 303 | 304 | try { 305 | x=phoneInputStream.read(buf); 306 | Log.d(TAG, "usb -received from usb "+bytesToHex((Arrays.copyOf(buf, x)))); 307 | phoneOutputStream.write(new byte[]{0, 3, 0, 8, 0, 2, 0, 1, 0, 4, 0, 0}); 308 | //tcpreader.join(); 309 | usbCompleted = true; 310 | } catch (Exception e) { 311 | Log.e(TAG, "usb - error init "+e.getMessage()); 312 | running = false; 313 | stopSelf(); 314 | } 315 | 316 | if (!localCompleted && running) 317 | Log.d(TAG, "usb - waiting for local"); 318 | while (!localCompleted && running) { 319 | try { 320 | Thread.sleep(100); 321 | } catch (InterruptedException e) { 322 | Log.e(TAG, "usb - error sleeping "+e.getMessage()); 323 | } 324 | } 325 | 326 | while (running) 327 | { 328 | try { 329 | x = phoneInputStream.read(buf); 330 | processCarMessage(Arrays.copyOf(buf, x)); 331 | } 332 | catch (Exception e) 333 | { 334 | Log.e(TAG,"usb - in main loop " + e.getMessage()); 335 | running = false; 336 | stopSelf(); 337 | } 338 | 339 | } 340 | if (mFileDescriptor!=null) { 341 | try { 342 | mFileDescriptor.close(); 343 | } catch (IOException e) { 344 | Log.d(TAG, "error closing usb " + e.getMessage()); 345 | } 346 | } 347 | Log.d(TAG,"usb - end"); 348 | stopSelf(); 349 | } 350 | }; 351 | 352 | private void getLocalmessage(boolean canBeEmpty) throws IOException { 353 | 354 | 355 | int enc_len; 356 | socketinput.readFully(readbuffer,0,4); 357 | int pos=4; 358 | enc_len = (readbuffer[2] & 0xFF) << 8 | (readbuffer[3] & 0xFF); 359 | if ((int) readbuffer[1] == 9) //Flag 9 means the header is 8 bytes long (read it in a separate byte array) 360 | { 361 | pos+=4; 362 | socketinput.readFully(readbuffer,4,4); 363 | } 364 | 365 | socketinput.readFully(readbuffer,pos,enc_len); 366 | phoneOutputStream.write(Arrays.copyOf(readbuffer,enc_len+pos)); 367 | 368 | 369 | } 370 | 371 | private void processCarMessage(final byte[] buf) throws IOException { 372 | socketoutput.write(buf); 373 | } 374 | 375 | @Override 376 | public void onDestroy() { 377 | running=false; 378 | mNotificationManager.cancelAll(); 379 | Log.d(TAG,"service destroyed"); 380 | //android.os.Process.killProcess (android.os.Process.myPid ()); 381 | } 382 | 383 | private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); 384 | public static String bytesToHex(byte[] bytes) { 385 | char[] hexChars = new char[bytes.length * 2]; 386 | for ( int j = 0; j < bytes.length; j++ ) { 387 | int v = bytes[j] & 0xFF; 388 | hexChars[j * 2] = hexArray[v >>> 4]; 389 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 390 | } 391 | String aux = new String(hexChars); 392 | // Log.d("AAGateWay","ByteTohex: " + aux); 393 | return aux; 394 | } 395 | 396 | } 397 | -------------------------------------------------------------------------------- /app/src/main/java/uk/co/borconi/emil/aagateway/MainActivity.java: -------------------------------------------------------------------------------- 1 | package uk.co.borconi.emil.aagateway; 2 | 3 | 4 | import android.content.Intent; 5 | import android.content.SharedPreferences; 6 | import android.os.Bundle; 7 | import android.preference.PreferenceManager; 8 | import android.support.v4.content.ContextCompat; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.util.Log; 11 | import android.view.View; 12 | import android.widget.Button; 13 | import android.widget.CompoundButton; 14 | import android.widget.Switch; 15 | import android.widget.ToggleButton; 16 | 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | 21 | 22 | private static final String TAG = "AAGateWay"; 23 | private SharedPreferences preferences; 24 | 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_main); 30 | 31 | 32 | if (getIntent().getAction()!=null && getIntent().getAction().equalsIgnoreCase("android.intent.action.MAIN")) { 33 | 34 | preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); 35 | 36 | ToggleButton listenswitch = findViewById(R.id.swListening); 37 | listenswitch.setChecked(preferences.getBoolean(Preferences.LISTENING_MODE, true)); 38 | listenswitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 39 | @Override 40 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 41 | SharedPreferences.Editor editor = preferences.edit() 42 | .putBoolean(Preferences.LISTENING_MODE, isChecked); 43 | editor.commit(); 44 | } 45 | }); 46 | 47 | ToggleButton ignoreipv6switch = findViewById(R.id.swIpMode); 48 | ignoreipv6switch.setChecked(preferences.getBoolean(Preferences.IGNORE_IPV6, true)); 49 | ignoreipv6switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 50 | @Override 51 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 52 | SharedPreferences.Editor editor = preferences.edit() 53 | .putBoolean(Preferences.IGNORE_IPV6, isChecked); 54 | editor.commit(); 55 | } 56 | }); 57 | 58 | Button button = findViewById(R.id.exitButton); 59 | button.setOnClickListener(new View.OnClickListener() { 60 | @Override 61 | public void onClick(View view) { 62 | finish(); 63 | } 64 | }); 65 | } 66 | 67 | } 68 | 69 | @Override 70 | protected void onResume() { 71 | super.onResume(); 72 | 73 | Intent paramIntent = getIntent(); 74 | Intent i = new Intent(this, HackerService.class); 75 | 76 | if (paramIntent.getAction() != null && paramIntent.getAction().equalsIgnoreCase("android.hardware.usb.action.USB_ACCESSORY_DETACHED")) { 77 | Log.d(TAG, "USB DISCONNECTED"); 78 | stopService(i); 79 | finish(); 80 | } else if (paramIntent.getAction() != null && paramIntent.getAction().equalsIgnoreCase("android.hardware.usb.action.USB_ACCESSORY_ATTACHED")) { 81 | Log.d(TAG, "USB CONNECTED"); 82 | 83 | // findViewById(R.id.textView).setVisibility(View.VISIBLE); 84 | //((TextView)findViewById(R.id.textView)).setText(paramIntent.getParcelableExtra("accessory").toString()); 85 | 86 | 87 | if (paramIntent.getParcelableExtra("accessory") != null) { 88 | i.putExtra("accessory", paramIntent.getParcelableExtra("accessory")); 89 | ContextCompat.startForegroundService(this,i); 90 | } 91 | finish(); 92 | } 93 | } 94 | 95 | @Override 96 | protected void onNewIntent(Intent paramIntent) 97 | { 98 | Log.i(TAG, "Got new intent: " + paramIntent); 99 | super.onNewIntent(paramIntent); 100 | setIntent(paramIntent); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/uk/co/borconi/emil/aagateway/PowerConnectionReceiver.java: -------------------------------------------------------------------------------- 1 | package uk.co.borconi.emil.aagateway; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.util.Log; 7 | 8 | public class PowerConnectionReceiver extends BroadcastReceiver { 9 | private static final String TAG = "AAGateWay"; 10 | 11 | @Override 12 | public void onReceive(Context context, Intent intent) { 13 | Log.d(TAG, "power disconnected"); 14 | Intent nopowerintent = new Intent(context, HackerService.class); 15 | context.stopService(nopowerintent); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/uk/co/borconi/emil/aagateway/Preferences.java: -------------------------------------------------------------------------------- 1 | package uk.co.borconi.emil.aagateway; 2 | 3 | public class Preferences { 4 | public static final String LISTENING_MODE = "listening_mode"; 5 | public static final String IGNORE_IPV6 = "ignore_ipv6"; 6 | } 7 | -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi/libhu_jni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeeq/aagateway/02e89d112572f0612c6c66e22e8f24a1ff7dcb01/app/src/main/jniLibs/armeabi/libhu_jni.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/x86/libhu_jni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeeq/aagateway/02e89d112572f0612c6c66e22e8f24a1ff7dcb01/app/src/main/jniLibs/x86/libhu_jni.so -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/aawifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeeq/aagateway/02e89d112572f0612c6c66e22e8f24a1ff7dcb01/app/src/main/res/drawable/aawifi.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/hu_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeeq/aagateway/02e89d112572f0612c6c66e22e8f24a1ff7dcb01/app/src/main/res/drawable/hu_icon_256.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 13 | 14 | 23 | 24 | 31 | 32 | 35 | 36 | 43 | 44 |