Skip to content

Commit

Permalink
More Android 4.4 fixes, updated README
Browse files Browse the repository at this point in the history
  • Loading branch information
lukehasawii committed Apr 16, 2023
1 parent 9125a56 commit f8b002f
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 65 deletions.
81 changes: 42 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,71 +1,72 @@

# TxtNet Browser
### Browse the Web over SMS, no WiFi or Mobile Data required!
<p align="center"><img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/app/src/main/ic_launcher-playstore.png" alt="App Icon" width="200"/></p>

TextNet Browser is an Android app that allows anyone around the world to browse the web without a mobile data connection! It uses SMS as a medium of transmitting HTTP requests to a server where a pre-parsed HTML response is compressed using Google's [Brotli](https://github.com/google/brotli) compression algorithm and encoded using a custom Base-114 encoding format (based on [Basest](https://github.com/saxbophone/basest-python)).
<p align="center"><img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/app/src/main/ic_launcher-playstore.png" alt="App Icon" width="200"/></p>

## TxtNet Browser Alpha 2 has been released! Please note that this is still in early stages of development.
TextNet Browser is an Android app that allows anyone around the world to browse the web without a mobile data connection! It uses SMS as a medium of transmitting HTTP requests to a server where a pre-parsed HTML response is compressed using Google's [Brotli](https://github.com/google/brotli) compression algorithm and encoded using a custom Base-114 encoding format (based on [Basest](https://github.com/saxbophone/basest-python)).

> ⚠️**Please note**: All web traffic should be considered unencrypted, as all requests are made over SMS and received in plaintext by the server!
In addition, any user can act as a server using their own phone's primary phone number and a Wi-Fi/data connection at the press of a button, allowing for peer-to-peer distributed networks.

## Download
### See the **[releases page](https://github.com/lukeaschenbrenner/TxtNet-Browser/releases)** for an APK download of the TxtNet Browser client. A Google Play release is coming soon.

> ⚠️**Please note**: All web traffic should be considered unencrypted, as all requests are made over SMS and received in plaintext by the server!
## How it works

