|
@ -1,17 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="deploymentTargetDropDown">
|
|
||||||
<targetSelectedWithDropDown>
|
|
||||||
<Target>
|
|
||||||
<type value="QUICK_BOOT_TARGET" />
|
|
||||||
<deviceKey>
|
|
||||||
<Key>
|
|
||||||
<type value="VIRTUAL_DEVICE_PATH" />
|
|
||||||
<value value="C:\Users\msman\.android\avd\Pixel_2_API_33.avd" />
|
|
||||||
</Key>
|
|
||||||
</deviceKey>
|
|
||||||
</Target>
|
|
||||||
</targetSelectedWithDropDown>
|
|
||||||
<timeTargetWasSelectedWithDropDown value="2022-08-21T18:03:56.258272500Z" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -78,6 +78,7 @@
|
||||||
<entry key="..\:/Workspace/Genesis-Android/app/src/main/res/layouts/tab/layout/tab_menu.xml" value="0.16875" />
|
<entry key="..\:/Workspace/Genesis-Android/app/src/main/res/layouts/tab/layout/tab_menu.xml" value="0.16875" />
|
||||||
<entry key="..\:/Workspace/Genesis-Android/app/src/main/res/layouts/tab/layout/tab_row_view.xml" value="0.5" />
|
<entry key="..\:/Workspace/Genesis-Android/app/src/main/res/layouts/tab/layout/tab_row_view.xml" value="0.5" />
|
||||||
<entry key="..\:/Workspace/Genesis-Android/app/src/main/res/layouts/tab/layout/tab_view.xml" value="0.203125" />
|
<entry key="..\:/Workspace/Genesis-Android/app/src/main/res/layouts/tab/layout/tab_view.xml" value="0.203125" />
|
||||||
|
<entry key="..\:/Workspace/Genesis-Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.1935" />
|
||||||
<entry key="..\:/workspace/Genesis-Android/app/src/main/res/layouts/alert/layout/certificate_info.xml" value="0.18541666666666667" />
|
<entry key="..\:/workspace/Genesis-Android/app/src/main/res/layouts/alert/layout/certificate_info.xml" value="0.18541666666666667" />
|
||||||
<entry key="..\:/workspace/Genesis-Android/app/src/main/res/layouts/alert/layout/popup_bridge_mail.xml" value="0.5" />
|
<entry key="..\:/workspace/Genesis-Android/app/src/main/res/layouts/alert/layout/popup_bridge_mail.xml" value="0.5" />
|
||||||
<entry key="..\:/workspace/Genesis-Android/app/src/main/res/layouts/alert/layout/popup_bridge_setting_custom.xml" value="0.5" />
|
<entry key="..\:/workspace/Genesis-Android/app/src/main/res/layouts/alert/layout/popup_bridge_setting_custom.xml" value="0.5" />
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import com.android.build.OutputFile
|
//import com.android.build.OutputFile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "com.jetbrains.python.envs" version "0.0.26"
|
id "com.jetbrains.python.envs" version "0.0.26"
|
||||||
|
@ -110,14 +110,18 @@ dependencies {
|
||||||
|
|
||||||
implementation 'com.applovin:applovin-sdk:+'
|
implementation 'com.applovin:applovin-sdk:+'
|
||||||
implementation 'com.applovin.mediation:mytarget-adapter:+'
|
implementation 'com.applovin.mediation:mytarget-adapter:+'
|
||||||
|
implementation 'com.applovin.mediation:adcolony-adapter:4.8.0.2'
|
||||||
implementation 'com.adcolony:sdk:4.8.0'
|
implementation 'com.adcolony:sdk:4.8.0'
|
||||||
|
implementation 'com.facebook.android:audience-network-sdk:6.8.0'
|
||||||
|
implementation 'com.applovin.mediation:facebook-adapter:6.8.0.12'
|
||||||
|
|
||||||
/* Orbot Service */
|
/* Orbot Service */
|
||||||
|
|
||||||
implementation project(path: ':orbotmanager')
|
implementation project(path: ':orbotmanager')
|
||||||
|
|
||||||
/* Helper Libraries */
|
/* Helper Libraries */
|
||||||
|
|
||||||
|
implementation "android.arch.lifecycle:extensions:1.1.0"
|
||||||
|
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
|
||||||
implementation 'com.coolerfall:android-http-download-manager:1.6.3'
|
implementation 'com.coolerfall:android-http-download-manager:1.6.3'
|
||||||
implementation 'com.android.volley:volley:1.2.1'
|
implementation 'com.android.volley:volley:1.2.1'
|
||||||
implementation "net.zetetic:android-database-sqlcipher:4.4.3"
|
implementation "net.zetetic:android-database-sqlcipher:4.4.3"
|
||||||
|
@ -129,7 +133,7 @@ dependencies {
|
||||||
|
|
||||||
/* Automated APK Generation */
|
/* Automated APK Generation */
|
||||||
|
|
||||||
android.applicationVariants.all { variant ->
|
/*android.applicationVariants.all { variant ->
|
||||||
|
|
||||||
def buildType = variant.buildType.name
|
def buildType = variant.buildType.name
|
||||||
|
|
||||||
|
@ -153,7 +157,7 @@ android.applicationVariants.all { variant ->
|
||||||
output.versionCodeOverride = versionCodeOverride
|
output.versionCodeOverride = versionCodeOverride
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||||
android:extractNativeLibs="true"
|
android:extractNativeLibs="false"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:largeHeap="false"
|
android:largeHeap="false"
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="false"
|
||||||
tools:targetApi="n">
|
tools:targetApi="n">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
|
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 44 KiB |
|
@ -49,6 +49,8 @@ import androidx.core.app.NotificationManagerCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.widget.NestedScrollView;
|
import androidx.core.widget.NestedScrollView;
|
||||||
import androidx.fragment.app.FragmentContainerView;
|
import androidx.fragment.app.FragmentContainerView;
|
||||||
|
import androidx.lifecycle.Lifecycle;
|
||||||
|
import androidx.lifecycle.OnLifecycleEvent;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
@ -1096,7 +1098,7 @@ public class homeController extends AppCompatActivity implements ComponentCallba
|
||||||
msearchstatuscopy = true;
|
msearchstatuscopy = true;
|
||||||
mSearchBarWasBackButtonPressed = false;
|
mSearchBarWasBackButtonPressed = false;
|
||||||
if (!isFocusChanging) {
|
if (!isFocusChanging) {
|
||||||
if (!status.mThemeApplying) {
|
if (!status.mThemeApplying && mGeckoClient!=null && mGeckoClient.getSession()!=null) {
|
||||||
mHomeViewController.initSearchBarFocus(true, isKeyboardOpened);
|
mHomeViewController.initSearchBarFocus(true, isKeyboardOpened);
|
||||||
mHomeViewController.onUpdateSearchBar(mGeckoClient.getSession().getCurrentURL(), true, true, false);
|
mHomeViewController.onUpdateSearchBar(mGeckoClient.getSession().getCurrentURL(), true, true, false);
|
||||||
}
|
}
|
||||||
|
@ -1740,6 +1742,17 @@ public class homeController extends AppCompatActivity implements ComponentCallba
|
||||||
|
|
||||||
static boolean mStateService = false;
|
static boolean mStateService = false;
|
||||||
|
|
||||||
|
static boolean mBackground = false;
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
|
||||||
|
public void onAppBackgrounded() {
|
||||||
|
mBackground = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_START)
|
||||||
|
public void onAppForegrounded() {
|
||||||
|
mBackground = false;
|
||||||
|
}
|
||||||
|
|
||||||
public void onStartApplication(View view) {
|
public void onStartApplication(View view) {
|
||||||
if (!mStateService) {
|
if (!mStateService) {
|
||||||
mStateService = true;
|
mStateService = true;
|
||||||
|
@ -1747,7 +1760,9 @@ public class homeController extends AppCompatActivity implements ComponentCallba
|
||||||
if (!isMyServiceRunning(activityStateManager.class)) {
|
if (!isMyServiceRunning(activityStateManager.class)) {
|
||||||
new Handler().postDelayed(() ->
|
new Handler().postDelayed(() ->
|
||||||
{
|
{
|
||||||
|
if(!mBackground){
|
||||||
startService(new Intent(this, activityStateManager.class));
|
startService(new Intent(this, activityStateManager.class));
|
||||||
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
}catch (Exception ex){
|
}catch (Exception ex){
|
||||||
|
|
|
@ -243,6 +243,9 @@ public class tabController extends Fragment {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int orientation = activityContextManager.getInstance().getHomeController().getResources().getConfiguration().orientation;
|
int orientation = activityContextManager.getInstance().getHomeController().getResources().getConfiguration().orientation;
|
||||||
|
if (mRecycleView == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||||
maxScroll = mRecycleView.computeVerticalScrollRange() - mScreenHeight * 0.350f + helperMethod.pxFromDp(helperMethod.getNavigationBarSize(getContext()).y);
|
maxScroll = mRecycleView.computeVerticalScrollRange() - mScreenHeight * 0.350f + helperMethod.pxFromDp(helperMethod.getNavigationBarSize(getContext()).y);
|
||||||
} else {
|
} else {
|
||||||
|
@ -258,7 +261,6 @@ public class tabController extends Fragment {
|
||||||
mScrolled = false;
|
mScrolled = false;
|
||||||
if (mRecycleView.getChildAt(mRecycleView.getChildCount() - 1) != null) {
|
if (mRecycleView.getChildAt(mRecycleView.getChildCount() - 1) != null) {
|
||||||
if ((scrollY >= (mRecycleView.getChildAt(mRecycleView.getChildCount() - 1).getMeasuredHeight() - mRecycleView.getMeasuredHeight())) && scrollY > oldScrollY) {
|
if ((scrollY >= (mRecycleView.getChildAt(mRecycleView.getChildCount() - 1).getMeasuredHeight() - mRecycleView.getMeasuredHeight())) && scrollY > oldScrollY) {
|
||||||
Log.i("FUCK2:::::::", scrollY + "");
|
|
||||||
onSwipeBounce(0);
|
onSwipeBounce(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,20 @@ package com.hiddenservices.onionservices.pluginManager.adPluginManager;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
import com.adcolony.sdk.AdColony;
|
import com.adcolony.sdk.AdColony;
|
||||||
import com.adcolony.sdk.AdColonyAppOptions;
|
import com.adcolony.sdk.AdColonyAppOptions;
|
||||||
import com.applovin.mediation.MaxAd;
|
import com.applovin.mediation.MaxAd;
|
||||||
import com.applovin.mediation.MaxAdViewAdListener;
|
import com.applovin.mediation.MaxAdViewAdListener;
|
||||||
import com.applovin.mediation.MaxError;
|
import com.applovin.mediation.MaxError;
|
||||||
|
import com.applovin.mediation.adapters.AdColonyMediationAdapter;
|
||||||
import com.applovin.mediation.ads.MaxAdView;
|
import com.applovin.mediation.ads.MaxAdView;
|
||||||
import com.applovin.sdk.AppLovinSdk;
|
import com.applovin.sdk.AppLovinSdk;
|
||||||
|
import com.facebook.ads.AdSettings;
|
||||||
import com.hiddenservices.onionservices.appManager.activityContextManager;
|
import com.hiddenservices.onionservices.appManager.activityContextManager;
|
||||||
import com.hiddenservices.onionservices.eventObserver;
|
import com.hiddenservices.onionservices.eventObserver;
|
||||||
import com.hiddenservices.onionservices.pluginManager.pluginEnums;
|
import com.hiddenservices.onionservices.pluginManager.pluginEnums;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
import static com.hiddenservices.onionservices.pluginManager.pluginEnums.eAdManagerCallbacks.M_ON_AD_LOAD;
|
import static com.hiddenservices.onionservices.pluginManager.pluginEnums.eAdManagerCallbacks.M_ON_AD_LOAD;
|
||||||
|
|
||||||
public class appLovinManager implements MaxAdViewAdListener {
|
public class appLovinManager implements MaxAdViewAdListener {
|
||||||
|
@ -26,6 +26,7 @@ public class appLovinManager implements MaxAdViewAdListener {
|
||||||
|
|
||||||
private int mRequestCount = 0;
|
private int mRequestCount = 0;
|
||||||
private boolean bannerAdRequested = false;
|
private boolean bannerAdRequested = false;
|
||||||
|
private boolean bannerAdsLoaded = false;
|
||||||
|
|
||||||
/*Initializations*/
|
/*Initializations*/
|
||||||
|
|
||||||
|
@ -36,7 +37,10 @@ public class appLovinManager implements MaxAdViewAdListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeBannerAds(Context pContext) {
|
private void initializeBannerAds(Context pContext) {
|
||||||
|
AdSettings.setDataProcessingOptions(new String[]{});
|
||||||
AdColonyAppOptions appOptions = new AdColonyAppOptions();
|
AdColonyAppOptions appOptions = new AdColonyAppOptions();
|
||||||
|
appOptions.setPrivacyFrameworkRequired(AdColonyAppOptions.GDPR, true);
|
||||||
|
appOptions.setPrivacyConsentString(AdColonyAppOptions.GDPR, "1");
|
||||||
AdColony.configure(activityContextManager.getInstance().getHomeController(), appOptions,"app3b56c67c45544c5c89");
|
AdColony.configure(activityContextManager.getInstance().getHomeController(), appOptions,"app3b56c67c45544c5c89");
|
||||||
|
|
||||||
AppLovinSdk.getInstance(pContext).setMediationProvider("max");
|
AppLovinSdk.getInstance(pContext).setMediationProvider("max");
|
||||||
|
@ -55,7 +59,7 @@ public class appLovinManager implements MaxAdViewAdListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAdvertLoaded() {
|
private boolean isAdvertLoaded() {
|
||||||
return false;
|
return bannerAdsLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Overriden Methods */
|
/* Overriden Methods */
|
||||||
|
@ -72,12 +76,13 @@ public class appLovinManager implements MaxAdViewAdListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAdLoaded(MaxAd ad) {
|
public void onAdLoaded(MaxAd ad) {
|
||||||
|
bannerAdsLoaded = true;
|
||||||
mEvent.invokeObserver(null, M_ON_AD_LOAD);
|
mEvent.invokeObserver(null, M_ON_AD_LOAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAdDisplayed(MaxAd ad) {
|
public void onAdDisplayed(MaxAd ad) {
|
||||||
|
Log.i("ads load", "ad has been loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector
|
||||||
|
android:height="108dp"
|
||||||
|
android:width="108dp"
|
||||||
|
android:viewportHeight="108"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
</vector>
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 836 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 9.8 KiB |
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 18 KiB |
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background">#3DDC84</color>
|
<color name="ic_launcher_background">#FF6A47</color>
|
||||||
</resources>
|
</resources>
|
|
@ -1,6 +1,6 @@
|
||||||
/* Version */
|
/* Version */
|
||||||
project.ext.vname = 'Build | Dark-Origin 1.0.5.6'
|
project.ext.vname = 'Build | Dark-Origin 1.0.5.7'
|
||||||
project.ext.vcode = 405
|
project.ext.vcode = 410
|
||||||
project.ext.buildType = 'release'
|
project.ext.buildType = 'release'
|
||||||
|
|
||||||
/* dimension */
|
/* dimension */
|
||||||
|
|
|
@ -76,6 +76,7 @@ public class OrbotRawEventListener implements RawEventListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBandwidth(long read, long written) {
|
private void handleBandwidth(long read, long written) {
|
||||||
|
try {
|
||||||
String message = OrbotService.formatBandwidthCount(mService, read) + " \u2193" + " / " +
|
String message = OrbotService.formatBandwidthCount(mService, read) + " \u2193" + " / " +
|
||||||
OrbotService.formatBandwidthCount(mService, written) + " \u2191";
|
OrbotService.formatBandwidthCount(mService, written) + " \u2191";
|
||||||
|
|
||||||
|
@ -86,7 +87,7 @@ public class OrbotRawEventListener implements RawEventListener {
|
||||||
mTotalBandwidthRead += read;
|
mTotalBandwidthRead += read;
|
||||||
|
|
||||||
mService.sendCallbackBandwidth(written, read, mTotalBandwidthWritten, mTotalBandwidthRead);
|
mService.sendCallbackBandwidth(written, read, mTotalBandwidthWritten, mTotalBandwidthRead);
|
||||||
|
}catch (Exception ex){}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleNewDescriptors(String[] descriptors) {
|
private void handleNewDescriptors(String[] descriptors) {
|
||||||
|
|
|
@ -746,9 +746,13 @@ public class OrbotService extends VpnService implements OrbotConstants {
|
||||||
|
|
||||||
debug("torrc.custom=" + extraLines);
|
debug("torrc.custom=" + extraLines);
|
||||||
|
|
||||||
|
try {
|
||||||
File fileTorRcCustom = TorService.getTorrc(this);
|
File fileTorRcCustom = TorService.getTorrc(this);
|
||||||
updateTorConfigCustom(fileTorRcCustom, extraLines.toString());
|
updateTorConfigCustom(fileTorRcCustom, extraLines.toString());
|
||||||
return fileTorRcCustom;
|
return fileTorRcCustom;
|
||||||
|
|
||||||
|
}catch (Exception ex){}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String checkPortOrAuto(String portString) {
|
private String checkPortOrAuto(String portString) {
|
||||||
|
@ -875,10 +879,12 @@ public class OrbotService extends VpnService implements OrbotConstants {
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void startTorService() throws Exception {
|
private synchronized void startTorService() throws Exception {
|
||||||
|
try {
|
||||||
updateTorConfigCustom(TorService.getDefaultsTorrc(this),
|
updateTorConfigCustom(TorService.getDefaultsTorrc(this),
|
||||||
"DNSPort 0\n" +
|
"DNSPort 0\n" +
|
||||||
"TransPort 0\n" +
|
"TransPort 0\n" +
|
||||||
"DisableNetwork 1\n");
|
"DisableNetwork 1\n");
|
||||||
|
}catch (Exception ex){}
|
||||||
|
|
||||||
var fileTorrcCustom = updateTorrcCustomFile();
|
var fileTorrcCustom = updateTorrcCustomFile();
|
||||||
if ((!fileTorrcCustom.exists()) || (!fileTorrcCustom.canRead()))
|
if ((!fileTorrcCustom.exists()) || (!fileTorrcCustom.canRead()))
|
||||||
|
@ -892,6 +898,7 @@ public class OrbotService extends VpnService implements OrbotConstants {
|
||||||
|
|
||||||
new Thread(){
|
new Thread(){
|
||||||
public void run(){
|
public void run(){
|
||||||
|
try {
|
||||||
TorService torService = ((TorService.LocalBinder) iBinder).getService();
|
TorService torService = ((TorService.LocalBinder) iBinder).getService();
|
||||||
|
|
||||||
while ((conn = torService.getTorControlConnection())==null)
|
while ((conn = torService.getTorControlConnection())==null)
|
||||||
|
@ -936,6 +943,7 @@ public class OrbotService extends VpnService implements OrbotConstants {
|
||||||
|
|
||||||
initControlConnection();
|
initControlConnection();
|
||||||
}
|
}
|
||||||
|
}catch (Exception ex){}
|
||||||
}
|
}
|
||||||
}.start();
|
}.start();
|
||||||
//moved torService to a local variable, since we only need it once
|
//moved torService to a local variable, since we only need it once
|
||||||
|
@ -960,12 +968,14 @@ public class OrbotService extends VpnService implements OrbotConstants {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
Intent serviceIntent = new Intent(this, TorService.class);
|
Intent serviceIntent = new Intent(this, TorService.class);
|
||||||
if (Build.VERSION.SDK_INT < 29) {
|
if (Build.VERSION.SDK_INT < 29) {
|
||||||
shouldUnbindTorService = bindService(serviceIntent, torServiceConnection, BIND_AUTO_CREATE);
|
shouldUnbindTorService = bindService(serviceIntent, torServiceConnection, BIND_AUTO_CREATE);
|
||||||
} else {
|
} else {
|
||||||
shouldUnbindTorService = bindService(serviceIntent, BIND_AUTO_CREATE, mExecutor, torServiceConnection);
|
shouldUnbindTorService = bindService(serviceIntent, BIND_AUTO_CREATE, mExecutor, torServiceConnection);
|
||||||
}
|
}
|
||||||
|
}catch (Exception ex){}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendLocalStatusOffBroadcast() {
|
private void sendLocalStatusOffBroadcast() {
|
||||||
|
|
|
@ -0,0 +1,501 @@
|
||||||
|
package org.torproject.android.service;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.FileObserver;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import net.freehaven.tor.control.RawEventListener;
|
||||||
|
import net.freehaven.tor.control.TorControlCommands;
|
||||||
|
import net.freehaven.tor.control.TorControlConnection;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Service} that runs Tor. To control Tor via the {@code ControlPort},
|
||||||
|
* first bind to this using {@link #bindService(Intent, android.content.ServiceConnection, int)},
|
||||||
|
* then use {@link #getTorControlConnection()} to get an instance of
|
||||||
|
* {@link TorControlConnection} from {@code jtorctl}. If
|
||||||
|
* {@link TorControlCommands#EVENT_CIRCUIT_STATUS} is not included in
|
||||||
|
* {@link TorControlConnection#setEvents(java.util.List)}, then this service
|
||||||
|
* will not be able to function properly since it relies on those events to
|
||||||
|
* detect the state of Tor.
|
||||||
|
*/
|
||||||
|
public class TorServiceCustom extends Service {
|
||||||
|
|
||||||
|
public static final String TAG = "TorService";
|
||||||
|
|
||||||
|
public static final String VERSION_NAME = org.torproject.jni.BuildConfig.VERSION_NAME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request to transparently start Tor services.
|
||||||
|
*/
|
||||||
|
public static final String ACTION_START = "org.torproject.android.intent.action.START";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal request to stop Tor services.
|
||||||
|
*/
|
||||||
|
private static final String ACTION_STOP = "org.torproject.android.intent.action.STOP";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Intent} sent by this app with {@code ON/OFF/STARTING/STOPPING} status
|
||||||
|
* included as an {@link #EXTRA_STATUS} {@code String}. Your app should
|
||||||
|
* always receive {@code ACTION_STATUS Intent}s since any other app could
|
||||||
|
* start Orbot. Also, user-triggered starts and stops will also cause
|
||||||
|
* {@code ACTION_STATUS Intent}s to be broadcast.
|
||||||
|
*/
|
||||||
|
public static final String ACTION_STATUS = "org.torproject.android.intent.action.STATUS";
|
||||||
|
|
||||||
|
public static final String ACTION_ERROR = "org.torproject.android.intent.action.ERROR";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code String} that contains a status constant: {@link #STATUS_ON},
|
||||||
|
* {@link #STATUS_OFF}, {@link #STATUS_STARTING}, or
|
||||||
|
* {@link #STATUS_STOPPING}
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_STATUS = "org.torproject.android.intent.extra.STATUS";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link String} {@code packageName} for {@code TorService} to direct its
|
||||||
|
* status reply to, after receiving an {@link #ACTION_START},
|
||||||
|
* {@link #ACTION_STOP}, or {@link #ACTION_STATUS} {@link Intent}. This allows
|
||||||
|
* {@code TorService} to send redundant replies to that single app, rather than
|
||||||
|
* broadcasting to all apps after every request.
|
||||||
|
*/
|
||||||
|
public final static String EXTRA_PACKAGE_NAME = "org.torproject.android.intent.extra.PACKAGE_NAME";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link String} {@code packageName} of the app to which this {@code TorService} belongs.
|
||||||
|
* This allows broadcast receivers to distinguish between broadcasts from different apps that
|
||||||
|
* use {@code TorService}.
|
||||||
|
*/
|
||||||
|
public final static String EXTRA_SERVICE_PACKAGE_NAME = "org.torproject.android.intent.extra.SERVICE_PACKAGE_NAME";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All tor-related services and daemons are stopped
|
||||||
|
*/
|
||||||
|
public static final String STATUS_OFF = "OFF";
|
||||||
|
/**
|
||||||
|
* All tor-related services and daemons have completed starting
|
||||||
|
*/
|
||||||
|
public static final String STATUS_ON = "ON";
|
||||||
|
public static final String STATUS_STARTING = "STARTING";
|
||||||
|
public static final String STATUS_STOPPING = "STOPPING";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a {@link File} pointing to the location of the optional
|
||||||
|
* {@code torrc} file.
|
||||||
|
* @see <a href="https://www.torproject.org/docs/tor-manual.html#_the_configuration_file_format">Tor configuration file format</a>
|
||||||
|
*/
|
||||||
|
public static File getTorrc(Context context) {
|
||||||
|
return new File(getAppTorServiceDir(context), "torrc");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a {@link File} pointing to the location of the optional
|
||||||
|
* {@code torrc-defaults} file.
|
||||||
|
* @see <a href="https://www.torproject.org/docs/tor-manual.html#_the_configuration_file_format">Tor configuration file format</a>
|
||||||
|
*/
|
||||||
|
public static File getDefaultsTorrc(Context context) {
|
||||||
|
return new File(getAppTorServiceDir(context), "torrc-defaults");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File getControlSocket(Context context) {
|
||||||
|
if (controlSocket == null) {
|
||||||
|
controlSocket = new File(getAppTorServiceDataDir(context), CONTROL_SOCKET_NAME);
|
||||||
|
}
|
||||||
|
return controlSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the directory that {@link TorServiceCustom} uses for:
|
||||||
|
* <ul>
|
||||||
|
* <li>writing {@code ControlPort.txt} // TODO
|
||||||
|
* <li>reading {@code torrc} and {@code torrc-defaults}
|
||||||
|
* <li>{@code DataDirectory} and {@code CacheDirectory}
|
||||||
|
* <li>the debug log file
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
private static File getAppTorServiceDir(Context context) {
|
||||||
|
if (appTorServiceDir == null) {
|
||||||
|
appTorServiceDir = context.getDir(TorServiceCustom.class.getSimpleName(), MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
return appTorServiceDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tor stores private, internal data in this directory.
|
||||||
|
*/
|
||||||
|
private static File getAppTorServiceDataDir(Context context) {
|
||||||
|
File dir = new File(getAppTorServiceDir(context), "data");
|
||||||
|
dir.mkdir();
|
||||||
|
if (!(dir.setReadable(true, true) && dir.setWritable(true, true) && dir.setExecutable(true, true))) {
|
||||||
|
throw new IllegalStateException("Cannot create " + dir);
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.loadLibrary("tor");
|
||||||
|
}
|
||||||
|
|
||||||
|
volatile static String currentStatus = STATUS_OFF;
|
||||||
|
|
||||||
|
private static File appTorServiceDir = null;
|
||||||
|
private static File controlSocket = null;
|
||||||
|
private static final String CONTROL_SOCKET_NAME = "ControlSocket";
|
||||||
|
|
||||||
|
public static int socksPort = -1;
|
||||||
|
public static int httpTunnelPort = -1;
|
||||||
|
|
||||||
|
// Store the opaque reference as a long (pointer) for the native code
|
||||||
|
private long torConfiguration = -1;
|
||||||
|
private int torControlFd = -1;
|
||||||
|
|
||||||
|
private TorControlConnection torControlConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This lock must be acquired before calling createTorConfiguration() and
|
||||||
|
* held until mainConfigurationFree() has been called.
|
||||||
|
*/
|
||||||
|
private static final ReentrantLock runLock = new ReentrantLock();
|
||||||
|
|
||||||
|
private native String apiGetProviderVersion();
|
||||||
|
|
||||||
|
private native boolean createTorConfiguration();
|
||||||
|
|
||||||
|
private native void mainConfigurationFree();
|
||||||
|
|
||||||
|
private native static FileDescriptor prepareFileDescriptor(String path);
|
||||||
|
|
||||||
|
private native boolean mainConfigurationSetCommandLine(String[] args);
|
||||||
|
|
||||||
|
private native boolean mainConfigurationSetupControlSocket();
|
||||||
|
|
||||||
|
private native int runMain();
|
||||||
|
|
||||||
|
|
||||||
|
public class LocalBinder extends Binder {
|
||||||
|
public TorServiceCustom getService() {
|
||||||
|
return TorServiceCustom.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final IBinder binder = new LocalBinder();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
// TODO send broadcastStatus() here?
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
broadcastStatus(this, STATUS_STARTING);
|
||||||
|
startTorServiceThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
sendBroadcastStatusIntent(this);
|
||||||
|
return super.onStartCommand(intent, flags, startId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Announce Tor is available for connections once the first circuit is complete
|
||||||
|
*/
|
||||||
|
private final RawEventListener startedEventListener = new RawEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onEvent(String keyword, String data) {
|
||||||
|
if (TorServiceCustom.STATUS_STARTING.equals(TorServiceCustom.currentStatus)
|
||||||
|
&& TorControlCommands.EVENT_CIRCUIT_STATUS.equals(keyword)
|
||||||
|
&& data != null && data.length() > 0) {
|
||||||
|
String[] tokenArray = data.split(" ");
|
||||||
|
if (tokenArray.length > 1 && TorControlCommands.CIRC_EVENT_BUILT.equals(tokenArray[1])) {
|
||||||
|
TorServiceCustom.broadcastStatus(TorServiceCustom.this, TorServiceCustom.STATUS_ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This waits for {@link #CONTROL_SOCKET_NAME} to be created by {@code tor},
|
||||||
|
* then continues on to connect to the {@code ControlSocket} as described in
|
||||||
|
* {@link #getControlSocket(Context)}. As a failsafe, this will only wait
|
||||||
|
* 10 seconds, after that it will check whether the {@code ControlSocket}
|
||||||
|
* file exists, and if not, throw a {@link IllegalStateException}.
|
||||||
|
*/
|
||||||
|
private Thread controlPortThread = new Thread(CONTROL_SOCKET_NAME) {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||||
|
try {
|
||||||
|
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
|
final String observeDir = getAppTorServiceDataDir(TorServiceCustom.this).getAbsolutePath();
|
||||||
|
FileObserver controlPortFileObserver = new FileObserver(observeDir) {
|
||||||
|
@Override
|
||||||
|
public void onEvent(int event, @Nullable String name) {
|
||||||
|
if ((event & FileObserver.CREATE) > 0 && CONTROL_SOCKET_NAME.equals(name)) {
|
||||||
|
countDownLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
controlPortFileObserver.startWatching();
|
||||||
|
controlPortThreadStarted.countDown();
|
||||||
|
countDownLatch.await(10, TimeUnit.SECONDS);
|
||||||
|
controlPortFileObserver.stopWatching();
|
||||||
|
File controlSocket = new File(observeDir, CONTROL_SOCKET_NAME);
|
||||||
|
if (!controlSocket.canRead()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDescriptor controlSocketFd = prepareFileDescriptor(getControlSocket(TorServiceCustom.this).getAbsolutePath());
|
||||||
|
InputStream is = new FileInputStream(controlSocketFd);
|
||||||
|
OutputStream os = new FileOutputStream(controlSocketFd);
|
||||||
|
torControlConnection = new TorControlConnection(is, os);
|
||||||
|
torControlConnection.launchThread(true);
|
||||||
|
torControlConnection.authenticate(new byte[0]);
|
||||||
|
torControlConnection.addRawEventListener(startedEventListener);
|
||||||
|
torControlConnection.setEvents(Arrays.asList(TorControlCommands.EVENT_CIRCUIT_STATUS));
|
||||||
|
|
||||||
|
socksPort = getPortFromGetInfo("net/listeners/socks");
|
||||||
|
httpTunnelPort = getPortFromGetInfo("net/listeners/httptunnel");
|
||||||
|
|
||||||
|
} catch (IOException | ArrayIndexOutOfBoundsException | InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
broadcastStatus(TorServiceCustom.this, STATUS_STOPPING);
|
||||||
|
TorServiceCustom.this.stopSelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private CountDownLatch controlPortThreadStarted;
|
||||||
|
|
||||||
|
private int getPortFromGetInfo(String key) {
|
||||||
|
final String value = getInfo(key);
|
||||||
|
return Integer.parseInt(value.substring(value.lastIndexOf(':') + 1, value.length() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start Tor in a {@link Thread} with the minimum required config. The
|
||||||
|
* rest of the config should happen via the Control Port. First Tor
|
||||||
|
* runs with {@code --verify-config} to check the command line flags and
|
||||||
|
* any {@code torrc} config. If they are correct, then this waits for the
|
||||||
|
* {@link #controlPortThread} to start so it is running before Tor could
|
||||||
|
* potentially create the {@code ControlSocket}. Then finally Tor is
|
||||||
|
* started in its own {@code Thread}.
|
||||||
|
* <p>
|
||||||
|
* Tor daemon does not output early debug messages to logcat, only after it
|
||||||
|
* tries to connect to the ports. So it is important that Tor does not run
|
||||||
|
* into port conflicts when first starting.
|
||||||
|
*
|
||||||
|
* @see <a href="https://trac.torproject.org/projects/tor/ticket/32036">#32036 output debug logs to logcat as early as possible on Android</a>
|
||||||
|
* @see <a href="https://github.com/torproject/tor/blob/40be20d542a83359ea480bbaa28380b4137c88b2/src/app/config/config.c#L4730">options that must be on the command line</a>
|
||||||
|
*/
|
||||||
|
private void startTorServiceThread() {
|
||||||
|
final Context context = this.getApplicationContext();
|
||||||
|
new Thread("tor") {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
String socksPort = "auto";
|
||||||
|
if (isPortAvailable(9050)) {
|
||||||
|
socksPort = Integer.toString(9050);
|
||||||
|
}
|
||||||
|
String httpTunnelPort = "auto";
|
||||||
|
if (isPortAvailable(8118)) {
|
||||||
|
httpTunnelPort = Integer.toString(8118);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runLock.isLocked()) {
|
||||||
|
Log.i(TAG, "Waiting for lock");
|
||||||
|
}
|
||||||
|
runLock.lock();
|
||||||
|
Log.i(TAG, "Acquired lock");
|
||||||
|
createTorConfiguration();
|
||||||
|
ArrayList<String> lines = new ArrayList<>(Arrays.asList("tor", "--verify-config", // must always be here
|
||||||
|
"--RunAsDaemon", "0",
|
||||||
|
"-f", getTorrc(context).getAbsolutePath(),
|
||||||
|
"--defaults-torrc", getDefaultsTorrc(context).getAbsolutePath(),
|
||||||
|
"--ignore-missing-torrc",
|
||||||
|
"--SyslogIdentityTag", TAG,
|
||||||
|
"--CacheDirectory", new File(getCacheDir(), TAG).getAbsolutePath(),
|
||||||
|
"--DataDirectory", getAppTorServiceDataDir(context).getAbsolutePath(),
|
||||||
|
"--ControlSocket", getControlSocket(context).getAbsolutePath(),
|
||||||
|
"--CookieAuthentication", "0",
|
||||||
|
"--SOCKSPort", socksPort,
|
||||||
|
"--HTTPTunnelPort", httpTunnelPort,
|
||||||
|
|
||||||
|
// can be moved to ControlPort messages
|
||||||
|
"--LogMessageDomains", "1",
|
||||||
|
"--TruncateLogFile", "1"
|
||||||
|
));
|
||||||
|
String[] verifyLines = lines.toArray(new String[0]);
|
||||||
|
if (!mainConfigurationSetCommandLine(verifyLines)) {
|
||||||
|
throw new IllegalArgumentException("Setting command line failed: " + Arrays.toString(verifyLines));
|
||||||
|
}
|
||||||
|
int result = runMain(); // run verify
|
||||||
|
if (result != 0) {
|
||||||
|
throw new IllegalArgumentException("Bad command flags: " + Arrays.toString(verifyLines));
|
||||||
|
}
|
||||||
|
|
||||||
|
controlPortThreadStarted = new CountDownLatch(1);
|
||||||
|
controlPortThread.start();
|
||||||
|
controlPortThreadStarted.await();
|
||||||
|
|
||||||
|
String[] runLines = new String[lines.size() - 1];
|
||||||
|
runLines[0] = "tor";
|
||||||
|
for (int i = 2; i < lines.size(); i++) {
|
||||||
|
runLines[i - 1] = lines.get(i);
|
||||||
|
}
|
||||||
|
if (!mainConfigurationSetCommandLine(runLines)) {
|
||||||
|
throw new IllegalArgumentException("Setting command line failed: " + Arrays.toString(runLines));
|
||||||
|
}
|
||||||
|
if (!mainConfigurationSetupControlSocket()) {
|
||||||
|
throw new IllegalStateException("Setting up ControlPort failed!");
|
||||||
|
}
|
||||||
|
if (runMain() != 0) {
|
||||||
|
throw new IllegalStateException("Tor could not start!");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IllegalStateException | IllegalArgumentException | InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
broadcastError(context, e);
|
||||||
|
} finally {
|
||||||
|
broadcastStatus(context, STATUS_STOPPING);
|
||||||
|
mainConfigurationFree();
|
||||||
|
Log.i(TAG, "Releasing lock");
|
||||||
|
runLock.unlock();
|
||||||
|
TorServiceCustom.this.stopSelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (torControlConnection != null) {
|
||||||
|
torControlConnection.removeRawEventListener(startedEventListener);
|
||||||
|
}
|
||||||
|
shutdownTor(TorControlCommands.SIGNAL_SHUTDOWN);
|
||||||
|
broadcastStatus(TorServiceCustom.this, STATUS_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSocksPort() {
|
||||||
|
return socksPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHttpTunnelPort() {
|
||||||
|
return httpTunnelPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the value or null on error
|
||||||
|
* @see <a href="https://gitweb.torproject.org/torspec.git/tree/control-spec.txt?id=bf318ccb042757cc47e47e19a63d1d825dcf222b#n527">control-spec GETINFO</a>
|
||||||
|
*/
|
||||||
|
public String getInfo(String key) {
|
||||||
|
try {
|
||||||
|
return torControlConnection.getInfo(key);
|
||||||
|
} catch (IOException | NullPointerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a signal to the Tor process to shut it down or halt it.
|
||||||
|
* Does not wait for a response, or report errors.
|
||||||
|
*
|
||||||
|
* @see TorControlConnection#shutdownTor(String)
|
||||||
|
*/
|
||||||
|
private void shutdownTor(String signal) {
|
||||||
|
try {
|
||||||
|
if (torControlConnection != null) {
|
||||||
|
torControlConnection.shutdownTor(signal);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an instance of {@link TorControlConnection} to control this instance
|
||||||
|
* of Tor, and configure it to set events.
|
||||||
|
*
|
||||||
|
* @see TorControlConnection#setEvents(java.util.List)
|
||||||
|
*/
|
||||||
|
public TorControlConnection getTorControlConnection() {
|
||||||
|
return torControlConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcasts the current status to any apps following the status of TorService.
|
||||||
|
*/
|
||||||
|
static void sendBroadcastStatusIntent(Context context) {
|
||||||
|
Intent intent = getBroadcastIntent(ACTION_STATUS, currentStatus);
|
||||||
|
intent.putExtra(EXTRA_SERVICE_PACKAGE_NAME, context.getPackageName());
|
||||||
|
context.sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the current status both internally and for any apps that need to
|
||||||
|
* follow the status of TorService.
|
||||||
|
*/
|
||||||
|
static void broadcastStatus(Context context, String currentStatus) {
|
||||||
|
TorServiceCustom.currentStatus = currentStatus;
|
||||||
|
Intent intent = getBroadcastIntent(ACTION_STATUS, currentStatus);
|
||||||
|
intent.putExtra(EXTRA_SERVICE_PACKAGE_NAME, context.getPackageName());
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||||
|
context.sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This might be better handled by {@link android.content.ServiceConnection}
|
||||||
|
* but there is no way to write tests for {@code ServiceConnection}.
|
||||||
|
*/
|
||||||
|
static void broadcastError(Context context, Throwable e) {
|
||||||
|
Intent intent = new Intent(ACTION_ERROR);
|
||||||
|
if (e != null) {
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||||
|
context.sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Intent getBroadcastIntent(String action, String currentStatus) {
|
||||||
|
Intent intent = new Intent(action);
|
||||||
|
intent.putExtra(EXTRA_STATUS, currentStatus);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPortAvailable(int port) {
|
||||||
|
try {
|
||||||
|
(new ServerSocket(port)).close();
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Could not connect.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|