This app utilizes a permission present in Android since KitKat (4.4) which allows any app to read incoming SMS messages and send SMS messages without being your default SMS app. While this is a security concern, the code for this app is open source and does not use any internet permissions (because that's the whole point!)
This app utilizes a permission present in Android since KitKat (4.4) which allows any app to read incoming SMS messages and send SMS messages without being your default SMS app. While this is a security concern, the code for this app is open source and does not use any internet permissions (because that's the whole point!)
The app communicates with a "server phone number", which is a phone number controlled by a messaging API (in this case Twilio) that communicates over REST to the Python server script. Each URL request is sent, encoded in a custom base 114, to the server. Usually, this only requires 1 SMS, but just in case, each message is prepended with an order specifier. When the server receives a request, it uses Pyppeteer to request the website in a Chromium instance running on the server. This allows any Javascript that may exist to parse all HTML required. Once the page is loaded, only the HTML is transferred back to the recipient device. The HTML is stripped of unnecessary tags and attributes, compressed into raw bytes, and then encoded. Once encoded, the messages are split into 160 character numbered segments (maximizing the [GSM-7 standard](https://en.wikipedia.org/wiki/GSM_03.38) SMS size) and sent to the app to parse using the Twilio API.

Side note: Compression savings have been estimated to be an average of 20% using Brotli, but oftentimes it can save much more! For example, the website `example.com` in stripped HTML is 285 characters, but only requires 2 SMS messages (189 characters) to receive. Even including the 225% overhead in data transmission, it is still more efficient!

##### Why encode the HTML in the first place?
#### Why encode the HTML in the first place?
SMS was created in 1984, and was created to utilize the extra bytes from the data channels in phone signalling. It was originally conceived to only support 128 characters in a 7-bit alphabet. When further characters were required to support a subset of the UTF-8 character set, a new standard called UCS-2 was created. Still limited by the 160 bytes available, UCS-2 supports more characters (many of which show up in HTML documents) but limits SMS sizes to 70 characters per SMS. By encoding all data in GSM-7, more data can be sent per SMS message than sending the raw HTML over SMS. It is possible that it may be even more efficient to create an encoding system using all the characters available in UCS-2, but this limits compatibility and is out of the scope of the project.

## Server Hosting (alpha)
TxtNet Browser has been rewritten to include a built-in server hosting option inside the app. Instead of the now-deprecated Python server using a paid SMS API, any user can now act as a server host, allowing for distributed communication.
To enable the background service, tap on the overflow menu and select "TxtNet Server Hosting". Once the necessary permissions are granted, you can press on the "Start Service" toggle to initialize a background service.
TxtNet Server uses your primary mobile number associated with the active carrier subscription SIM as a number that others can add and connect to.
Please note that this feature is still in early stages of development and likely has many issues. Please submit issue reports for any problems you encounter.
TxtNet Browser has been rewritten to include a built-in server hosting option inside the app. Instead of the now-deprecated Python server using a paid SMS API, any user can now act as a server host, allowing for distributed communication.
To enable the background service, tap on the overflow menu and select "TxtNet Server Hosting". Once the necessary permissions are granted, you can press on the "Start Service" toggle to initialize a background service.
TxtNet Server uses your primary mobile number associated with the active carrier subscription SIM as a number that others can add and connect to.
Please note that this feature is still in early stages of development and likely has many issues. Please submit issue reports for any problems you encounter.
For Android 4.4-6.0, you will need to run adb commands one time as specified in the app. For Android 6.0-10.0, you may also use Skizuku, but a PC will still be required once. For Android 11+, no PC is required to activate the server using [Shizuku](https://shizuku.rikka.app/guide/setup/).

<strike>
Server Installation (Deprecated)

The current source code is pointed at my own server, using a Twilio API with credits I have purchased. If you would like to run your own server, follow the instructions below:
1. Register for an account at [Twilio](https://twilio.com/), purchase a toll-free number with SMS capability, and purchase credits. (This project will not work with Twilio free accounts)
2. Create a Twilio application for the number.
3. Sign up for an [ngrok](http://ngrok.com/) account and download the ngrok application
4. Open the ngrok directory and run this command: `./ngrok tcp 5000`
5. Visit the [active numbers](https://console.twilio.com/US1/develop/phone-numbers/manage/incoming) page and add the ngrok url to the "A Message Comes In" section after selecting "webhook". For example: "https://xyz.ngrok.io/receive_sms"
6. Download the TxtNet Browser [server script](https://github.com/lukeaschenbrenner/TxtNet-Browser/blob/master/SMS_Server_Twilio.py) and install all the required modules using "pip install x"
7. Add your Twilio API ID and Key into your environment variables, and run the script! `python3 ./SMS_Server_Twilio.py`
8. In the TxtNet Browser app, press the three dots and press "Change Server Phone Number". Enter in the phone number you purchased from Twilio and press OK!
</strike>
##### Desktop Server Installation (Deprecated)
<strike>
The current source code is pointed at my own server, using a Twilio API with credits I have purchased. If you would like to run your own server, follow the instructions below:
<strike>1. Register for an account at [Twilio](https://twilio.com/), purchase a toll-free number with SMS capability, and purchase credits. (This project will not work with Twilio free accounts)
2. Create a Twilio application for the number.
3. Sign up for an [ngrok](http://ngrok.com/) account and download the ngrok application
4. Open the ngrok directory and run this command: `./ngrok tcp 5000`
5. Visit the [active numbers](https://console.twilio.com/US1/develop/phone-numbers/manage/incoming) page and add the ngrok url to the "A Message Comes In" section after selecting "webhook". For example: "https://xyz.ngrok.io/receive_sms"
6. Download the TxtNet Browser [server script](https://github.com/lukeaschenbrenner/TxtNet-Browser/blob/master/SMS_Server_Twilio.py) and install all the required modules using "pip install x"
7. Add your Twilio API ID and Key into your environment variables, and run the script! `python3 ./SMS_Server_Twilio.py`
8. In the TxtNet Browser app, press the three dots and press "Change Server Phone Number". Enter in the phone number you purchased from Twilio and press OK!
</strike>

## FAQ/Troubleshooting

Bugs:
- Many carriers are unnecessarily rate limiting incoming text messages, so a page may look as though it "stalled" while loading on large pages. As of now the only way to fix this is to wait!
- In Android 12 (or possibly a new version of Google Messages?), there is a new and "improved" messages blocking feature. This results in no SMS messages getting through when a number is blocked, which makes the blocking feature of TxtNet Browser break the app! Instead of blocking messages, to get around this "feature", you can silent message notifications from the server phone number.
<img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/silentMessages.png" alt="Silence Number" width="200"/>
- In Android 12 (or possibly a new version of Google Messages?), there is a new and "improved" messages blocking feature. This results in no SMS messages getting through when a number is blocked, which makes the blocking feature of TxtNet Browser break the app! Instead of blocking messages, to get around this "feature", you can silent message notifications from the server phone number.
<img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/silentMessages.png" alt="Silence Number" width="200"/>

<img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/Messages_Migrating_Popup.png" alt="Contacts Popup" width="200"/>
<img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/Messages_Migrating_Popup.png" alt="Contacts Popup" width="200"/>

<img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/MigratingBlockedContacts.png" alt="Migrating Contacts" width="200"/>
- The app might randomly crash when attempting to relaunch it after some time. Please submit a PR if you know how to fix this!
<img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/MigratingBlockedContacts.png" alt="Migrating Contacts" width="200"/>
- The app might randomly crash when attempting to relaunch it after some time. Please submit a PR if you know how to fix this!

## Screenshots / Demo

<table>
<tr>
<td> <img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/screenshot1.png" alt="1" height = 640px ></td>
<td><img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/screenshot2.png" alt="2" height = 640px></td>
</tr>
<tr>
<td><img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/screenshot3.png" alt="3" height = 640px></td>
<td><img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/screenshot4.png" align="right" alt="4" height = 640px>
</td>
</tr>
<table>
<tr>
<td> <img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/screenshot1.png" alt="1" height = 640px ></td>
<td><img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/screenshot2.png" alt="2" height = 640px></td>
</tr>
<tr>
<td><img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/screenshot3.png" alt="3" height = 640px></td>
<td><img src="https://github.com/lukeaschenbrenner/TxtNet-Browser/raw/master/media/screenshot4.png" align="right" alt="4" height = 640px>
</td>
</tr>
</table>

##### Demo
Expand All @@ -77,6 +78,7 @@ https://user-images.githubusercontent.com/5207700/191133921-ee39c87a-c817-4dde-b

## Development

### 🚧 **If you are skilled in Android UI design, your help would be greatly appreciated!** 🚧 A consistent theme and dark mode would be great additions to this app.
Feel free to submit pull requests! I am a second-year CS student with basic knowledge of Android Development and Server Development, and greatly appreciate help and support from the community.

## Future Impact
Expand All @@ -92,5 +94,6 @@ GPLv3 - See LICENSE.md

## Credits

Thank you to everyone who has contributed to the libraries used by this app, especially Brotli and Basest. Special thanks goes to [Coldsauce](https://github.com/ColdSauce), whose original project [Cosmos Browser](https://github.com/ColdSauce/CosmosBrowserAndroid) was the original inspiration for this project!
Thank you to everyone who has contributed to the libraries used by this app, especially Brotli and Basest. Special thanks goes to [Coldsauce](https://github.com/ColdSauce), whose original project [Cosmos Browser](https://github.com/ColdSauce/CosmosBrowserAndroid) was the original inspiration for this project!
My original reply to his Hacker News comment is [here](https://news.ycombinator.com/item?id=30685223#30687202).
In addition, I would like to thank [Zachary Wander](https://www.xda-developers.com/implementing-shizuku/) from XDA for their excellent Shizuku implementation tutorial and [Aayush Atharva](https://github.com/hyperxpro/Brotli4j/) for the amazing foundation they created with Brotli4J, allowing for a streamlined forking process to create the library BrotliDroid used in this app.
11 changes: 1 addition & 10 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,14 @@
android:supportsRtl="true"
android:usesCleartextTraffic="true">
<activity
android:name=".ShizukuIncompatible"
android:name=".blockingactivities.ShizukuIncompatible"
android:exported="false"
android:label="@string/title_activity_shizuku_incompatible"
android:theme="@style/Theme.TxtNetBrowser.NoActionBar">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".ShizukuIncompatibleActivity"
android:exported="false"
android:label="@string/title_activity_shizuku_incompatible"
android:theme="@style/Theme.TxtNetBrowser">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".phonenumbers.NewServerActivity"
android:exported="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,6 @@ public void onReceive(Context context, Intent intent) {
//
// }
// /* ----------------------------------*/

sms.sendTextMessage(outputNumber, null, body, null, null);

}else if(Message.toString().contains("Website Cancel")){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.txtnet.txtnetbrowser.Constants;
import com.txtnet.txtnetbrowser.R;
import com.txtnet.txtnetbrowser.blockingactivities.ShizukuIncompatible;
import com.txtnet.txtnetbrowser.blockingactivities.UnsupportedDeviceActivity;

import org.lsposed.hiddenapibypass.HiddenApiBypass;

Expand Down Expand Up @@ -86,8 +87,10 @@ protected void onCreate(Bundle savedInstanceState) {
) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.POST_NOTIFICATIONS}, 100);
}


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
Intent intent = new Intent(this, UnsupportedDeviceActivity.class);
startActivity(intent);
}
createNotificationChannel(this);

SwitchCompat serverSwitch = (SwitchCompat) findViewById(R.id.startServiceSwitch);
Expand Down Expand Up @@ -120,10 +123,9 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
// start service
// It turns out that SMS_OUTGOING_CHECK_MAX_COUNT and SMS_OUTGOING_CHECK_MAX_INTERVAL_MS are no longer secure settings as of 9/14/2012 ( https://cs.android.com/android/_/android/platform/frameworks/opt/telephony/+/3ca3a570c0ad836dc42378e4359dbf28c6ef71db:src/java/com/android/internal/telephony/SmsUsageMonitor.java;l=258;bpv=1;bpt=0;drc=4658a1a8c23111d5cc89feb040ce547a7b65dfb0;dlc=c38bb60d867c5d61d90b7179a9ed2b2d1848124f )
// still, just the WRITE_SETTINGS permission DOES NOT allow us to write global settings.
if(ContextCompat.checkSelfPermission(compoundButton.getContext(), Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_DENIED)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(compoundButton.getContext(), Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_DENIED){
Log.e("perm", "No permission!");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
boolean isGranted;
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
isGranted = checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED;
Expand Down Expand Up @@ -156,9 +158,10 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

//TODO: Add Instructions for manual ADB on Android 4.4-6 (cant use shizuku)
}
serverSwitch.setChecked(false);
}
else{ // permission granted, let's roll
if(ContextCompat.checkSelfPermission(compoundButton.getContext(), Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_DENIED){
else{ // permission granted on Android 6+, or we assume that Android 4.4-5.1 manually changed settings in adb
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(compoundButton.getContext(), Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_DENIED){
grantPermissions();
}

Expand Down Expand Up @@ -328,10 +331,11 @@ public void grantPermissions(){
boolean put1 = Settings.Global.putInt(getContentResolver(), "sms_outgoing_check_max_count", 1_000_000); // 1 million SMS messages every
boolean put2 = Settings.Global.putInt(getContentResolver(), "sms_outgoing_check_interval_ms", 30000); // 30 seconds
// Something tells me we won't hit this limit.
Toast.makeText(this, "Done. You may need to reboot. This should only be done once.", Toast.LENGTH_LONG).show();
Toast.makeText(this, "Done. You may need to restart server. This should only be done once.", Toast.LENGTH_LONG).show();
Log.e("put1", String.valueOf(put1));
Log.e("put2", String.valueOf(put2));


}catch(SecurityException se){
Toast.makeText(this, "Permission WRITE_SECURE_SETTINGS not obtained!", Toast.LENGTH_LONG).show();

Expand Down
Loading

0 comments on commit f8b002f

Please sign in to comment.