big check-in of major gradle refactor; now building and running!
VPN features are disabled for now
|
@ -1,11 +1,11 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.torproject.android"
|
package="org.torproject.android"
|
||||||
android:versionName="15.1.3-beta"
|
android:versionName="15.2.0-alpha-1"
|
||||||
android:versionCode="15130000"
|
android:versionCode="15200001"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
>
|
>
|
||||||
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23"/>
|
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
|
||||||
<!--
|
<!--
|
||||||
<permission android:name="org.torproject.android.MANAGE_TOR"
|
<permission android:name="org.torproject.android.MANAGE_TOR"
|
||||||
android:label="@string/permission_manage_tor_label"
|
android:label="@string/permission_manage_tor_label"
|
||||||
|
@ -103,7 +103,7 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name="org.torproject.android.service.OnBootReceiver"
|
<receiver android:name="org.torproject.android.OnBootReceiver"
|
||||||
android:enabled="true" android:exported="true"
|
android:enabled="true" android:exported="true"
|
||||||
|
|
||||||
>
|
>
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 23
|
||||||
|
buildToolsVersion "23.0.2"
|
||||||
|
|
||||||
|
sourceSets.main.jni.srcDirs = []
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "org.torproject.android"
|
||||||
|
minSdkVersion 9
|
||||||
|
targetSdkVersion 23
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project(':jsocksAndroid')
|
||||||
|
compile project(':orbotservice')
|
||||||
|
compile 'com.android.support:support-v4:23.4.0'
|
||||||
|
compile 'com.android.support:appcompat-v7:23.4.0'
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.torproject.android"
|
||||||
|
android:versionName="15.2.0-alpha-1"
|
||||||
|
android:versionCode="15200001"
|
||||||
|
android:installLocation="auto"
|
||||||
|
>
|
||||||
|
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
|
||||||
|
<!--
|
||||||
|
<permission android:name="org.torproject.android.MANAGE_TOR"
|
||||||
|
android:label="@string/permission_manage_tor_label"
|
||||||
|
android:description="@string/permission_manage_tor_description"
|
||||||
|
android:protectionLevel="signature"/>
|
||||||
|
|
||||||
|
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
||||||
|
<application android:name="org.torproject.android.OrbotApp" android:icon="@drawable/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:description="@string/app_description"
|
||||||
|
android:configChanges="locale|orientation|screenSize"
|
||||||
|
android:theme="@style/DefaultTheme"
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:allowClearUserData="true"
|
||||||
|
android:persistent="true"
|
||||||
|
android:stopWithTask="false"
|
||||||
|
android:largeHeap="false"
|
||||||
|
>
|
||||||
|
|
||||||
|
<activity android:name=".OrbotMainActivity"
|
||||||
|
android:configChanges="orientation|screenSize"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="bridge" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<action android:name="org.torproject.android.START_TOR" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This is for ensuring the background service still runs when/if the app is swiped away
|
||||||
|
-->
|
||||||
|
<activity
|
||||||
|
android:name=".service.DummyActivity"
|
||||||
|
android:theme="@android:style/Theme.Translucent"
|
||||||
|
android:enabled="true"
|
||||||
|
android:allowTaskReparenting="true"
|
||||||
|
android:noHistory="true"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:alwaysRetainTaskState="false"
|
||||||
|
android:stateNotNeeded="true"
|
||||||
|
android:clearTaskOnLaunch="true"
|
||||||
|
android:finishOnTaskLaunch="true"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".vpn.VPNEnableActivity" android:label="@string/app_name" android:exported="false"
|
||||||
|
android:theme="@android:style/Theme.Translucent"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<activity android:name="org.torproject.android.ui.PromoAppsActivity" android:exported="false"/>
|
||||||
|
|
||||||
|
|
||||||
|
<activity android:name=".settings.SettingsPreferences" android:label="@string/app_name"/>
|
||||||
|
<activity android:name=".ui.AppManager" android:label="@string/app_name"/>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".service.TorService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||||
|
android:stopWithTask="false" >
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".service.StartTorReceiver"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.torproject.android.intent.action.START" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<receiver android:name=".OnBootReceiver"
|
||||||
|
android:enabled="true" android:exported="true"
|
||||||
|
|
||||||
|
>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
<category android:name="android.intent.category.HOME" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||||
|
<category android:name="android.intent.category.HOME" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
|
||||||
|
<category android:name="android.intent.category.HOME" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<service android:name="org.torproject.android.vpn.OrbotVpnService"
|
||||||
|
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.net.VpnService"/>
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
-->
|
||||||
|
</application>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,22 @@
|
||||||
|
obfs3 83.212.101.3:80 A09D536DD1752D542E1FBB3C9CE4449D51298239
|
||||||
|
obfs3 169.229.59.74:31493 AF9F66B7B04F8FF6F32D455F05135250A16543C9
|
||||||
|
obfs3 169.229.59.75:46328 AF9F66B7B04F8FF6F32D455F05135250A16543C9
|
||||||
|
obfs3 109.105.109.163:38980 1E05F577A0EC0213F971D81BF4D86A9E4E8229ED
|
||||||
|
obfs3 109.105.109.163:47779 4C331FA9B3D1D6D8FB0D8FBBF0C259C360D97E6A
|
||||||
|
scramblesuit 83.212.101.3:443 A09D536DD1752D542E1FBB3C9CE4449D51298239 password=XTCXLG2JAMJKZW2POLBAOWOQETQSMASH
|
||||||
|
obfs4 198.245.60.50:443 752CF7825B3B9EA6A98C83AC41F7099D67007EA5 cert=xpmQtKUqQ/6v5X7ijgYE/f03+l2/EuQ1dexjyUhh16wQlu/cpXUGalmhDIlhuiQPNEKmKw iat-mode=0
|
||||||
|
obfs4 109.105.109.165:10527 8DFCD8FB3285E855F5A55EDDA35696C743ABFC4E cert=Bvg/itxeL4TWKLP6N1MaQzSOC6tcRIBv6q57DYAZc3b2AzuM+/TfB7mqTFEfXILCjEwzVA iat-mode=0
|
||||||
|
obfs4 83.212.101.3:41213 A09D536DD1752D542E1FBB3C9CE4449D51298239 cert=lPRQ/MXdD1t5SRZ9MquYQNT9m5DV757jtdXdlePmRCudUU9CFUOX1Tm7/meFSyPOsud7Cw iat-mode=0
|
||||||
|
obfs4 104.131.108.182:56880 EF577C30B9F788B0E1801CF7E433B3B77792B77A cert=0SFhfDQrKjUJP8Qq6wrwSICEPf3Vl/nJRsYxWbg3QRoSqhl2EB78MPS2lQxbXY4EW1wwXA iat-mode=0
|
||||||
|
obfs4 109.105.109.147:13764 BBB28DF0F201E706BE564EFE690FE9577DD8386D cert=KfMQN/tNMFdda61hMgpiMI7pbwU1T+wxjTulYnfw+4sgvG0zSH7N7fwT10BI8MUdAD7iJA iat-mode=0
|
||||||
|
obfs4 154.35.22.10:41835 8FB9F4319E89E5C6223052AA525A192AFBC85D55 cert=GGGS1TX4R81m3r0HBl79wKy1OtPPNR2CZUIrHjkRg65Vc2VR8fOyo64f9kmT1UAFG7j0HQ iat-mode=0
|
||||||
|
obfs4 154.35.22.11:49868 A832D176ECD5C7C6B58825AE22FC4C90FA249637 cert=YPbQqXPiqTUBfjGFLpm9JYEFTBvnzEJDKJxXG5Sxzrr/v2qrhGU4Jls9lHjLAhqpXaEfZw iat-mode=0
|
||||||
|
obfs4 154.35.22.12:80 00DC6C4FA49A65BD1472993CF6730D54F11E0DBB cert=N86E9hKXXXVz6G7w2z8wFfhIDztDAzZ/3poxVePHEYjbKDWzjkRDccFMAnhK75fc65pYSg iat-mode=0
|
||||||
|
obfs4 154.35.22.13:443 FE7840FE1E21FE0A0639ED176EDA00A3ECA1E34D cert=fKnzxr+m+jWXXQGCaXe4f2gGoPXMzbL+bTBbXMYXuK0tMotd+nXyS33y2mONZWU29l81CA iat-mode=0
|
||||||
|
obfs4 154.35.22.10:1984 8FB9F4319E89E5C6223052AA525A192AFBC85D55 cert=GGGS1TX4R81m3r0HBl79wKy1OtPPNR2CZUIrHjkRg65Vc2VR8fOyo64f9kmT1UAFG7j0HQ iat-mode=0
|
||||||
|
obfs4 154.35.22.11:1984 A832D176ECD5C7C6B58825AE22FC4C90FA249637 cert=YPbQqXPiqTUBfjGFLpm9JYEFTBvnzEJDKJxXG5Sxzrr/v2qrhGU4Jls9lHjLAhqpXaEfZw iat-mode=0
|
||||||
|
obfs4 154.35.22.12:1984 00DC6C4FA49A65BD1472993CF6730D54F11E0DBB cert=N86E9hKXXXVz6G7w2z8wFfhIDztDAzZ/3poxVePHEYjbKDWzjkRDccFMAnhK75fc65pYSg iat-mode=0
|
||||||
|
obfs4 154.35.22.13:1984 FE7840FE1E21FE0A0639ED176EDA00A3ECA1E34D cert=fKnzxr+m+jWXXQGCaXe4f2gGoPXMzbL+bTBbXMYXuK0tMotd+nXyS33y2mONZWU29l81CA iat-mode=0
|
||||||
|
meek 0.0.2.0:1 46D4A71197B8FA515A826C6B017C522FE264655B url=https://meek-reflect.appspot.com/ front=www.google.com
|
||||||
|
meek 0.0.2.0:2 B9E7141C594AF25699E0079C1F0146F409495296 url=https://d2zfqthxsdq309.cloudfront.net/ front=a0.awsstatic.com
|
||||||
|
meek 0.0.2.0:3 A2C13B7DFCAB1CBF3A884B6EB99A98067AB6EF44 url=https://az786092.vo.msecnd.net/ front=ajax.aspnetcdn.com
|
|
@ -0,0 +1,506 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2009 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.integration.android;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.Fragment;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A utility class which helps ease integration with Barcode Scanner via {@link Intent}s. This is a simple
|
||||||
|
* way to invoke barcode scanning and receive the result, without any need to integrate, modify, or learn the
|
||||||
|
* project's source code.</p>
|
||||||
|
*
|
||||||
|
* <h2>Initiating a barcode scan</h2>
|
||||||
|
*
|
||||||
|
* <p>To integrate, create an instance of {@code IntentIntegrator} and call {@link #initiateScan()} and wait
|
||||||
|
* for the result in your app.</p>
|
||||||
|
*
|
||||||
|
* <p>It does require that the Barcode Scanner (or work-alike) application is installed. The
|
||||||
|
* {@link #initiateScan()} method will prompt the user to download the application, if needed.</p>
|
||||||
|
*
|
||||||
|
* <p>There are a few steps to using this integration. First, your {@link Activity} must implement
|
||||||
|
* the method {@link Activity#onActivityResult(int, int, Intent)} and include a line of code like this:</p>
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
|
* IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
|
||||||
|
* if (scanResult != null) {
|
||||||
|
* // handle scan result
|
||||||
|
* }
|
||||||
|
* // else continue with any other code you need in the method
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>This is where you will handle a scan result.</p>
|
||||||
|
*
|
||||||
|
* <p>Second, just call this in response to a user action somewhere to begin the scan process:</p>
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* IntentIntegrator integrator = new IntentIntegrator(yourActivity);
|
||||||
|
* integrator.initiateScan();
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>Note that {@link #initiateScan()} returns an {@link AlertDialog} which is non-null if the
|
||||||
|
* user was prompted to download the application. This lets the calling app potentially manage the dialog.
|
||||||
|
* In particular, ideally, the app dismisses the dialog if it's still active in its {@link Activity#onPause()}
|
||||||
|
* method.</p>
|
||||||
|
*
|
||||||
|
* <p>You can use {@link #setTitle(String)} to customize the title of this download prompt dialog (or, use
|
||||||
|
* {@link #setTitleByID(int)} to set the title by string resource ID.) Likewise, the prompt message, and
|
||||||
|
* yes/no button labels can be changed.</p>
|
||||||
|
*
|
||||||
|
* <p>Finally, you can use {@link #addExtra(String, Object)} to add more parameters to the Intent used
|
||||||
|
* to invoke the scanner. This can be used to set additional options not directly exposed by this
|
||||||
|
* simplified API.</p>
|
||||||
|
*
|
||||||
|
* <p>By default, this will only allow applications that are known to respond to this intent correctly
|
||||||
|
* do so. The apps that are allowed to response can be set with {@link #setTargetApplications(List)}.
|
||||||
|
* For example, set to {@link #TARGET_BARCODE_SCANNER_ONLY} to only target the Barcode Scanner app itself.</p>
|
||||||
|
*
|
||||||
|
* <h2>Sharing text via barcode</h2>
|
||||||
|
*
|
||||||
|
* <p>To share text, encoded as a QR Code on-screen, similarly, see {@link #shareText(CharSequence)}.</p>
|
||||||
|
*
|
||||||
|
* <p>Some code, particularly download integration, was contributed from the Anobiit application.</p>
|
||||||
|
*
|
||||||
|
* <h2>Enabling experimental barcode formats</h2>
|
||||||
|
*
|
||||||
|
* <p>Some formats are not enabled by default even when scanning with {@link #ALL_CODE_TYPES}, such as
|
||||||
|
* PDF417. Use {@link #initiateScan(java.util.Collection)} with
|
||||||
|
* a collection containing the names of formats to scan for explicitly, like "PDF_417", to use such
|
||||||
|
* formats.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author Fred Lin
|
||||||
|
* @author Isaac Potoczny-Jones
|
||||||
|
* @author Brad Drehmer
|
||||||
|
* @author gcstang
|
||||||
|
*/
|
||||||
|
public class IntentIntegrator {
|
||||||
|
|
||||||
|
public static final int REQUEST_CODE = 0x0000c0de; // Only use bottom 16 bits
|
||||||
|
private static final String TAG = IntentIntegrator.class.getSimpleName();
|
||||||
|
|
||||||
|
public static final String DEFAULT_TITLE = "Install Barcode Scanner?";
|
||||||
|
public static final String DEFAULT_MESSAGE =
|
||||||
|
"This application requires Barcode Scanner. Would you like to install it?";
|
||||||
|
public static final String DEFAULT_YES = "Yes";
|
||||||
|
public static final String DEFAULT_NO = "No";
|
||||||
|
|
||||||
|
private static final String BS_PACKAGE = "com.google.zxing.client.android";
|
||||||
|
private static final String BSPLUS_PACKAGE = "com.srowen.bs.android";
|
||||||
|
|
||||||
|
// supported barcode formats
|
||||||
|
public static final Collection<String> PRODUCT_CODE_TYPES = list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "RSS_14");
|
||||||
|
public static final Collection<String> ONE_D_CODE_TYPES =
|
||||||
|
list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128",
|
||||||
|
"ITF", "RSS_14", "RSS_EXPANDED");
|
||||||
|
public static final Collection<String> QR_CODE_TYPES = Collections.singleton("QR_CODE");
|
||||||
|
public static final Collection<String> DATA_MATRIX_TYPES = Collections.singleton("DATA_MATRIX");
|
||||||
|
|
||||||
|
public static final Collection<String> ALL_CODE_TYPES = null;
|
||||||
|
|
||||||
|
public static final List<String> TARGET_BARCODE_SCANNER_ONLY = Collections.singletonList(BS_PACKAGE);
|
||||||
|
public static final List<String> TARGET_ALL_KNOWN = list(
|
||||||
|
BSPLUS_PACKAGE, // Barcode Scanner+
|
||||||
|
BSPLUS_PACKAGE + ".simple", // Barcode Scanner+ Simple
|
||||||
|
BS_PACKAGE // Barcode Scanner
|
||||||
|
// What else supports this intent?
|
||||||
|
);
|
||||||
|
|
||||||
|
private final Activity activity;
|
||||||
|
private final Fragment fragment;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
private String message;
|
||||||
|
private String buttonYes;
|
||||||
|
private String buttonNo;
|
||||||
|
private List<String> targetApplications;
|
||||||
|
private final Map<String,Object> moreExtras = new HashMap<String,Object>(3);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param activity {@link Activity} invoking the integration
|
||||||
|
*/
|
||||||
|
public IntentIntegrator(Activity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
this.fragment = null;
|
||||||
|
initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param fragment {@link Fragment} invoking the integration.
|
||||||
|
* {@link #startActivityForResult(Intent, int)} will be called on the {@link Fragment} instead
|
||||||
|
* of an {@link Activity}
|
||||||
|
*/
|
||||||
|
public IntentIntegrator(Fragment fragment) {
|
||||||
|
this.activity = fragment.getActivity();
|
||||||
|
this.fragment = fragment;
|
||||||
|
initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeConfiguration() {
|
||||||
|
title = DEFAULT_TITLE;
|
||||||
|
message = DEFAULT_MESSAGE;
|
||||||
|
buttonYes = DEFAULT_YES;
|
||||||
|
buttonNo = DEFAULT_NO;
|
||||||
|
targetApplications = TARGET_ALL_KNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitleByID(int titleID) {
|
||||||
|
title = activity.getString(titleID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessageByID(int messageID) {
|
||||||
|
message = activity.getString(messageID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getButtonYes() {
|
||||||
|
return buttonYes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setButtonYes(String buttonYes) {
|
||||||
|
this.buttonYes = buttonYes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setButtonYesByID(int buttonYesID) {
|
||||||
|
buttonYes = activity.getString(buttonYesID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getButtonNo() {
|
||||||
|
return buttonNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setButtonNo(String buttonNo) {
|
||||||
|
this.buttonNo = buttonNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setButtonNoByID(int buttonNoID) {
|
||||||
|
buttonNo = activity.getString(buttonNoID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<String> getTargetApplications() {
|
||||||
|
return targetApplications;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setTargetApplications(List<String> targetApplications) {
|
||||||
|
if (targetApplications.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("No target applications");
|
||||||
|
}
|
||||||
|
this.targetApplications = targetApplications;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSingleTargetApplication(String targetApplication) {
|
||||||
|
this.targetApplications = Collections.singletonList(targetApplication);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String,?> getMoreExtras() {
|
||||||
|
return moreExtras;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void addExtra(String key, Object value) {
|
||||||
|
moreExtras.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a scan for all known barcode types with the default camera.
|
||||||
|
*
|
||||||
|
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||||
|
* if a prompt was needed, or null otherwise.
|
||||||
|
*/
|
||||||
|
public final AlertDialog initiateScan() {
|
||||||
|
return initiateScan(ALL_CODE_TYPES, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a scan for all known barcode types with the specified camera.
|
||||||
|
*
|
||||||
|
* @param cameraId camera ID of the camera to use. A negative value means "no preference".
|
||||||
|
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||||
|
* if a prompt was needed, or null otherwise.
|
||||||
|
*/
|
||||||
|
public final AlertDialog initiateScan(int cameraId) {
|
||||||
|
return initiateScan(ALL_CODE_TYPES, cameraId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a scan, using the default camera, only for a certain set of barcode types, given as strings corresponding
|
||||||
|
* to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants
|
||||||
|
* like {@link #PRODUCT_CODE_TYPES} for example.
|
||||||
|
*
|
||||||
|
* @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for
|
||||||
|
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||||
|
* if a prompt was needed, or null otherwise.
|
||||||
|
*/
|
||||||
|
public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats) {
|
||||||
|
return initiateScan(desiredBarcodeFormats, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a scan, using the specified camera, only for a certain set of barcode types, given as strings corresponding
|
||||||
|
* to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants
|
||||||
|
* like {@link #PRODUCT_CODE_TYPES} for example.
|
||||||
|
*
|
||||||
|
* @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for
|
||||||
|
* @param cameraId camera ID of the camera to use. A negative value means "no preference".
|
||||||
|
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||||
|
* if a prompt was needed, or null otherwise
|
||||||
|
*/
|
||||||
|
public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats, int cameraId) {
|
||||||
|
Intent intentScan = new Intent(BS_PACKAGE + ".SCAN");
|
||||||
|
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
|
||||||
|
|
||||||
|
// check which types of codes to scan for
|
||||||
|
if (desiredBarcodeFormats != null) {
|
||||||
|
// set the desired barcode types
|
||||||
|
StringBuilder joinedByComma = new StringBuilder();
|
||||||
|
for (String format : desiredBarcodeFormats) {
|
||||||
|
if (joinedByComma.length() > 0) {
|
||||||
|
joinedByComma.append(',');
|
||||||
|
}
|
||||||
|
joinedByComma.append(format);
|
||||||
|
}
|
||||||
|
intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// check requested camera ID
|
||||||
|
if (cameraId >= 0) {
|
||||||
|
intentScan.putExtra("SCAN_CAMERA_ID", cameraId);
|
||||||
|
}
|
||||||
|
|
||||||
|
String targetAppPackage = findTargetAppPackage(intentScan);
|
||||||
|
if (targetAppPackage == null) {
|
||||||
|
return showDownloadDialog();
|
||||||
|
}
|
||||||
|
intentScan.setPackage(targetAppPackage);
|
||||||
|
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
attachMoreExtras(intentScan);
|
||||||
|
startActivityForResult(intentScan, REQUEST_CODE);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start an activity. This method is defined to allow different methods of activity starting for
|
||||||
|
* newer versions of Android and for compatibility library.
|
||||||
|
*
|
||||||
|
* @param intent Intent to start.
|
||||||
|
* @param code Request code for the activity
|
||||||
|
* @see android.app.Activity#startActivityForResult(Intent, int)
|
||||||
|
* @see android.app.Fragment#startActivityForResult(Intent, int)
|
||||||
|
*/
|
||||||
|
protected void startActivityForResult(Intent intent, int code) {
|
||||||
|
if (fragment == null) {
|
||||||
|
activity.startActivityForResult(intent, code);
|
||||||
|
} else {
|
||||||
|
fragment.startActivityForResult(intent, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String findTargetAppPackage(Intent intent) {
|
||||||
|
PackageManager pm = activity.getPackageManager();
|
||||||
|
List<ResolveInfo> availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||||
|
if (availableApps != null) {
|
||||||
|
for (String targetApp : targetApplications) {
|
||||||
|
if (contains(availableApps, targetApp)) {
|
||||||
|
return targetApp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean contains(Iterable<ResolveInfo> availableApps, String targetApp) {
|
||||||
|
for (ResolveInfo availableApp : availableApps) {
|
||||||
|
String packageName = availableApp.activityInfo.packageName;
|
||||||
|
if (targetApp.equals(packageName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AlertDialog showDownloadDialog() {
|
||||||
|
AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity);
|
||||||
|
downloadDialog.setTitle(title);
|
||||||
|
downloadDialog.setMessage(message);
|
||||||
|
downloadDialog.setPositiveButton(buttonYes, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
String packageName;
|
||||||
|
if (targetApplications.contains(BS_PACKAGE)) {
|
||||||
|
// Prefer to suggest download of BS if it's anywhere in the list
|
||||||
|
packageName = BS_PACKAGE;
|
||||||
|
} else {
|
||||||
|
// Otherwise, first option:
|
||||||
|
packageName = targetApplications.get(0);
|
||||||
|
}
|
||||||
|
Uri uri = Uri.parse("market://details?id=" + packageName);
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||||
|
try {
|
||||||
|
if (fragment == null) {
|
||||||
|
activity.startActivity(intent);
|
||||||
|
} else {
|
||||||
|
fragment.startActivity(intent);
|
||||||
|
}
|
||||||
|
} catch (ActivityNotFoundException anfe) {
|
||||||
|
// Hmm, market is not installed
|
||||||
|
Log.w(TAG, "Google Play is not installed; cannot install " + packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
downloadDialog.setNegativeButton(buttonNo, null);
|
||||||
|
downloadDialog.setCancelable(true);
|
||||||
|
return downloadDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Call this from your {@link Activity}'s
|
||||||
|
* {@link Activity#onActivityResult(int, int, Intent)} method.</p>
|
||||||
|
*
|
||||||
|
* @param requestCode request code from {@code onActivityResult()}
|
||||||
|
* @param resultCode result code from {@code onActivityResult()}
|
||||||
|
* @param intent {@link Intent} from {@code onActivityResult()}
|
||||||
|
* @return null if the event handled here was not related to this class, or
|
||||||
|
* else an {@link IntentResult} containing the result of the scan. If the user cancelled scanning,
|
||||||
|
* the fields will be null.
|
||||||
|
*/
|
||||||
|
public static IntentResult parseActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
|
if (requestCode == REQUEST_CODE) {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
String contents = intent.getStringExtra("SCAN_RESULT");
|
||||||
|
String formatName = intent.getStringExtra("SCAN_RESULT_FORMAT");
|
||||||
|
byte[] rawBytes = intent.getByteArrayExtra("SCAN_RESULT_BYTES");
|
||||||
|
int intentOrientation = intent.getIntExtra("SCAN_RESULT_ORIENTATION", Integer.MIN_VALUE);
|
||||||
|
Integer orientation = intentOrientation == Integer.MIN_VALUE ? null : intentOrientation;
|
||||||
|
String errorCorrectionLevel = intent.getStringExtra("SCAN_RESULT_ERROR_CORRECTION_LEVEL");
|
||||||
|
return new IntentResult(contents,
|
||||||
|
formatName,
|
||||||
|
rawBytes,
|
||||||
|
orientation,
|
||||||
|
errorCorrectionLevel);
|
||||||
|
}
|
||||||
|
return new IntentResult();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defaults to type "TEXT_TYPE".
|
||||||
|
*
|
||||||
|
* @param text the text string to encode as a barcode
|
||||||
|
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||||
|
* if a prompt was needed, or null otherwise
|
||||||
|
* @see #shareText(CharSequence, CharSequence)
|
||||||
|
*/
|
||||||
|
public final AlertDialog shareText(CharSequence text) {
|
||||||
|
return shareText(text, "TEXT_TYPE");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shares the given text by encoding it as a barcode, such that another user can
|
||||||
|
* scan the text off the screen of the device.
|
||||||
|
*
|
||||||
|
* @param text the text string to encode as a barcode
|
||||||
|
* @param type type of data to encode. See {@code com.google.zxing.client.android.Contents.Type} constants.
|
||||||
|
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
||||||
|
* if a prompt was needed, or null otherwise
|
||||||
|
*/
|
||||||
|
public final AlertDialog shareText(CharSequence text, CharSequence type) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
||||||
|
intent.setAction(BS_PACKAGE + ".ENCODE");
|
||||||
|
intent.putExtra("ENCODE_TYPE", type);
|
||||||
|
intent.putExtra("ENCODE_DATA", text);
|
||||||
|
String targetAppPackage = findTargetAppPackage(intent);
|
||||||
|
if (targetAppPackage == null) {
|
||||||
|
return showDownloadDialog();
|
||||||
|
}
|
||||||
|
intent.setPackage(targetAppPackage);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
attachMoreExtras(intent);
|
||||||
|
if (fragment == null) {
|
||||||
|
activity.startActivity(intent);
|
||||||
|
} else {
|
||||||
|
fragment.startActivity(intent);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> list(String... values) {
|
||||||
|
return Collections.unmodifiableList(Arrays.asList(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attachMoreExtras(Intent intent) {
|
||||||
|
for (Map.Entry<String,Object> entry : moreExtras.entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
Object value = entry.getValue();
|
||||||
|
// Kind of hacky
|
||||||
|
if (value instanceof Integer) {
|
||||||
|
intent.putExtra(key, (Integer) value);
|
||||||
|
} else if (value instanceof Long) {
|
||||||
|
intent.putExtra(key, (Long) value);
|
||||||
|
} else if (value instanceof Boolean) {
|
||||||
|
intent.putExtra(key, (Boolean) value);
|
||||||
|
} else if (value instanceof Double) {
|
||||||
|
intent.putExtra(key, (Double) value);
|
||||||
|
} else if (value instanceof Float) {
|
||||||
|
intent.putExtra(key, (Float) value);
|
||||||
|
} else if (value instanceof Bundle) {
|
||||||
|
intent.putExtra(key, (Bundle) value);
|
||||||
|
} else {
|
||||||
|
intent.putExtra(key, value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2009 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.integration.android;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates the result of a barcode scan invoked through {@link IntentIntegrator}.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class IntentResult {
|
||||||
|
|
||||||
|
private final String contents;
|
||||||
|
private final String formatName;
|
||||||
|
private final byte[] rawBytes;
|
||||||
|
private final Integer orientation;
|
||||||
|
private final String errorCorrectionLevel;
|
||||||
|
|
||||||
|
IntentResult() {
|
||||||
|
this(null, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
IntentResult(String contents,
|
||||||
|
String formatName,
|
||||||
|
byte[] rawBytes,
|
||||||
|
Integer orientation,
|
||||||
|
String errorCorrectionLevel) {
|
||||||
|
this.contents = contents;
|
||||||
|
this.formatName = formatName;
|
||||||
|
this.rawBytes = rawBytes;
|
||||||
|
this.orientation = orientation;
|
||||||
|
this.errorCorrectionLevel = errorCorrectionLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return raw content of barcode
|
||||||
|
*/
|
||||||
|
public String getContents() {
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return name of format, like "QR_CODE", "UPC_A". See {@code BarcodeFormat} for more format names.
|
||||||
|
*/
|
||||||
|
public String getFormatName() {
|
||||||
|
return formatName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return raw bytes of the barcode content, if applicable, or null otherwise
|
||||||
|
*/
|
||||||
|
public byte[] getRawBytes() {
|
||||||
|
return rawBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return rotation of the image, in degrees, which resulted in a successful scan. May be null.
|
||||||
|
*/
|
||||||
|
public Integer getOrientation() {
|
||||||
|
return orientation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return name of the error correction level used in the barcode, if applicable
|
||||||
|
*/
|
||||||
|
public String getErrorCorrectionLevel() {
|
||||||
|
return errorCorrectionLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder dialogText = new StringBuilder(100);
|
||||||
|
dialogText.append("Format: ").append(formatName).append('\n');
|
||||||
|
dialogText.append("Contents: ").append(contents).append('\n');
|
||||||
|
int rawBytesLength = rawBytes == null ? 0 : rawBytes.length;
|
||||||
|
dialogText.append("Raw bytes: (").append(rawBytesLength).append(" bytes)\n");
|
||||||
|
dialogText.append("Orientation: ").append(orientation).append('\n');
|
||||||
|
dialogText.append("EC level: ").append(errorCorrectionLevel).append('\n');
|
||||||
|
return dialogText.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package org.torproject.android;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import org.torproject.android.service.Prefs;
|
||||||
|
import org.torproject.android.service.TorService;
|
||||||
|
import org.torproject.android.service.TorServiceConstants;
|
||||||
|
import org.torproject.android.vpn.VPNEnableActivity;
|
||||||
|
|
||||||
|
public class OnBootReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
Prefs.setContext(context);
|
||||||
|
if (Prefs.startOnBoot())
|
||||||
|
{
|
||||||
|
|
||||||
|
if (Prefs.useVpn())
|
||||||
|
startVpnService(context); //VPN will start Tor once it is done
|
||||||
|
else
|
||||||
|
startService(TorServiceConstants.ACTION_START, context);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startVpnService (final Context context)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent(context,VPNEnableActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startService (String action, Context context)
|
||||||
|
{
|
||||||
|
|
||||||
|
Intent torService = new Intent(context, TorService.class);
|
||||||
|
torService.setAction(action);
|
||||||
|
context.startService(torService);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
|
||||||
|
package org.torproject.android;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.torproject.android.service.OrbotConstants;
|
||||||
|
import org.torproject.android.service.Prefs;
|
||||||
|
import org.torproject.android.service.TorServiceConstants;
|
||||||
|
|
||||||
|
import org.torproject.android.settings.Languages;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class OrbotApp extends Application implements OrbotConstants
|
||||||
|
{
|
||||||
|
|
||||||
|
private Locale locale;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
Prefs.setContext(this);
|
||||||
|
|
||||||
|
Languages.setup(OrbotMainActivity.class, R.string.menu_settings);
|
||||||
|
Languages.setLanguage(this, Prefs.getDefaultLocale(), true);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
Log.i(TAG, "onConfigurationChanged " + newConfig.locale.getLanguage());
|
||||||
|
Languages.setLanguage(this, Prefs.getDefaultLocale(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void forceChangeLanguage(Activity activity) {
|
||||||
|
Intent intent = activity.getIntent();
|
||||||
|
if (intent == null) // when launched as LAUNCHER
|
||||||
|
intent = new Intent(activity, OrbotMainActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||||
|
activity.finish();
|
||||||
|
activity.overridePendingTransition(0, 0);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
activity.overridePendingTransition(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Languages getLanguages(Activity activity) {
|
||||||
|
return Languages.get(activity);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
package org.torproject.android.settings;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
public class Languages {
|
||||||
|
public static final String TAG = "Languages";
|
||||||
|
|
||||||
|
public static final Locale defaultLocale;
|
||||||
|
public static final Locale TIBETAN = new Locale("bo");
|
||||||
|
static final Locale localesToTest[] = {
|
||||||
|
Locale.ENGLISH, Locale.FRENCH, Locale.GERMAN,
|
||||||
|
Locale.ITALIAN, Locale.JAPANESE, Locale.KOREAN,
|
||||||
|
Locale.TRADITIONAL_CHINESE, Locale.SIMPLIFIED_CHINESE,
|
||||||
|
TIBETAN, new Locale("af"), new Locale("am"),
|
||||||
|
new Locale("ar"), new Locale("az"), new Locale("bg"),
|
||||||
|
new Locale("bn"), new Locale("ca"), new Locale("cs"),
|
||||||
|
new Locale("da"), new Locale("el"), new Locale("es"),
|
||||||
|
new Locale("et"), new Locale("eu"), new Locale("fa"),
|
||||||
|
new Locale("fi"), new Locale("gl"), new Locale("hi"),
|
||||||
|
new Locale("hr"), new Locale("hu"), new Locale("hy"),
|
||||||
|
new Locale("in"), new Locale("hy"), new Locale("in"),
|
||||||
|
new Locale("is"), new Locale("it"), new Locale("iw"),
|
||||||
|
new Locale("ka"), new Locale("kk"), new Locale("km"),
|
||||||
|
new Locale("kn"), new Locale("ky"), new Locale("lo"),
|
||||||
|
new Locale("lt"), new Locale("lv"), new Locale("mk"),
|
||||||
|
new Locale("ml"), new Locale("mn"), new Locale("mr"),
|
||||||
|
new Locale("ms"), new Locale("my"), new Locale("nb"),
|
||||||
|
new Locale("ne"), new Locale("nl"), new Locale("pl"),
|
||||||
|
new Locale("pt"), new Locale("rm"), new Locale("ro"),
|
||||||
|
new Locale("ru"), new Locale("si"), new Locale("sk"),
|
||||||
|
new Locale("sl"), new Locale("sn"), new Locale("sr"),
|
||||||
|
new Locale("sv"), new Locale("sw"), new Locale("ta"),
|
||||||
|
new Locale("te"), new Locale("th"), new Locale("tl"),
|
||||||
|
new Locale("tr"), new Locale("uk"), new Locale("ur"),
|
||||||
|
new Locale("uz"), new Locale("vi"), new Locale("zu"),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String USE_SYSTEM_DEFAULT = "";
|
||||||
|
private static final String defaultString = "Use System Default";
|
||||||
|
|
||||||
|
private static Locale locale;
|
||||||
|
private static Languages singleton;
|
||||||
|
private static Class<?> clazz;
|
||||||
|
private static int resId;
|
||||||
|
private static Map<String, String> tmpMap = new TreeMap<String, String>();
|
||||||
|
private static Map<String, String> nameMap;
|
||||||
|
|
||||||
|
static {
|
||||||
|
defaultLocale = Locale.getDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Languages(Activity activity) {
|
||||||
|
AssetManager assets = activity.getAssets();
|
||||||
|
Configuration config = activity.getResources().getConfiguration();
|
||||||
|
// Resources() requires DisplayMetrics, but they are only needed for drawables
|
||||||
|
DisplayMetrics ignored = new DisplayMetrics();
|
||||||
|
activity.getWindowManager().getDefaultDisplay().getMetrics(ignored);
|
||||||
|
Resources resources;
|
||||||
|
Set<Locale> localeSet = new LinkedHashSet<Locale>();
|
||||||
|
for (Locale locale : localesToTest) {
|
||||||
|
config.locale = locale;
|
||||||
|
resources = new Resources(assets, ignored, config);
|
||||||
|
if (!TextUtils.equals(defaultString, resources.getString(resId))
|
||||||
|
|| locale.equals(Locale.ENGLISH))
|
||||||
|
localeSet.add(locale);
|
||||||
|
}
|
||||||
|
for (Locale locale : localeSet) {
|
||||||
|
if (locale.equals(TIBETAN)) {
|
||||||
|
// include English name for devices without Tibetan font support
|
||||||
|
tmpMap.put(TIBETAN.getLanguage(), "Tibetan བོད་སྐད།"); // Tibetan
|
||||||
|
} else if (locale.equals(Locale.SIMPLIFIED_CHINESE)) {
|
||||||
|
tmpMap.put(Locale.SIMPLIFIED_CHINESE.toString(), "中文 (中国)"); // Chinese (China)
|
||||||
|
} else if (locale.equals(Locale.TRADITIONAL_CHINESE)) {
|
||||||
|
tmpMap.put(Locale.TRADITIONAL_CHINESE.toString(), "中文 (台灣)"); // Chinese (Taiwan)
|
||||||
|
} else {
|
||||||
|
tmpMap.put(locale.getLanguage(), locale.getDisplayLanguage(locale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* USE_SYSTEM_DEFAULT is a fake one for displaying in a chooser menu. */
|
||||||
|
localeSet.add(null);
|
||||||
|
tmpMap.put(USE_SYSTEM_DEFAULT, activity.getString(resId));
|
||||||
|
nameMap = Collections.unmodifiableMap(tmpMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the instance of {@link Languages} to work with, providing the
|
||||||
|
* {@link Activity} that is will be working as part of, as well as the
|
||||||
|
* {@code resId} that has the exact string "Use System Default",
|
||||||
|
* i.e. {@code R.string.use_system_default}.
|
||||||
|
* <p/>
|
||||||
|
* That string resource {@code resId} is also used to find the supported
|
||||||
|
* translations: if an included translation has a translated string that
|
||||||
|
* matches that {@code resId}, then that language will be included as a
|
||||||
|
* supported language.
|
||||||
|
*
|
||||||
|
* @param clazz the {@link Class} of the default {@code Activity},
|
||||||
|
* usually the main {@code Activity} from where the
|
||||||
|
* Settings is launched from.
|
||||||
|
* @param resId the string resource ID to for the string "Use System Default",
|
||||||
|
* e.g. {@code R.string.use_system_default}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static void setup(Class<?> clazz, int resId) {
|
||||||
|
if (Languages.clazz == null) {
|
||||||
|
Languages.clazz = clazz;
|
||||||
|
Languages.resId = resId;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Languages singleton was already initialized, duplicate call to Languages.setup()!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the singleton to work with.
|
||||||
|
*
|
||||||
|
* @param activity the {@link Activity} this is working as part of
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Languages get(Activity activity) {
|
||||||
|
if (singleton == null) {
|
||||||
|
singleton = new Languages(activity);
|
||||||
|
}
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
public static void setLanguage(final ContextWrapper contextWrapper, String language, boolean refresh) {
|
||||||
|
if (locale != null && TextUtils.equals(locale.getLanguage(), language) && (!refresh)) {
|
||||||
|
return; // already configured
|
||||||
|
} else if (language == null || language == USE_SYSTEM_DEFAULT) {
|
||||||
|
locale = defaultLocale;
|
||||||
|
} else {
|
||||||
|
/* handle locales with the country in it, i.e. zh_CN, zh_TW, etc */
|
||||||
|
String localeSplit[] = language.split("_");
|
||||||
|
if (localeSplit.length > 1) {
|
||||||
|
locale = new Locale(localeSplit[0], localeSplit[1]);
|
||||||
|
} else {
|
||||||
|
locale = new Locale(language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Resources resources = contextWrapper.getBaseContext().getResources();
|
||||||
|
Configuration config = resources.getConfiguration();
|
||||||
|
if (Build.VERSION.SDK_INT >= 17) {
|
||||||
|
config.setLocale(locale);
|
||||||
|
} else {
|
||||||
|
config.locale = locale;
|
||||||
|
}
|
||||||
|
resources.updateConfiguration(config, resources.getDisplayMetrics());
|
||||||
|
Locale.setDefault(locale);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force reload the {@link Activity to make language changes take effect.}
|
||||||
|
*
|
||||||
|
* @param activity the {@code Activity} to force reload
|
||||||
|
*/
|
||||||
|
public static void forceChangeLanguage(Activity activity) {
|
||||||
|
Intent intent = activity.getIntent();
|
||||||
|
if (intent == null) // when launched as LAUNCHER
|
||||||
|
return;
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||||
|
activity.finish();
|
||||||
|
activity.overridePendingTransition(0, 0);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
activity.overridePendingTransition(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the language based on the locale.
|
||||||
|
*
|
||||||
|
* @param locale
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getName(String locale) {
|
||||||
|
String ret = nameMap.get(locale);
|
||||||
|
// if no match, try to return a more general name (i.e. English for
|
||||||
|
// en_IN)
|
||||||
|
if (ret == null && locale.contains("_"))
|
||||||
|
ret = nameMap.get(locale.split("_")[0]);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of the names of all the supported languages, sorted to
|
||||||
|
* match what is returned by {@link Languages#getSupportedLocales()}.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String[] getAllNames() {
|
||||||
|
return nameMap.values().toArray(new String[nameMap.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPosition(Locale locale) {
|
||||||
|
String localeName = locale.getLanguage();
|
||||||
|
int i = 0;
|
||||||
|
for (String key : nameMap.keySet())
|
||||||
|
if (TextUtils.equals(key, localeName))
|
||||||
|
return i;
|
||||||
|
else
|
||||||
|
i++;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get sorted list of supported locales.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String[] getSupportedLocales() {
|
||||||
|
Set<String> keys = nameMap.keySet();
|
||||||
|
return keys.toArray(new String[keys.size()]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,161 @@
|
||||||
|
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
package org.torproject.android.settings;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.CheckBoxPreference;
|
||||||
|
import android.preference.EditTextPreference;
|
||||||
|
import android.preference.ListPreference;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.Preference.OnPreferenceChangeListener;
|
||||||
|
import android.preference.Preference.OnPreferenceClickListener;
|
||||||
|
import android.preference.PreferenceActivity;
|
||||||
|
|
||||||
|
import org.torproject.android.OrbotApp;
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.AppManager;
|
||||||
|
import org.torproject.android.service.TorServiceUtils;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
|
||||||
|
public class SettingsPreferences
|
||||||
|
extends PreferenceActivity implements OnPreferenceClickListener {
|
||||||
|
private static final String TAG = "SettingsPreferences";
|
||||||
|
|
||||||
|
private CheckBoxPreference prefCBTransProxy = null;
|
||||||
|
private CheckBoxPreference prefcBTransProxyAll = null;
|
||||||
|
private Preference prefTransProxyFlush = null;
|
||||||
|
|
||||||
|
private Preference prefTransProxyApps = null;
|
||||||
|
private CheckBoxPreference prefHiddenServices = null;
|
||||||
|
private EditTextPreference prefHiddenServicesPorts;
|
||||||
|
private EditTextPreference prefHiddenServicesHostname;
|
||||||
|
private CheckBoxPreference prefRequestRoot = null;
|
||||||
|
private ListPreference prefLocale = null;
|
||||||
|
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
addPreferencesFromResource(R.xml.preferences);
|
||||||
|
getPreferenceManager().setSharedPreferencesMode(Context.MODE_MULTI_PROCESS);
|
||||||
|
SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
|
||||||
|
|
||||||
|
prefRequestRoot = (CheckBoxPreference) findPreference("has_root");
|
||||||
|
prefRequestRoot.setOnPreferenceClickListener(this);
|
||||||
|
|
||||||
|
prefLocale = (ListPreference) findPreference("pref_default_locale");
|
||||||
|
prefLocale.setOnPreferenceClickListener(this);
|
||||||
|
Languages languages = Languages.get(this);
|
||||||
|
prefLocale.setEntries(languages.getAllNames());
|
||||||
|
prefLocale.setEntryValues(languages.getSupportedLocales());
|
||||||
|
prefLocale.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
String language = (String) newValue;
|
||||||
|
if (preference == prefLocale) {
|
||||||
|
SharedPreferences settings = TorServiceUtils
|
||||||
|
.getSharedPrefs(getApplicationContext());
|
||||||
|
|
||||||
|
String lang = settings.getString("pref_default_locale",
|
||||||
|
Locale.getDefault().getLanguage());
|
||||||
|
OrbotApp app = (OrbotApp) getApplication();
|
||||||
|
Languages.setLanguage(app, language, true);
|
||||||
|
lang = settings.getString("pref_default_locale",
|
||||||
|
Locale.getDefault().getLanguage());
|
||||||
|
OrbotApp.forceChangeLanguage(SettingsPreferences.this);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
prefCBTransProxy = (CheckBoxPreference) findPreference("pref_transparent");
|
||||||
|
prefcBTransProxyAll = (CheckBoxPreference) findPreference("pref_transparent_all");
|
||||||
|
|
||||||
|
prefTransProxyFlush = (Preference) findPreference("pref_transproxy_flush");
|
||||||
|
prefTransProxyFlush.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference arg0) {
|
||||||
|
|
||||||
|
Intent data = new Intent();
|
||||||
|
data.putExtra("transproxywipe", true);
|
||||||
|
setResult(RESULT_OK, data);
|
||||||
|
|
||||||
|
finish();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
prefTransProxyApps = findPreference("pref_transparent_app_list");
|
||||||
|
prefTransProxyApps.setOnPreferenceClickListener(this);
|
||||||
|
prefCBTransProxy.setOnPreferenceClickListener(this);
|
||||||
|
prefcBTransProxyAll.setOnPreferenceClickListener(this);
|
||||||
|
prefHiddenServices = (CheckBoxPreference) findPreference("pref_hs_enable");
|
||||||
|
prefHiddenServices.setOnPreferenceClickListener(this);
|
||||||
|
prefHiddenServicesHostname = (EditTextPreference) findPreference("pref_hs_hostname");
|
||||||
|
|
||||||
|
|
||||||
|
prefCBTransProxy.setEnabled(prefRequestRoot.isChecked());
|
||||||
|
|
||||||
|
prefcBTransProxyAll.setEnabled(prefCBTransProxy.isChecked());
|
||||||
|
|
||||||
|
if (prefCBTransProxy.isChecked())
|
||||||
|
prefTransProxyApps.setEnabled((!prefcBTransProxyAll.isChecked()));
|
||||||
|
|
||||||
|
prefHiddenServicesPorts = (EditTextPreference) findPreference("pref_hs_ports");
|
||||||
|
prefHiddenServicesHostname.setEnabled(prefHiddenServices.isChecked());
|
||||||
|
prefHiddenServicesPorts.setEnabled(prefHiddenServices.isChecked());
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
{
|
||||||
|
prefTransProxyApps.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
|
||||||
|
if (preference == prefRequestRoot)
|
||||||
|
{
|
||||||
|
if (prefRequestRoot.isChecked())
|
||||||
|
{
|
||||||
|
|
||||||
|
prefCBTransProxy.setEnabled(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (preference == prefTransProxyApps)
|
||||||
|
{
|
||||||
|
startActivity(new Intent(this, AppManager.class));
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (preference == prefHiddenServices)
|
||||||
|
{
|
||||||
|
prefHiddenServicesPorts.setEnabled(prefHiddenServices.isChecked());
|
||||||
|
prefHiddenServicesHostname.setEnabled(prefHiddenServices.isChecked());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prefcBTransProxyAll.setEnabled(prefCBTransProxy.isChecked());
|
||||||
|
prefTransProxyApps.setEnabled(prefCBTransProxy.isChecked() && (!prefcBTransProxyAll.isChecked()));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,389 @@
|
||||||
|
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
package org.torproject.android.ui;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import org.torproject.android.service.OrbotConstants;
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.service.TorServiceUtils;
|
||||||
|
import org.torproject.android.service.TorifiedApp;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.SharedPreferences.Editor;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ListAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
public class AppManager extends Activity implements OnCheckedChangeListener, OnClickListener, OrbotConstants {
|
||||||
|
|
||||||
|
private ListView listApps;
|
||||||
|
private final static String TAG = "Orbot";
|
||||||
|
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
this.setContentView(R.layout.layout_apps);
|
||||||
|
|
||||||
|
Button buttonSelectAll, buttonSelectNone, buttonInvert;
|
||||||
|
|
||||||
|
buttonSelectAll = (Button) findViewById(R.id.button_proxy_all);
|
||||||
|
buttonSelectNone = (Button) findViewById(R.id.button_proxy_none);
|
||||||
|
buttonInvert = (Button) findViewById(R.id.button_invert_selection);
|
||||||
|
|
||||||
|
buttonSelectAll.setOnClickListener(new OnAutoClickListener(0));
|
||||||
|
buttonSelectNone.setOnClickListener(new OnAutoClickListener(1));
|
||||||
|
buttonInvert.setOnClickListener(new OnAutoClickListener(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
class OnAutoClickListener implements Button.OnClickListener {
|
||||||
|
private int status;
|
||||||
|
public OnAutoClickListener(int status){
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void onClick(View button){
|
||||||
|
ListView listView;
|
||||||
|
ViewGroup viewGroup;
|
||||||
|
View parentView, currentView;
|
||||||
|
ArrayAdapter<TorifiedApp> adapter;
|
||||||
|
TorifiedApp app;
|
||||||
|
CheckBox box;
|
||||||
|
float buttonId;
|
||||||
|
boolean[] isSelected;
|
||||||
|
int posI, selectedI, lvSz;
|
||||||
|
|
||||||
|
buttonId = button.getId();
|
||||||
|
listView = (ListView) findViewById(R.id.applistview);
|
||||||
|
lvSz = listView.getCount();
|
||||||
|
isSelected = new boolean[lvSz];
|
||||||
|
|
||||||
|
selectedI = -1;
|
||||||
|
|
||||||
|
if (this.status == 0){
|
||||||
|
Log.d(TAG, "Proxifying ALL");
|
||||||
|
}else if (this.status == 1){
|
||||||
|
Log.d(TAG, "Proxifying NONE");
|
||||||
|
}else {
|
||||||
|
Log.d(TAG, "Proxifying invert");
|
||||||
|
}
|
||||||
|
|
||||||
|
Context context = getApplicationContext();
|
||||||
|
SharedPreferences prefs = TorServiceUtils.getSharedPrefs(context);
|
||||||
|
ArrayList<TorifiedApp> apps = getApps(context, prefs);
|
||||||
|
parentView = (View) findViewById(R.id.applistview);
|
||||||
|
viewGroup = (ViewGroup) listView;
|
||||||
|
|
||||||
|
adapter = (ArrayAdapter<TorifiedApp>) listApps.getAdapter();
|
||||||
|
if (adapter == null){
|
||||||
|
Log.w(TAG, "List adapter is null. Getting apps.");
|
||||||
|
loadApps(prefs);
|
||||||
|
adapter = (ArrayAdapter<TorifiedApp>) listApps.getAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0 ; i < adapter.getCount(); ++i){
|
||||||
|
app = (TorifiedApp) adapter.getItem(i);
|
||||||
|
currentView = adapter.getView(i, parentView, viewGroup);
|
||||||
|
box = (CheckBox) currentView.findViewById(R.id.itemcheck);
|
||||||
|
if (this.status == 0){
|
||||||
|
if (!box.isChecked())
|
||||||
|
box.performClick();
|
||||||
|
}else if (this.status == 1){
|
||||||
|
if (box.isChecked())
|
||||||
|
box.performClick();
|
||||||
|
}else {
|
||||||
|
box.performClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
saveAppSettings(context);
|
||||||
|
loadApps(prefs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
listApps = (ListView)findViewById(R.id.applistview);
|
||||||
|
|
||||||
|
Button btnSave = (Button)findViewById(R.id.btnsave);
|
||||||
|
btnSave.setOnClickListener(new OnClickListener()
|
||||||
|
{
|
||||||
|
|
||||||
|
public void onClick(View v) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mPrefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
|
||||||
|
loadApps(mPrefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPreferences mPrefs = null;
|
||||||
|
ArrayList<TorifiedApp> mApps = null;
|
||||||
|
|
||||||
|
private void loadApps (SharedPreferences prefs)
|
||||||
|
{
|
||||||
|
|
||||||
|
mApps = getApps(getApplicationContext(), prefs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Arrays.sort(apps, new Comparator<TorifiedApp>() {
|
||||||
|
public int compare(TorifiedApp o1, TorifiedApp o2) {
|
||||||
|
if (o1.isTorified() == o2.isTorified()) return o1.getName().compareTo(o2.getName());
|
||||||
|
if (o1.isTorified()) return -1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
|
||||||
|
final LayoutInflater inflater = getLayoutInflater();
|
||||||
|
|
||||||
|
ListAdapter adapter = new ArrayAdapter<TorifiedApp>(this, R.layout.layout_apps_item, R.id.itemtext,mApps) {
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
ListEntry entry;
|
||||||
|
if (convertView == null) {
|
||||||
|
// Inflate a new view
|
||||||
|
convertView = inflater.inflate(R.layout.layout_apps_item, parent, false);
|
||||||
|
entry = new ListEntry();
|
||||||
|
entry.icon = (ImageView) convertView.findViewById(R.id.itemicon);
|
||||||
|
entry.box = (CheckBox) convertView.findViewById(R.id.itemcheck);
|
||||||
|
entry.text = (TextView) convertView.findViewById(R.id.itemtext);
|
||||||
|
|
||||||
|
entry.text.setOnClickListener(AppManager.this);
|
||||||
|
entry.text.setOnClickListener(AppManager.this);
|
||||||
|
|
||||||
|
convertView.setTag(entry);
|
||||||
|
|
||||||
|
entry.box.setOnCheckedChangeListener(AppManager.this);
|
||||||
|
} else {
|
||||||
|
// Convert an existing view
|
||||||
|
entry = (ListEntry) convertView.getTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final TorifiedApp app = mApps.get(position);
|
||||||
|
|
||||||
|
if (app.getIcon() != null)
|
||||||
|
entry.icon.setImageDrawable(app.getIcon());
|
||||||
|
else
|
||||||
|
entry.icon.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
entry.text.setText(app.getName());
|
||||||
|
|
||||||
|
final CheckBox box = entry.box;
|
||||||
|
box.setTag(app);
|
||||||
|
box.setChecked(app.isTorified());
|
||||||
|
|
||||||
|
entry.text.setTag(box);
|
||||||
|
entry.icon.setTag(box);
|
||||||
|
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
listApps.setAdapter(adapter);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ListEntry {
|
||||||
|
private CheckBox box;
|
||||||
|
private TextView text;
|
||||||
|
private ImageView icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see android.app.Activity#onStop()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static ArrayList<TorifiedApp> getApps (Context context, SharedPreferences prefs)
|
||||||
|
{
|
||||||
|
|
||||||
|
String tordAppString = prefs.getString(PREFS_KEY_TORIFIED, "");
|
||||||
|
String[] tordApps;
|
||||||
|
|
||||||
|
StringTokenizer st = new StringTokenizer(tordAppString,"|");
|
||||||
|
tordApps = new String[st.countTokens()];
|
||||||
|
int tordIdx = 0;
|
||||||
|
while (st.hasMoreTokens())
|
||||||
|
{
|
||||||
|
tordApps[tordIdx++] = st.nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
Arrays.sort(tordApps);
|
||||||
|
|
||||||
|
//else load the apps up
|
||||||
|
PackageManager pMgr = context.getPackageManager();
|
||||||
|
|
||||||
|
List<ApplicationInfo> lAppInfo = pMgr.getInstalledApplications(0);
|
||||||
|
|
||||||
|
Iterator<ApplicationInfo> itAppInfo = lAppInfo.iterator();
|
||||||
|
|
||||||
|
ArrayList<TorifiedApp> apps = new ArrayList<TorifiedApp>();
|
||||||
|
|
||||||
|
ApplicationInfo aInfo = null;
|
||||||
|
|
||||||
|
int appIdx = 0;
|
||||||
|
TorifiedApp app = null;
|
||||||
|
|
||||||
|
while (itAppInfo.hasNext())
|
||||||
|
{
|
||||||
|
aInfo = itAppInfo.next();
|
||||||
|
|
||||||
|
app = new TorifiedApp();
|
||||||
|
|
||||||
|
try {
|
||||||
|
PackageInfo pInfo = pMgr.getPackageInfo(aInfo.packageName, PackageManager.GET_PERMISSIONS);
|
||||||
|
|
||||||
|
if (pInfo != null && pInfo.requestedPermissions != null)
|
||||||
|
{
|
||||||
|
for (String permInfo:pInfo.requestedPermissions)
|
||||||
|
{
|
||||||
|
if (permInfo.equals("android.permission.INTERNET"))
|
||||||
|
{
|
||||||
|
app.setUsesInternet(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 1)
|
||||||
|
{
|
||||||
|
//System app
|
||||||
|
app.setUsesInternet(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!app.usesInternet())
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
apps.add(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
app.setEnabled(aInfo.enabled);
|
||||||
|
app.setUid(aInfo.uid);
|
||||||
|
app.setUsername(pMgr.getNameForUid(app.getUid()));
|
||||||
|
app.setProcname(aInfo.processName);
|
||||||
|
app.setPackageName(aInfo.packageName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
app.setName(pMgr.getApplicationLabel(aInfo).toString());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
app.setName(aInfo.packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//app.setIcon(pMgr.getApplicationIcon(aInfo));
|
||||||
|
|
||||||
|
// check if this application is allowed
|
||||||
|
if (Arrays.binarySearch(tordApps, app.getUsername()) >= 0) {
|
||||||
|
app.setTorified(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.setTorified(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
appIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(apps);
|
||||||
|
|
||||||
|
return apps;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void saveAppSettings (Context context)
|
||||||
|
{
|
||||||
|
|
||||||
|
StringBuilder tordApps = new StringBuilder();
|
||||||
|
|
||||||
|
for (TorifiedApp tApp:mApps)
|
||||||
|
{
|
||||||
|
if (tApp.isTorified())
|
||||||
|
{
|
||||||
|
tordApps.append(tApp.getUsername());
|
||||||
|
tordApps.append("|");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Editor edit = mPrefs.edit();
|
||||||
|
edit.putString(PREFS_KEY_TORIFIED, tordApps.toString());
|
||||||
|
edit.commit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called an application is check/unchecked
|
||||||
|
*/
|
||||||
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
|
final TorifiedApp app = (TorifiedApp) buttonView.getTag();
|
||||||
|
if (app != null) {
|
||||||
|
app.setTorified(isChecked);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAppSettings(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void onClick(View v) {
|
||||||
|
|
||||||
|
CheckBox cbox = (CheckBox)v.getTag();
|
||||||
|
|
||||||
|
final TorifiedApp app = (TorifiedApp)cbox.getTag();
|
||||||
|
if (app != null) {
|
||||||
|
app.setTorified(!app.isTorified());
|
||||||
|
cbox.setChecked(app.isTorified());
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAppSettings(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package org.torproject.android.ui;
|
||||||
|
|
||||||
|
public class BridgeSetupActivity {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package org.torproject.android.ui;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
public class ImageProgressView extends ImageView
|
||||||
|
{
|
||||||
|
|
||||||
|
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
|
||||||
|
private float progress = 0f; // 0 to 1
|
||||||
|
|
||||||
|
private RectF circle;
|
||||||
|
|
||||||
|
public ImageProgressView(Context context) {
|
||||||
|
super(context);
|
||||||
|
// TODO Auto-generated constructor stub
|
||||||
|
init();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageProgressView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageProgressView(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(){
|
||||||
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
|
paint.setColor(Color.GREEN);
|
||||||
|
paint.setAntiAlias(true);
|
||||||
|
paint.setStrokeWidth(20);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
|
||||||
|
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
|
||||||
|
MeasureSpec.getSize(heightMeasureSpec));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
|
||||||
|
super.onDraw(canvas);
|
||||||
|
|
||||||
|
if (circle == null)
|
||||||
|
{
|
||||||
|
circle = new RectF(getWidth()/2,getHeight()/2+getHeight()/8, getWidth()/3,getHeight()/3);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sweepAngle = 360f * progress;
|
||||||
|
|
||||||
|
canvas.drawArc(circle, 0, sweepAngle, true, paint);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,329 @@
|
||||||
|
package org.torproject.android.ui;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.service.TorResourceInstaller;
|
||||||
|
import org.torproject.android.service.TorServiceConstants;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.StatFs;
|
||||||
|
import android.text.format.Formatter;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
|
||||||
|
public class OrbotDiagnosticsActivity extends Activity {
|
||||||
|
|
||||||
|
private TextView mTextView = null;
|
||||||
|
private final static String TAG = "OrbotDiag";
|
||||||
|
private StringBuffer log = new StringBuffer();
|
||||||
|
Process mProcess;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.layout_diag);
|
||||||
|
|
||||||
|
mTextView = (TextView)findViewById(R.id.diaglog);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFreeStorage ()
|
||||||
|
{
|
||||||
|
File path = Environment.getDataDirectory();
|
||||||
|
StatFs stat = new StatFs(path.getPath());
|
||||||
|
long blockSize = stat.getBlockSize();
|
||||||
|
long availableBlocks = stat.getAvailableBlocks();
|
||||||
|
return Formatter.formatFileSize(this, availableBlocks * blockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
stopTor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopTor ()
|
||||||
|
{
|
||||||
|
File appBinHome = this.getDir("bin", Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
File fileTor= new File(appBinHome, TorServiceConstants.TOR_ASSET_KEY);
|
||||||
|
|
||||||
|
if (mProcess != null)
|
||||||
|
mProcess.destroy();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
|
||||||
|
log("Hello, Orbot!");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log(android.os.Build.DEVICE);
|
||||||
|
log(android.os.Build.HARDWARE);
|
||||||
|
log(android.os.Build.MANUFACTURER);
|
||||||
|
log(android.os.Build.MODEL);
|
||||||
|
log(android.os.Build.VERSION.CODENAME);
|
||||||
|
log(android.os.Build.VERSION.RELEASE);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log("error getting device info");
|
||||||
|
}
|
||||||
|
|
||||||
|
showFileTree ();
|
||||||
|
|
||||||
|
runTorTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runTorTest ()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File appBinHome = this.getDir("bin", Context.MODE_PRIVATE);
|
||||||
|
File appDataHome = this.getDir("data", Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
File fileTor= new File(appBinHome, TorServiceConstants.TOR_ASSET_KEY);
|
||||||
|
enableBinExec (fileTor, appBinHome);
|
||||||
|
|
||||||
|
InputStream is = getResources().openRawResource(R.raw.torrc);
|
||||||
|
File fileTorrc = new File(appBinHome, TorServiceConstants.TORRC_ASSET_KEY + "diag");
|
||||||
|
TorResourceInstaller.streamToFile(is,fileTorrc, false, false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
ArrayList<String> alEnv = new ArrayList<String>();
|
||||||
|
alEnv.add("HOME=" + appBinHome.getAbsolutePath());
|
||||||
|
Shell shell = Shell.startShell(alEnv,appBinHome.getAbsolutePath());
|
||||||
|
SimpleCommand cmdTor = new SimpleCommand(fileTor.getAbsolutePath() + " DataDirectory " + appDataHome.getAbsolutePath() + " -f " + fileTorrc.getAbsolutePath());
|
||||||
|
shell.add(cmdTor);
|
||||||
|
**/
|
||||||
|
|
||||||
|
String cmd = fileTor.getAbsolutePath() + " DataDirectory " + appDataHome.getAbsolutePath() + " -f " + fileTorrc.getAbsolutePath();
|
||||||
|
|
||||||
|
log ("Executing command> " + cmd);
|
||||||
|
|
||||||
|
mProcess = Runtime.getRuntime().exec(cmd);
|
||||||
|
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
|
||||||
|
StreamGobbler sg = new StreamGobbler();
|
||||||
|
sg.reader = bufferedReader;
|
||||||
|
sg.process = mProcess;
|
||||||
|
new Thread(sg).start();
|
||||||
|
|
||||||
|
if (mProcess.getErrorStream() != null)
|
||||||
|
{
|
||||||
|
bufferedReader = new BufferedReader(new InputStreamReader(mProcess.getErrorStream()));
|
||||||
|
sg = new StreamGobbler();
|
||||||
|
sg.reader = bufferedReader;
|
||||||
|
sg.process = mProcess;
|
||||||
|
new Thread(sg).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.d(TAG,"runTorTest exception",e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class StreamGobbler implements Runnable
|
||||||
|
{
|
||||||
|
BufferedReader reader;
|
||||||
|
Process process;
|
||||||
|
|
||||||
|
public void run ()
|
||||||
|
{
|
||||||
|
String line = null;
|
||||||
|
try {
|
||||||
|
while ( (line = reader.readLine()) != null)
|
||||||
|
{
|
||||||
|
Message msg = mHandler.obtainMessage(0);
|
||||||
|
msg.getData().putString("log", line);
|
||||||
|
mHandler.sendMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.d(TAG, "error reading line",e);
|
||||||
|
}
|
||||||
|
|
||||||
|
//log("Tor exit code=" + process.exitValue() + ";");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean enableBinExec (File fileBin, File appBinHome) throws Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
log(fileBin.getName() + ": PRE: Is binary exec? " + fileBin.canExecute());
|
||||||
|
|
||||||
|
if (!fileBin.canExecute())
|
||||||
|
{
|
||||||
|
log("(re)Setting permission on binary: " + fileBin.getAbsolutePath());
|
||||||
|
|
||||||
|
Runtime.getRuntime().exec("chmod " + TorServiceConstants.CHMOD_EXE_VALUE + ' ' + fileBin.getAbsolutePath()).waitFor();
|
||||||
|
|
||||||
|
File fileTest = new File(fileBin.getAbsolutePath());
|
||||||
|
log(fileTest.getName() + ": POST: Is binary exec? " + fileTest.canExecute());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileBin.canExecute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showFileTree ()
|
||||||
|
{
|
||||||
|
|
||||||
|
File fileDir = this.getDir("bin", Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
if (fileDir.exists())
|
||||||
|
{
|
||||||
|
log("checking file tree: " + fileDir.getAbsolutePath());
|
||||||
|
printDir (fileDir.getName(), fileDir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log("app_bin does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDir = this.getDir("data", Context.MODE_PRIVATE);
|
||||||
|
if (fileDir.exists())
|
||||||
|
{
|
||||||
|
log("checking file tree: " + fileDir.getAbsolutePath());
|
||||||
|
printDir (fileDir.getName(), fileDir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log ("app_data does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printDir (String path, File fileDir)
|
||||||
|
{
|
||||||
|
File[] files = fileDir.listFiles();
|
||||||
|
|
||||||
|
if (files != null && files.length > 0)
|
||||||
|
{
|
||||||
|
for (File file : files)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (file.isDirectory())
|
||||||
|
{
|
||||||
|
printDir(path + '/' + file.getName(), file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log(path + '/' + file.getName() + " len:" + file.length() + " exec:" + file.canExecute());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log("problem printing out file information");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Handler mHandler = new Handler ()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
|
||||||
|
super.handleMessage(msg);
|
||||||
|
|
||||||
|
String logMsg = msg.getData().getString("log");
|
||||||
|
log(logMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
private void log (String msg)
|
||||||
|
{
|
||||||
|
Log.d(TAG, msg);
|
||||||
|
mTextView.append(msg + '\n');
|
||||||
|
log.append(msg + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
// Inflate menu resource file.
|
||||||
|
getMenuInflater().inflate(R.menu.share_menu, menu);
|
||||||
|
|
||||||
|
// Locate MenuItem with ShareActionProvider
|
||||||
|
MenuItem item = menu.findItem(R.id.menu_item_share);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
|
||||||
|
case R.id.menu_item_share:
|
||||||
|
sendLog();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendLog ()
|
||||||
|
{
|
||||||
|
int maxLength = 5000;
|
||||||
|
|
||||||
|
String logShare = null;
|
||||||
|
|
||||||
|
if (log.length() > maxLength)
|
||||||
|
logShare = log.substring(0, maxLength);
|
||||||
|
else
|
||||||
|
logShare = log.toString();
|
||||||
|
|
||||||
|
Intent sendIntent = new Intent();
|
||||||
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, logShare);
|
||||||
|
sendIntent.setType("text/plain");
|
||||||
|
startActivity(sendIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,215 @@
|
||||||
|
package org.torproject.android.ui;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import org.torproject.android.service.OrbotConstants;
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.service.TorServiceConstants;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PromoAppsActivity extends Activity implements OrbotConstants {
|
||||||
|
|
||||||
|
final static String MARKET_URI = "market://details?id=";
|
||||||
|
final static String FDROID_APP_URI = "https://f-droid.org/repository/browse/?fdid=";
|
||||||
|
final static String PLAY_APP_URI = "https://play.google.com/store/apps/details?id=";
|
||||||
|
final static String FDROID_URI = "https://f-droid.org/repository/browse/?fdfilter=info.guardianproject";
|
||||||
|
final static String PLAY_URI = "https://play.google.com/store/apps/developer?id=The+Guardian+Project";
|
||||||
|
|
||||||
|
private final static String FDROID_PACKAGE_NAME = "org.fdroid.fdroid";
|
||||||
|
private final static String PLAY_PACKAGE_NAME = "com.android.vending";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
|
||||||
|
super.onStart();
|
||||||
|
setContentView(R.layout.layout_promo_apps);
|
||||||
|
|
||||||
|
stepFive();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void stepFive(){
|
||||||
|
|
||||||
|
|
||||||
|
String title = getString(R.string.wizard_tips_title);
|
||||||
|
|
||||||
|
setTitle(title);
|
||||||
|
|
||||||
|
Button btnLink = (Button)findViewById(R.id.WizardRootButtonInstallGibberbot);
|
||||||
|
|
||||||
|
btnLink.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
finish();
|
||||||
|
startActivity(getInstallIntent("info.guardianproject.otr.app.im"));
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnLink = (Button)findViewById(R.id.WizardRootButtonInstallOrweb);
|
||||||
|
|
||||||
|
btnLink.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
finish();
|
||||||
|
startActivity(getInstallIntent(TorServiceConstants.BROWSER_APP_USERNAME));
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnLink = (Button)findViewById(R.id.WizardRootButtonInstallDuckgo);
|
||||||
|
|
||||||
|
btnLink.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
|
||||||
|
finish();
|
||||||
|
startActivity(getInstallIntent("com.duckduckgo.mobile.android"));
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnLink = (Button)findViewById(R.id.WizardRootButtonInstallTwitter);
|
||||||
|
|
||||||
|
btnLink.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
|
||||||
|
String url = getString(R.string.twitter_setup_url);
|
||||||
|
finish();
|
||||||
|
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnLink = (Button)findViewById(R.id.WizardRootButtonInstallStoryMaker);
|
||||||
|
|
||||||
|
btnLink.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
finish();
|
||||||
|
startActivity(getInstallIntent("info.guardianproject.mrapp"));
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnLink = (Button)findViewById(R.id.WizardRootButtonInstallMartus);
|
||||||
|
|
||||||
|
btnLink.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
finish();
|
||||||
|
startActivity(getInstallIntent("org.martus.android"));
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnLink = (Button)findViewById(R.id.WizardRootButtonGooglePlay);
|
||||||
|
PackageManager pm = getPackageManager();
|
||||||
|
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
// change text and icon based on which app store is installed (or not)
|
||||||
|
try {
|
||||||
|
if (isAppInstalled(pm, FDROID_PACKAGE_NAME)) {
|
||||||
|
Drawable icon = pm.getApplicationIcon(FDROID_PACKAGE_NAME);
|
||||||
|
btnLink.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
|
||||||
|
btnLink.setText(R.string.wizard_tips_fdroid);
|
||||||
|
intent.setPackage(FDROID_PACKAGE_NAME);
|
||||||
|
intent.setData(Uri.parse(FDROID_URI));
|
||||||
|
} else if (isAppInstalled(pm, PLAY_PACKAGE_NAME)) {
|
||||||
|
Drawable icon = pm.getApplicationIcon(PLAY_PACKAGE_NAME);
|
||||||
|
btnLink.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
|
||||||
|
btnLink.setText(R.string.wizard_tips_play);
|
||||||
|
intent.setPackage(PLAY_PACKAGE_NAME);
|
||||||
|
intent.setData(Uri.parse(PLAY_URI));
|
||||||
|
}
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
btnLink.setText(R.string.wizard_tips_fdroid_org);
|
||||||
|
intent.setData(Uri.parse(FDROID_URI));
|
||||||
|
}
|
||||||
|
|
||||||
|
btnLink.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button next = ((Button)findViewById(R.id.btnWizard2));
|
||||||
|
next.setOnClickListener(new View.OnClickListener() {
|
||||||
|
|
||||||
|
public void onClick(View v) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAppInstalled(PackageManager pm, String packageName) {
|
||||||
|
try {
|
||||||
|
pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
|
||||||
|
return true;
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent getInstallIntent(String packageName) {
|
||||||
|
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(MARKET_URI + packageName));
|
||||||
|
|
||||||
|
PackageManager pm = getPackageManager();
|
||||||
|
List<ResolveInfo> resInfos = pm.queryIntentActivities(intent, 0);
|
||||||
|
|
||||||
|
String foundPackageName = null;
|
||||||
|
for (ResolveInfo r : resInfos) {
|
||||||
|
Log.i(TAG, "market: " + r.activityInfo.packageName);
|
||||||
|
if (TextUtils.equals(r.activityInfo.packageName, FDROID_PACKAGE_NAME)
|
||||||
|
|| TextUtils.equals(r.activityInfo.packageName, PLAY_PACKAGE_NAME)) {
|
||||||
|
foundPackageName = r.activityInfo.packageName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundPackageName == null) {
|
||||||
|
intent.setData(Uri.parse(FDROID_APP_URI + packageName));
|
||||||
|
} else {
|
||||||
|
intent.setPackage(foundPackageName);
|
||||||
|
}
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package org.torproject.android.ui;
|
||||||
|
|
||||||
|
import android.graphics.Camera;
|
||||||
|
import android.graphics.Matrix;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.Transformation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An animation that rotates the view on the Y axis between two specified angles.
|
||||||
|
* This animation also adds a translation on the Z axis (depth) to improve the effect.
|
||||||
|
*/
|
||||||
|
public class Rotate3dAnimation extends Animation {
|
||||||
|
private final float mFromDegrees;
|
||||||
|
private final float mToDegrees;
|
||||||
|
private final float mCenterX;
|
||||||
|
private final float mCenterY;
|
||||||
|
private final float mDepthZ;
|
||||||
|
private final boolean mReverse;
|
||||||
|
private Camera mCamera;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new 3D rotation on the Y axis. The rotation is defined by its
|
||||||
|
* start angle and its end angle. Both angles are in degrees. The rotation
|
||||||
|
* is performed around a center point on the 2D space, definied by a pair
|
||||||
|
* of X and Y coordinates, called centerX and centerY. When the animation
|
||||||
|
* starts, a translation on the Z axis (depth) is performed. The length
|
||||||
|
* of the translation can be specified, as well as whether the translation
|
||||||
|
* should be reversed in time.
|
||||||
|
*
|
||||||
|
* @param fromDegrees the start angle of the 3D rotation
|
||||||
|
* @param toDegrees the end angle of the 3D rotation
|
||||||
|
* @param centerX the X center of the 3D rotation
|
||||||
|
* @param centerY the Y center of the 3D rotation
|
||||||
|
* @param reverse true if the translation should be reversed, false otherwise
|
||||||
|
*/
|
||||||
|
public Rotate3dAnimation(float fromDegrees, float toDegrees,
|
||||||
|
float centerX, float centerY, float depthZ, boolean reverse) {
|
||||||
|
mFromDegrees = fromDegrees;
|
||||||
|
mToDegrees = toDegrees;
|
||||||
|
mCenterX = centerX;
|
||||||
|
mCenterY = centerY;
|
||||||
|
mDepthZ = depthZ;
|
||||||
|
mReverse = reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(int width, int height, int parentWidth, int parentHeight) {
|
||||||
|
super.initialize(width, height, parentWidth, parentHeight);
|
||||||
|
mCamera = new Camera();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void applyTransformation(float interpolatedTime, Transformation t) {
|
||||||
|
final float fromDegrees = mFromDegrees;
|
||||||
|
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
|
||||||
|
|
||||||
|
final float centerX = mCenterX;
|
||||||
|
final float centerY = mCenterY;
|
||||||
|
final Camera camera = mCamera;
|
||||||
|
|
||||||
|
final Matrix matrix = t.getMatrix();
|
||||||
|
|
||||||
|
camera.save();
|
||||||
|
if (mReverse) {
|
||||||
|
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
|
||||||
|
} else {
|
||||||
|
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
|
||||||
|
}
|
||||||
|
camera.rotateY(degrees);
|
||||||
|
camera.getMatrix(matrix);
|
||||||
|
camera.restore();
|
||||||
|
|
||||||
|
matrix.preTranslate(-centerX, -centerY);
|
||||||
|
matrix.postTranslate(centerX, centerY);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package org.torproject.android.ui;
|
||||||
|
|
||||||
|
public class VPNSetupActivity {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,452 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.torproject.android.vpn;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.torproject.android.OrbotApp;
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.service.TorService;
|
||||||
|
import org.torproject.android.service.TorServiceConstants;
|
||||||
|
import org.torproject.android.service.TorServiceUtils;
|
||||||
|
import org.torproject.android.service.TorifiedApp;
|
||||||
|
import org.torproject.android.ui.AppManager;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.net.VpnService;
|
||||||
|
import android.net.VpnService.Builder;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.runjva.sourceforge.jsocks.protocol.ProxyServer;
|
||||||
|
import com.runjva.sourceforge.jsocks.server.ServerAuthenticatorNone;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
|
public class OrbotVpnManager implements Handler.Callback {
|
||||||
|
private static final String TAG = "OrbotVpnService";
|
||||||
|
|
||||||
|
private PendingIntent mConfigureIntent;
|
||||||
|
|
||||||
|
private Thread mThreadVPN;
|
||||||
|
|
||||||
|
private String mSessionName = "OrbotVPN";
|
||||||
|
private ParcelFileDescriptor mInterface;
|
||||||
|
|
||||||
|
private int mTorSocks = TorServiceConstants.SOCKS_PROXY_PORT_DEFAULT;
|
||||||
|
|
||||||
|
public static int sSocksProxyServerPort = -1;
|
||||||
|
public static String sSocksProxyLocalhost = null;
|
||||||
|
private ProxyServer mSocksProxyServer;
|
||||||
|
|
||||||
|
|
||||||
|
private final static int VPN_MTU = 1500;
|
||||||
|
|
||||||
|
private final static boolean mIsLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
|
||||||
|
|
||||||
|
//this is the actual DNS server we talk to over UDP or TCP (now using Tor's DNS port)
|
||||||
|
private final static String DEFAULT_ACTUAL_DNS_HOST = "127.0.0.1";
|
||||||
|
private final static int DEFAULT_ACTUAL_DNS_PORT = TorServiceConstants.TOR_DNS_PORT_DEFAULT;
|
||||||
|
|
||||||
|
private boolean isRestart = false;
|
||||||
|
|
||||||
|
private VpnService mService;
|
||||||
|
|
||||||
|
|
||||||
|
static{
|
||||||
|
System.loadLibrary("tun2socks");
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrbotVpnManager (VpnService service)
|
||||||
|
{
|
||||||
|
mService = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
//public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
public int handleIntent(Builder builder, Intent intent) {
|
||||||
|
|
||||||
|
if (intent != null)
|
||||||
|
{
|
||||||
|
String action = intent.getAction();
|
||||||
|
|
||||||
|
if (action.equals("start"))
|
||||||
|
{
|
||||||
|
|
||||||
|
// Stop the previous session by interrupting the thread.
|
||||||
|
if (mThreadVPN == null || (!mThreadVPN.isAlive()))
|
||||||
|
{
|
||||||
|
Log.d(TAG,"starting OrbotVPNService service!");
|
||||||
|
|
||||||
|
mTorSocks = intent.getIntExtra("torSocks", TorServiceConstants.SOCKS_PROXY_PORT_DEFAULT);
|
||||||
|
|
||||||
|
if (!mIsLollipop)
|
||||||
|
{
|
||||||
|
|
||||||
|
startSocksBypass();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupTun2Socks(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (action.equals("stop"))
|
||||||
|
{
|
||||||
|
Log.d(TAG,"stop OrbotVPNService service!");
|
||||||
|
|
||||||
|
stopVPN();
|
||||||
|
//if (mHandler != null)
|
||||||
|
//mHandler.postDelayed(new Runnable () { public void run () { stopSelf(); }}, 1000);
|
||||||
|
}
|
||||||
|
else if (action.equals("refresh"))
|
||||||
|
{
|
||||||
|
Log.d(TAG,"refresh OrbotVPNService service!");
|
||||||
|
|
||||||
|
if (!mIsLollipop)
|
||||||
|
startSocksBypass();
|
||||||
|
|
||||||
|
if (!isRestart)
|
||||||
|
setupTun2Socks(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Service.START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startSocksBypass()
|
||||||
|
{
|
||||||
|
|
||||||
|
new Thread ()
|
||||||
|
{
|
||||||
|
|
||||||
|
public void run ()
|
||||||
|
{
|
||||||
|
|
||||||
|
//generate the proxy port that the
|
||||||
|
if (sSocksProxyServerPort == -1)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
sSocksProxyLocalhost = "127.0.0.1";// InetAddress.getLocalHost().getHostAddress();
|
||||||
|
sSocksProxyServerPort = (int)((Math.random()*1000)+10000);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG,"Unable to access localhost",e);
|
||||||
|
throw new RuntimeException("Unable to access localhost: " + e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (mSocksProxyServer != null)
|
||||||
|
{
|
||||||
|
stopSocksBypass ();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mSocksProxyServer = new ProxyServer(new ServerAuthenticatorNone(null, null));
|
||||||
|
ProxyServer.setVpnService(mService);
|
||||||
|
mSocksProxyServer.start(sSocksProxyServerPort, 5, InetAddress.getLocalHost());
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.e(TAG,"error getting host",e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void stopSocksBypass ()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (mSocksProxyServer != null){
|
||||||
|
mSocksProxyServer.stop();
|
||||||
|
mSocksProxyServer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
// Set the locale to English (or probably any other language that^M
|
||||||
|
// uses Hindu-Arabic (aka Latin) numerals).^M
|
||||||
|
// We have found that VpnService.Builder does something locale-dependent^M
|
||||||
|
// internally that causes errors when the locale uses its own numerals^M
|
||||||
|
// (i.e., Farsi and Arabic).^M
|
||||||
|
Locale.setDefault(new Locale("en"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
stopVPN();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
private void stopVPN ()
|
||||||
|
{
|
||||||
|
if (mIsLollipop)
|
||||||
|
stopSocksBypass ();
|
||||||
|
|
||||||
|
if (mInterface != null){
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.d(TAG,"closing interface, destroying VPN interface");
|
||||||
|
|
||||||
|
mInterface.close();
|
||||||
|
mInterface = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.d(TAG,"error stopping tun2socks",e);
|
||||||
|
}
|
||||||
|
catch (Error e)
|
||||||
|
{
|
||||||
|
Log.d(TAG,"error stopping tun2socks",e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tun2Socks.Stop();
|
||||||
|
|
||||||
|
try {
|
||||||
|
TorServiceUtils.killProcess(filePdnsd);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
mThreadVPN = null;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleMessage(Message message) {
|
||||||
|
if (message != null) {
|
||||||
|
Toast.makeText(mService, message.what, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private synchronized void setupTun2Socks(final Builder builder) {
|
||||||
|
|
||||||
|
|
||||||
|
if (mInterface != null) //stop tun2socks now to give it time to clean up
|
||||||
|
{
|
||||||
|
isRestart = true;
|
||||||
|
Tun2Socks.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
mThreadVPN = new Thread ()
|
||||||
|
{
|
||||||
|
|
||||||
|
public void run ()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
if (isRestart)
|
||||||
|
{
|
||||||
|
Log.d(TAG,"is a restart... let's wait for a few seconds");
|
||||||
|
Thread.sleep(3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
//start PDNSD daemon pointing to actual DNS
|
||||||
|
startDNS(DEFAULT_ACTUAL_DNS_HOST,DEFAULT_ACTUAL_DNS_PORT);
|
||||||
|
|
||||||
|
final String vpnName = "OrbotVPN";
|
||||||
|
final String localhost = "127.0.0.1";
|
||||||
|
|
||||||
|
final String virtualGateway = "10.10.10.1";
|
||||||
|
final String virtualIP = "10.10.10.2";
|
||||||
|
final String virtualNetMask = "255.255.255.0";
|
||||||
|
final String dummyDNS = "8.8.8.8"; //this is intercepted by the tun2socks library, but we must put in a valid DNS to start
|
||||||
|
final String defaultRoute = "0.0.0.0";
|
||||||
|
|
||||||
|
final String localSocks = localhost + ':'
|
||||||
|
+ String.valueOf(mTorSocks);
|
||||||
|
|
||||||
|
final String localDNS = virtualGateway + ':' + "8091";//String.valueOf(TorServiceConstants.TOR_DNS_PORT_DEFAULT);
|
||||||
|
final boolean localDnsTransparentProxy = true;
|
||||||
|
|
||||||
|
builder.setMtu(VPN_MTU);
|
||||||
|
builder.addAddress(virtualGateway,32);
|
||||||
|
|
||||||
|
builder.setSession(vpnName);
|
||||||
|
|
||||||
|
builder.addDnsServer(dummyDNS);
|
||||||
|
builder.addRoute(dummyDNS,32);
|
||||||
|
|
||||||
|
//route all traffic through VPN (we might offer country specific exclude lists in the future)
|
||||||
|
builder.addRoute(defaultRoute,0);
|
||||||
|
|
||||||
|
//handle ipv6
|
||||||
|
//builder.addAddress("fdfe:dcba:9876::1", 126);
|
||||||
|
//builder.addRoute("::", 0);
|
||||||
|
|
||||||
|
if (mIsLollipop)
|
||||||
|
doLollipopAppRouting(builder);
|
||||||
|
|
||||||
|
// Create a new interface using the builder and save the parameters.
|
||||||
|
ParcelFileDescriptor newInterface = builder.setSession(mSessionName)
|
||||||
|
.setConfigureIntent(mConfigureIntent)
|
||||||
|
.establish();
|
||||||
|
|
||||||
|
if (mInterface != null)
|
||||||
|
{
|
||||||
|
Log.d(TAG,"Stopping existing VPN interface");
|
||||||
|
mInterface.close();
|
||||||
|
mInterface = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mInterface = newInterface;
|
||||||
|
|
||||||
|
Tun2Socks.Start(mInterface, VPN_MTU, virtualIP, virtualNetMask, localSocks , localDNS , localDnsTransparentProxy);
|
||||||
|
|
||||||
|
isRestart = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.d(TAG,"tun2Socks has stopped",e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
mThreadVPN.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
private void doLollipopAppRouting (Builder builder) throws NameNotFoundException
|
||||||
|
{
|
||||||
|
|
||||||
|
ArrayList<TorifiedApp> apps = AppManager.getApps(mService, TorServiceUtils.getSharedPrefs(mService.getApplicationContext()));
|
||||||
|
|
||||||
|
boolean perAppEnabled = false;
|
||||||
|
|
||||||
|
for (TorifiedApp app : apps)
|
||||||
|
{
|
||||||
|
if (app.isTorified() && (!app.getPackageName().equals(mService.getPackageName())))
|
||||||
|
{
|
||||||
|
builder.addAllowedApplication(app.getPackageName());
|
||||||
|
perAppEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!perAppEnabled)
|
||||||
|
builder.addDisallowedApplication(mService.getPackageName());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void onRevoke() {
|
||||||
|
|
||||||
|
Log.w(TAG,"VPNService REVOKED!");
|
||||||
|
|
||||||
|
if (!isRestart)
|
||||||
|
{
|
||||||
|
SharedPreferences prefs = TorServiceUtils.getSharedPrefs(mService.getApplicationContext());
|
||||||
|
prefs.edit().putBoolean("pref_vpn", false).commit();
|
||||||
|
stopVPN();
|
||||||
|
}
|
||||||
|
|
||||||
|
isRestart = false;
|
||||||
|
|
||||||
|
//super.onRevoke();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
File filePdnsd = null;
|
||||||
|
|
||||||
|
private void startDNS (String dns, int port) throws IOException, TimeoutException
|
||||||
|
{
|
||||||
|
File filePdnsd = null;//getDir(TorServiceConstants.DIRECTORY_TOR_BINARY, Application.MODE_PRIVATE);
|
||||||
|
|
||||||
|
makePdnsdConf(mService, dns, port,filePdnsd.getParentFile() );
|
||||||
|
|
||||||
|
ArrayList<String> customEnv = new ArrayList<String>();
|
||||||
|
String baseDirectory = filePdnsd.getParent();
|
||||||
|
|
||||||
|
String cmdString = "sh " + filePdnsd.getCanonicalPath() +
|
||||||
|
" -c " + baseDirectory + "/pdnsd.conf";
|
||||||
|
|
||||||
|
Process proc = Runtime.getRuntime().exec(cmdString);
|
||||||
|
try { proc.waitFor();} catch (Exception e){}
|
||||||
|
|
||||||
|
Log.i(TAG,"PDNSD: " + proc.exitValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void makePdnsdConf(Context context, String dns, int port, File fileDir) throws FileNotFoundException {
|
||||||
|
String conf = String.format(context.getString(R.string.pdnsd_conf), dns, port);
|
||||||
|
|
||||||
|
File f = new File(fileDir,"pdnsd.conf");
|
||||||
|
|
||||||
|
if (f.exists()) {
|
||||||
|
f.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileOutputStream fos = new FileOutputStream(f, false);
|
||||||
|
PrintStream ps = new PrintStream(fos);
|
||||||
|
ps.print(conf);
|
||||||
|
ps.close();
|
||||||
|
|
||||||
|
//f.withWriter { out -> out.print conf };
|
||||||
|
|
||||||
|
|
||||||
|
File cache = new File(fileDir,"pdnsd.cache");
|
||||||
|
|
||||||
|
if (!cache.exists()) {
|
||||||
|
try {
|
||||||
|
cache.createNewFile();
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
package org.torproject.android.vpn;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, Psiphon Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
|
||||||
|
public class Tun2Socks
|
||||||
|
{
|
||||||
|
public static interface IProtectSocket
|
||||||
|
{
|
||||||
|
boolean doVpnProtect(Socket socket);
|
||||||
|
boolean doVpnProtect(DatagramSocket socket);
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String TAG = Tun2Socks.class.getSimpleName();
|
||||||
|
private static final boolean LOGD = true;
|
||||||
|
|
||||||
|
private static Thread mThread;
|
||||||
|
private static ParcelFileDescriptor mVpnInterfaceFileDescriptor;
|
||||||
|
private static int mVpnInterfaceMTU;
|
||||||
|
private static String mVpnIpAddress;
|
||||||
|
private static String mVpnNetMask;
|
||||||
|
private static String mSocksServerAddress;
|
||||||
|
private static String mUdpgwServerAddress;
|
||||||
|
private static boolean mUdpgwTransparentDNS;
|
||||||
|
|
||||||
|
// Note: this class isn't a singleton, but you can't run more
|
||||||
|
// than one instance due to the use of global state (the lwip
|
||||||
|
// module, etc.) in the native code.
|
||||||
|
|
||||||
|
private static boolean mLibLoaded = false;
|
||||||
|
|
||||||
|
public static void Start(
|
||||||
|
ParcelFileDescriptor vpnInterfaceFileDescriptor,
|
||||||
|
int vpnInterfaceMTU,
|
||||||
|
String vpnIpAddress,
|
||||||
|
String vpnNetMask,
|
||||||
|
String socksServerAddress,
|
||||||
|
String udpgwServerAddress,
|
||||||
|
boolean udpgwTransparentDNS)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!mLibLoaded)
|
||||||
|
{
|
||||||
|
System.loadLibrary("tun2socks");
|
||||||
|
mLibLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mVpnInterfaceFileDescriptor = vpnInterfaceFileDescriptor;
|
||||||
|
mVpnInterfaceMTU = vpnInterfaceMTU;
|
||||||
|
mVpnIpAddress = vpnIpAddress;
|
||||||
|
mVpnNetMask = vpnNetMask;
|
||||||
|
mSocksServerAddress = socksServerAddress;
|
||||||
|
mUdpgwServerAddress = udpgwServerAddress;
|
||||||
|
mUdpgwTransparentDNS = udpgwTransparentDNS;
|
||||||
|
|
||||||
|
if (mVpnInterfaceFileDescriptor != null)
|
||||||
|
runTun2Socks(
|
||||||
|
mVpnInterfaceFileDescriptor.detachFd(),
|
||||||
|
mVpnInterfaceMTU,
|
||||||
|
mVpnIpAddress,
|
||||||
|
mVpnNetMask,
|
||||||
|
mSocksServerAddress,
|
||||||
|
mUdpgwServerAddress,
|
||||||
|
mUdpgwTransparentDNS ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Stop()
|
||||||
|
{
|
||||||
|
|
||||||
|
terminateTun2Socks();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void logTun2Socks(
|
||||||
|
String level,
|
||||||
|
String channel,
|
||||||
|
String msg)
|
||||||
|
{
|
||||||
|
String logMsg = level + "(" + channel + "): " + msg;
|
||||||
|
if (0 == level.compareTo("ERROR"))
|
||||||
|
{
|
||||||
|
Log.e(TAG, logMsg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (LOGD) Log.d(TAG, logMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private native static int runTun2Socks(
|
||||||
|
int vpnInterfaceFileDescriptor,
|
||||||
|
int vpnInterfaceMTU,
|
||||||
|
String vpnIpAddress,
|
||||||
|
String vpnNetMask,
|
||||||
|
String socksServerAddress,
|
||||||
|
String udpgwServerAddress,
|
||||||
|
int udpgwTransparentDNS);
|
||||||
|
|
||||||
|
private native static void terminateTun2Socks();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
package org.torproject.android.vpn;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.service.Prefs;
|
||||||
|
import org.torproject.android.service.TorService;
|
||||||
|
import org.torproject.android.service.TorServiceConstants;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.VpnService;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To combat background service being stopped/swiped
|
||||||
|
*/
|
||||||
|
public class VPNEnableActivity extends Activity {
|
||||||
|
|
||||||
|
private final static int REQUEST_VPN = 7777;
|
||||||
|
private Intent intent = null;
|
||||||
|
private boolean checkVpn = true;
|
||||||
|
private Handler h = new Handler();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate( Bundle icicle ) {
|
||||||
|
|
||||||
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
//getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
|
|
||||||
|
super.onCreate( icicle );
|
||||||
|
|
||||||
|
Log.d("VPNEnableActivity","prompting user to start Orbot VPN");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onResume ()
|
||||||
|
{
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
if (checkVpn)
|
||||||
|
{
|
||||||
|
intent = VpnService.prepare(this);
|
||||||
|
|
||||||
|
if (intent != null)
|
||||||
|
promptStartVpnService();
|
||||||
|
else
|
||||||
|
startVpnService ();
|
||||||
|
|
||||||
|
checkVpn = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void promptStartVpnService ()
|
||||||
|
{
|
||||||
|
|
||||||
|
AlertDialog dialog = new AlertDialog.Builder(this)
|
||||||
|
.setTitle(getString(R.string.app_name) + ' ' + getString(R.string.apps_mode))
|
||||||
|
.setMessage(getString(R.string.you_can_enable_all_apps_on_your_device_to_run_through_the_tor_network_using_the_vpn_feature_of_android_))
|
||||||
|
.setPositiveButton(R.string.activate, new Dialog.OnClickListener ()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
Prefs.putUseVpn(true);
|
||||||
|
|
||||||
|
startVpnService();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.btn_cancel, new Dialog.OnClickListener ()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
|
||||||
|
h.postDelayed(new Runnable () {
|
||||||
|
|
||||||
|
public void run ()
|
||||||
|
{
|
||||||
|
VPNEnableActivity.this.finish();
|
||||||
|
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
}).create();
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startVpnService ()
|
||||||
|
{
|
||||||
|
if (intent == null)
|
||||||
|
{
|
||||||
|
Log.d("VPNEnableActivity","VPN enabled, starting Tor...");
|
||||||
|
sendIntentToService(TorServiceConstants.CMD_VPN);
|
||||||
|
|
||||||
|
Handler h = new Handler();
|
||||||
|
h.postDelayed(new Runnable () {
|
||||||
|
|
||||||
|
public void run ()
|
||||||
|
{
|
||||||
|
sendIntentToService(TorServiceConstants.ACTION_START);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.w("VPNEnableActivity","prompt for VPN");
|
||||||
|
startActivityForResult(intent,REQUEST_VPN);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int request, int response, Intent data) {
|
||||||
|
super.onActivityResult(request, response, data);
|
||||||
|
|
||||||
|
if (request == REQUEST_VPN && response == RESULT_OK)
|
||||||
|
{
|
||||||
|
sendIntentToService(TorServiceConstants.CMD_VPN);
|
||||||
|
|
||||||
|
h.postDelayed(new Runnable () {
|
||||||
|
|
||||||
|
public void run ()
|
||||||
|
{
|
||||||
|
sendIntentToService(TorServiceConstants.ACTION_START);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void sendIntentToService(String action) {
|
||||||
|
Intent torService = new Intent(this, TorService.class);
|
||||||
|
torService.setAction(action);
|
||||||
|
startService(torService);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
# Copyright (C) 2009 The Android Open Source Project
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
ROOT_PATH := $(LOCAL_PATH)
|
||||||
|
EXTERN_PATH := $(LOCAL_PATH)/../external
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
## pdnsd library
|
||||||
|
########################################################
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
PDNSD_SOURCES := $(wildcard $(LOCAL_PATH)/pdnsd/src/*.c)
|
||||||
|
|
||||||
|
LOCAL_MODULE := pdnsd
|
||||||
|
LOCAL_SRC_FILES := $(PDNSD_SOURCES:$(LOCAL_PATH)/%=%)
|
||||||
|
LOCAL_CFLAGS := -Wall -O2 -I$(LOCAL_PATH)/pdnsd -DHAVE_STPCPY
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
## libancillary
|
||||||
|
########################################################
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
ANCILLARY_SOURCE := fd_recv.c fd_send.c
|
||||||
|
|
||||||
|
LOCAL_MODULE := libancillary
|
||||||
|
LOCAL_CFLAGS := -O2 -I$(LOCAL_PATH)/libancillary
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE))
|
||||||
|
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
## tun2socks
|
||||||
|
########################################################
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := -std=gnu99
|
||||||
|
LOCAL_CFLAGS += -DBADVPN_THREADWORK_USE_PTHREAD -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE
|
||||||
|
LOCAL_CFLAGS += -DBADVPN_USE_SELFPIPE -DBADVPN_USE_EPOLL
|
||||||
|
LOCAL_CFLAGS += -DBADVPN_LITTLE_ENDIAN -DBADVPN_THREAD_SAFE
|
||||||
|
LOCAL_CFLAGS += -DNDEBUG -DANDROID
|
||||||
|
LOCAL_CFLAGS += -DTUN2SOCKS_JNI
|
||||||
|
LOCAL_CFLAGS += -DPSIPHON
|
||||||
|
|
||||||
|
LOCAL_STATIC_LIBRARIES := libancillary
|
||||||
|
|
||||||
|
LOCAL_C_INCLUDES:= \
|
||||||
|
$(LOCAL_PATH)/libancillary \
|
||||||
|
$(EXTERN_PATH)/badvpn/ \
|
||||||
|
$(EXTERN_PATH)/badvpn/lwip/src/include/ipv4 \
|
||||||
|
$(EXTERN_PATH)/badvpn/lwip/src/include/ipv6 \
|
||||||
|
$(EXTERN_PATH)/badvpn/lwip/src/include \
|
||||||
|
$(EXTERN_PATH)/badvpn/lwip/custom \
|
||||||
|
|
||||||
|
TUN2SOCKS_SOURCES := \
|
||||||
|
base/BLog_syslog.c \
|
||||||
|
system/BReactor_badvpn.c \
|
||||||
|
system/BSignal.c \
|
||||||
|
system/BConnection_unix.c \
|
||||||
|
system/BTime.c \
|
||||||
|
system/BUnixSignal.c \
|
||||||
|
system/BNetwork.c \
|
||||||
|
flow/StreamRecvInterface.c \
|
||||||
|
flow/PacketRecvInterface.c \
|
||||||
|
flow/PacketPassInterface.c \
|
||||||
|
flow/StreamPassInterface.c \
|
||||||
|
flow/SinglePacketBuffer.c \
|
||||||
|
flow/BufferWriter.c \
|
||||||
|
flow/PacketBuffer.c \
|
||||||
|
flow/PacketStreamSender.c \
|
||||||
|
flow/PacketPassConnector.c \
|
||||||
|
flow/PacketProtoFlow.c \
|
||||||
|
flow/PacketPassFairQueue.c \
|
||||||
|
flow/PacketProtoEncoder.c \
|
||||||
|
flow/PacketProtoDecoder.c \
|
||||||
|
socksclient/BSocksClient.c \
|
||||||
|
tuntap/BTap.c \
|
||||||
|
lwip/src/core/timers.c \
|
||||||
|
lwip/src/core/udp.c \
|
||||||
|
lwip/src/core/memp.c \
|
||||||
|
lwip/src/core/init.c \
|
||||||
|
lwip/src/core/pbuf.c \
|
||||||
|
lwip/src/core/tcp.c \
|
||||||
|
lwip/src/core/tcp_out.c \
|
||||||
|
lwip/src/core/netif.c \
|
||||||
|
lwip/src/core/def.c \
|
||||||
|
lwip/src/core/mem.c \
|
||||||
|
lwip/src/core/tcp_in.c \
|
||||||
|
lwip/src/core/stats.c \
|
||||||
|
lwip/src/core/inet_chksum.c \
|
||||||
|
lwip/src/core/ipv4/icmp.c \
|
||||||
|
lwip/src/core/ipv4/igmp.c \
|
||||||
|
lwip/src/core/ipv4/ip4_addr.c \
|
||||||
|
lwip/src/core/ipv4/ip_frag.c \
|
||||||
|
lwip/src/core/ipv4/ip4.c \
|
||||||
|
lwip/src/core/ipv4/autoip.c \
|
||||||
|
lwip/src/core/ipv6/ethip6.c \
|
||||||
|
lwip/src/core/ipv6/inet6.c \
|
||||||
|
lwip/src/core/ipv6/ip6_addr.c \
|
||||||
|
lwip/src/core/ipv6/mld6.c \
|
||||||
|
lwip/src/core/ipv6/dhcp6.c \
|
||||||
|
lwip/src/core/ipv6/icmp6.c \
|
||||||
|
lwip/src/core/ipv6/ip6.c \
|
||||||
|
lwip/src/core/ipv6/ip6_frag.c \
|
||||||
|
lwip/src/core/ipv6/nd6.c \
|
||||||
|
lwip/custom/sys.c \
|
||||||
|
tun2socks/tun2socks.c \
|
||||||
|
base/DebugObject.c \
|
||||||
|
base/BLog.c \
|
||||||
|
base/BPending.c \
|
||||||
|
system/BDatagram_unix.c \
|
||||||
|
flowextra/PacketPassInactivityMonitor.c \
|
||||||
|
tun2socks/SocksUdpGwClient.c \
|
||||||
|
udpgw_client/UdpGwClient.c
|
||||||
|
|
||||||
|
LOCAL_MODULE := tun2socks
|
||||||
|
|
||||||
|
LOCAL_LDLIBS := -ldl -llog
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(addprefix ../external/badvpn/, $(TUN2SOCKS_SOURCES))
|
||||||
|
|
||||||
|
##include $(BUILD_EXECUTABLE)
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
||||||
|
# Import cpufeatures
|
||||||
|
$(call import-module,android/cpufeatures)
|
|
@ -0,0 +1,4 @@
|
||||||
|
APP_ABI := armeabi x86
|
||||||
|
APP_PLATFORM := android-9
|
||||||
|
APP_STL := stlport_static
|
||||||
|
NDK_TOOLCHAIN_VERSION := 4.8
|
|
@ -0,0 +1,139 @@
|
||||||
|
This library provide an easy interface to the black magic that can be done
|
||||||
|
on Unix domain sockets, like passing file descriptors from one process to
|
||||||
|
another.
|
||||||
|
|
||||||
|
Programs that uses this library should include the ancillary.h header file.
|
||||||
|
Nothing else is required.
|
||||||
|
|
||||||
|
All functions of this library require the following header:
|
||||||
|
|
||||||
|
#include <ancillary.h>
|
||||||
|
|
||||||
|
At this time, the only ancillary data defined by the Single Unix
|
||||||
|
Specification (v3) is file descriptors.
|
||||||
|
|
||||||
|
Passing file descriptors
|
||||||
|
|
||||||
|
int ancil_send_fd(socket, file_descriptor)
|
||||||
|
int socket: the Unix socket
|
||||||
|
int file_descriptor: the file descriptor
|
||||||
|
Return value: 0 for success, -1 for failure.
|
||||||
|
|
||||||
|
Sends one file descriptor on a socket.
|
||||||
|
In case of failure, errno is set; the possible values are the ones of the
|
||||||
|
sendmsg(2) system call.
|
||||||
|
|
||||||
|
|
||||||
|
int ancil_recv_fd(socket, file_descriptor)
|
||||||
|
int socket: the Unix socket
|
||||||
|
int *file_descriptor: pointer to the returned file descriptor
|
||||||
|
Return value: 0 for success, -1 for failure
|
||||||
|
|
||||||
|
Receives one file descriptor from a socket.
|
||||||
|
In case of success, the file descriptor is stored in the integer pointed
|
||||||
|
to by file_descriptor.
|
||||||
|
In case of failure, errno is set; the possible values are the ones of the
|
||||||
|
recvmsg(2) system call.
|
||||||
|
The behavior is undefined if the recv_fd does not match a send_fd* on the
|
||||||
|
other side.
|
||||||
|
|
||||||
|
|
||||||
|
int ancil_send_fds(socket, file_descriptors, num_file_descriptors)
|
||||||
|
int socket: the Unix socket
|
||||||
|
const int *file_descriptors: array of file descriptors
|
||||||
|
unsigned num_file_descriptors: number of file descriptors
|
||||||
|
Return value: 0 for success, -1 for failure
|
||||||
|
|
||||||
|
Sends several file descriptors on a socket.
|
||||||
|
In case of failure, errno is set; the possible values are the ones of the
|
||||||
|
sendmsg(2) system call.
|
||||||
|
The maximum number of file descriptors that can be sent using this
|
||||||
|
function is ANCIL_MAX_N_FDS; the behavior is undefined in case of
|
||||||
|
overflow, probably a stack corruption.
|
||||||
|
|
||||||
|
|
||||||
|
int ancil_recv_fds(socket, file_descriptors, num_file_descriptors)
|
||||||
|
int socket: the Unix socket
|
||||||
|
int *file_descriptors: return array of file descriptors
|
||||||
|
unsigned num_file_descriptors: number of file descriptors
|
||||||
|
Return value: number of received fd for success, -1 for failure
|
||||||
|
|
||||||
|
Receives several file descriptors from a socket, no more than
|
||||||
|
num_file_descriptors.
|
||||||
|
In case of success, the received file descriptors are stored in the array
|
||||||
|
pointed to by file_descriptors.
|
||||||
|
In case of failure, errno is set; the possible values are the ones of the
|
||||||
|
recvmsg(2) system call.
|
||||||
|
The maximum number of file descriptors that can be received using this
|
||||||
|
function is ANCIL_MAX_N_FDS; the behavior is undefined in case of
|
||||||
|
overflow, probably a stack corruption.
|
||||||
|
The behavior is undefined if the recv_fds does not match a send_fd* on
|
||||||
|
the other side, or if the number of received file descriptors is more than
|
||||||
|
num_file_descriptors.
|
||||||
|
|
||||||
|
|
||||||
|
int ancil_send_fds_with_buffer(socket, fds, num, buffer)
|
||||||
|
int socket: the Unix socket
|
||||||
|
const int *fds: array of file descriptors
|
||||||
|
unsigned num: number of file descriptors
|
||||||
|
void *buffer: buffer to hold the system data structures
|
||||||
|
Return value: 0 for success, -1 for failure
|
||||||
|
|
||||||
|
Sends several file descriptors on a socket.
|
||||||
|
In case of failure, errno is set; the possible values are the ones of the
|
||||||
|
sendmsg(2) system call.
|
||||||
|
The buffer argument must point to a memory area large enough to hold the
|
||||||
|
system data structures, see ANCIL_FD_BUFFER.
|
||||||
|
|
||||||
|
|
||||||
|
int ancil_send_fds_with_buffer(socket, fds, num, buffer)
|
||||||
|
int socket: the Unix socket
|
||||||
|
int *fds: return array of file descriptors
|
||||||
|
unsigned num: number of file descriptors
|
||||||
|
void *buffer: buffer to hold the system data structures
|
||||||
|
Return value: number of received fd for success, -1 for failure
|
||||||
|
|
||||||
|
Receives several file descriptors from a socket, no more than
|
||||||
|
num_file_descriptors.
|
||||||
|
In case of success, the received file descriptors are stored in the array
|
||||||
|
pointed to by file_descriptors.
|
||||||
|
In case of failure, errno is set; the possible values are the ones of the
|
||||||
|
recvmsg(2) system call.
|
||||||
|
The behavior is undefined if the recv_fds does not match a send_fd* on
|
||||||
|
the other side, or if the number of received file descriptors is more than
|
||||||
|
num_file_descriptors.
|
||||||
|
The buffer argument must point to a memory area large enough to hold the
|
||||||
|
system data structures, see ANCIL_FD_BUFFER.
|
||||||
|
|
||||||
|
|
||||||
|
ANCIL_MAX_N_FDS
|
||||||
|
|
||||||
|
Maximum number of file descriptors that can be sent with the sent_fds and
|
||||||
|
recv_fds functions. If you have to send more at once, use the
|
||||||
|
*_with_buffer versions. The value is enough to send "quite a few" file
|
||||||
|
descriptors.
|
||||||
|
|
||||||
|
|
||||||
|
ANCIL_FD_BUFFER(n)
|
||||||
|
int n: number of file descriptors
|
||||||
|
|
||||||
|
Expands to a structure data type large enough to hold the system data
|
||||||
|
structures for n file descriptors. So the address of a variable declared
|
||||||
|
of type ANCIL_FD_BUFFER(n) is suitable as the buffer argument for
|
||||||
|
*_with_buffer on n file descriptors.
|
||||||
|
To use this macro, you need <sys/types.h> and <sys/socket.h>. Bevare: with
|
||||||
|
Solaris, the _XPG4_2 macro must be defined before sys/socket is included.
|
||||||
|
|
||||||
|
|
||||||
|
Tuning the compilation
|
||||||
|
|
||||||
|
This library is designed to be included in projects, not installed in
|
||||||
|
/usr/lib. If your project does not use some of the functions, the
|
||||||
|
TUNE_OPTS variable in the Makefile allows not to build them. It is a list
|
||||||
|
of proprocessor options:
|
||||||
|
|
||||||
|
-DNDEBUG: turn assertions off (see assert(3))
|
||||||
|
-DSPARE_SEND_FDS: do not build ancil_send_fds
|
||||||
|
-DSPARE_SEND_FD: do not build ancil_send_fd
|
||||||
|
-DSPARE_RECV_FDS: do not build ancil_recv_fds
|
||||||
|
-DSPARE_RECV_FD: do not build ancil_recv_fd
|
|
@ -0,0 +1,21 @@
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,73 @@
|
||||||
|
###########################################################################
|
||||||
|
# libancillary - black magic on Unix domain sockets
|
||||||
|
# (C) Nicolas George
|
||||||
|
# Makefile - guess what
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# 3. The name of the author may not be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS=-Wall -g -O2
|
||||||
|
LDFLAGS=
|
||||||
|
LIBS=
|
||||||
|
AR=ar
|
||||||
|
RANLIB=ranlib
|
||||||
|
RM=rm
|
||||||
|
CP=cp
|
||||||
|
MKDIR=mkdir
|
||||||
|
TAR=tar
|
||||||
|
GZIP=gzip -9
|
||||||
|
|
||||||
|
NAME=libancillary
|
||||||
|
DISTRIBUTION=API COPYING Makefile ancillary.h fd_send.c fd_recv.c test.c
|
||||||
|
VERSION=0.9.1
|
||||||
|
|
||||||
|
OBJECTS=fd_send.o fd_recv.o
|
||||||
|
|
||||||
|
TUNE_OPTS=-DNDEBUG
|
||||||
|
#TUNE_OPTS=-DNDEBUG \
|
||||||
|
-DSPARE_SEND_FDS -DSPARE_SEND_FD -DSPARE_RECV_FDS -DSPARE_RECV_FD
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
$(CC) -c $(CFLAGS) $(TUNE_OPTS) $<
|
||||||
|
|
||||||
|
all: libancillary.a
|
||||||
|
|
||||||
|
libancillary.a: $(OBJECTS)
|
||||||
|
$(AR) cr $@ $(OBJECTS)
|
||||||
|
$(RANLIB) $@
|
||||||
|
|
||||||
|
fd_send.o: ancillary.h
|
||||||
|
fd_recv.o: ancillary.h
|
||||||
|
|
||||||
|
test: test.c libancillary.a
|
||||||
|
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) -L. test.c -lancillary $(LIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-$(RM) -f *.o *.a test
|
||||||
|
|
||||||
|
dist:
|
||||||
|
$(MKDIR) $(NAME)-$(VERSION)
|
||||||
|
$(CP) $(DISTRIBUTION) $(NAME)-$(VERSION)
|
||||||
|
$(TAR) -cf - $(NAME)-$(VERSION) | $(GZIP) > $(NAME)-$(VERSION).tar.gz
|
||||||
|
$(RM) -rf $(NAME)-$(VERSION)
|
|
@ -0,0 +1,131 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* libancillary - black magic on Unix domain sockets
|
||||||
|
* (C) Nicolas George
|
||||||
|
* ancillary.c - public header
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ANCILLARY_H__
|
||||||
|
#define ANCILLARY_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Start of the readable part.
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#define ANCIL_MAX_N_FDS 960
|
||||||
|
/*
|
||||||
|
* Maximum number of fds that can be sent or received using the "esay"
|
||||||
|
* functions; this is so that all can fit in one page.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int
|
||||||
|
ancil_send_fds_with_buffer(int, const int *, unsigned, void *);
|
||||||
|
/*
|
||||||
|
* ancil_send_fds_with_buffer(sock, n_fds, fds, buffer)
|
||||||
|
*
|
||||||
|
* Sends the file descriptors in the array pointed by fds, of length n_fds
|
||||||
|
* on the socket sock.
|
||||||
|
* buffer is a writeable memory area large enough to hold the required data
|
||||||
|
* structures.
|
||||||
|
* Returns: -1 and errno in case of error, 0 in case of success.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int
|
||||||
|
ancil_recv_fds_with_buffer(int, int *, unsigned, void *);
|
||||||
|
/*
|
||||||
|
* ancil_recv_fds_with_buffer(sock, n_fds, fds, buffer)
|
||||||
|
*
|
||||||
|
* Receives *n_fds file descriptors into the array pointed by fds
|
||||||
|
* from the socket sock.
|
||||||
|
* buffer is a writeable memory area large enough to hold the required data
|
||||||
|
* structures.
|
||||||
|
* Returns: -1 and errno in case of error, the actual number of received fd
|
||||||
|
* in case of success
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ANCIL_FD_BUFFER(n) \
|
||||||
|
struct { \
|
||||||
|
struct cmsghdr h; \
|
||||||
|
int fd[n]; \
|
||||||
|
}
|
||||||
|
/* ANCIL_FD_BUFFER(n)
|
||||||
|
*
|
||||||
|
* A structure type suitable to be used as buffer for n file descriptors.
|
||||||
|
* Requires <sys/socket.h>.
|
||||||
|
* Example:
|
||||||
|
* ANCIL_FD_BUFFER(42) buffer;
|
||||||
|
* ancil_recv_fds_with_buffer(sock, 42, my_fds, &buffer);
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int
|
||||||
|
ancil_send_fds(int, const int *, unsigned);
|
||||||
|
/*
|
||||||
|
* ancil_send_fds(sock, n_fds, fds)
|
||||||
|
*
|
||||||
|
* Sends the file descriptors in the array pointed by fds, of length n_fds
|
||||||
|
* on the socket sock.
|
||||||
|
* n_fds must not be greater than ANCIL_MAX_N_FDS.
|
||||||
|
* Returns: -1 and errno in case of error, 0 in case of success.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int
|
||||||
|
ancil_recv_fds(int, int *, unsigned);
|
||||||
|
/*
|
||||||
|
* ancil_recv_fds(sock, n_fds, fds)
|
||||||
|
*
|
||||||
|
* Receives *n_fds file descriptors into the array pointed by fds
|
||||||
|
* from the socket sock.
|
||||||
|
* *n_fds must not be greater than ANCIL_MAX_N_FDS.
|
||||||
|
* Returns: -1 and errno in case of error, the actual number of received fd
|
||||||
|
* in case of success.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
extern int
|
||||||
|
ancil_send_fd(int, int);
|
||||||
|
/* ancil_recv_fd(sock, fd);
|
||||||
|
*
|
||||||
|
* Sends the file descriptor fd on the socket sock.
|
||||||
|
* Returns : -1 and errno in case of error, 0 in case of success.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int
|
||||||
|
ancil_recv_fd(int, int *);
|
||||||
|
/* ancil_send_fd(sock, &fd);
|
||||||
|
*
|
||||||
|
* Receives the file descriptor fd from the socket sock.
|
||||||
|
* Returns : -1 and errno in case of error, 0 in case of success.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ANCILLARY_H__ */
|
|
@ -0,0 +1,98 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* libancillary - black magic on Unix domain sockets
|
||||||
|
* (C) Nicolas George
|
||||||
|
* fd_send.c - receiving file descriptors
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _XPG4_2 /* Solaris sucks */
|
||||||
|
# define _XPG4_2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#if defined(__FreeBSD__)
|
||||||
|
# include <sys/param.h> /* FreeBSD sucks */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ancillary.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
ancil_recv_fds_with_buffer(int sock, int *fds, unsigned n_fds, void *buffer)
|
||||||
|
{
|
||||||
|
struct msghdr msghdr;
|
||||||
|
char nothing;
|
||||||
|
struct iovec nothing_ptr;
|
||||||
|
struct cmsghdr *cmsg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
nothing_ptr.iov_base = ¬hing;
|
||||||
|
nothing_ptr.iov_len = 1;
|
||||||
|
msghdr.msg_name = NULL;
|
||||||
|
msghdr.msg_namelen = 0;
|
||||||
|
msghdr.msg_iov = ¬hing_ptr;
|
||||||
|
msghdr.msg_iovlen = 1;
|
||||||
|
msghdr.msg_flags = 0;
|
||||||
|
msghdr.msg_control = buffer;
|
||||||
|
msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds;
|
||||||
|
cmsg = CMSG_FIRSTHDR(&msghdr);
|
||||||
|
cmsg->cmsg_len = msghdr.msg_controllen;
|
||||||
|
cmsg->cmsg_level = SOL_SOCKET;
|
||||||
|
cmsg->cmsg_type = SCM_RIGHTS;
|
||||||
|
for(i = 0; i < n_fds; i++)
|
||||||
|
((int *)CMSG_DATA(cmsg))[i] = -1;
|
||||||
|
|
||||||
|
if(recvmsg(sock, &msghdr, 0) < 0)
|
||||||
|
return(-1);
|
||||||
|
for(i = 0; i < n_fds; i++)
|
||||||
|
fds[i] = ((int *)CMSG_DATA(cmsg))[i];
|
||||||
|
n_fds = (msghdr.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int);
|
||||||
|
return(n_fds);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SPARE_RECV_FDS
|
||||||
|
int
|
||||||
|
ancil_recv_fds(int sock, int *fd, unsigned n_fds)
|
||||||
|
{
|
||||||
|
ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer;
|
||||||
|
|
||||||
|
assert(n_fds <= ANCIL_MAX_N_FDS);
|
||||||
|
return(ancil_recv_fds_with_buffer(sock, fd, n_fds, &buffer));
|
||||||
|
}
|
||||||
|
#endif /* SPARE_RECV_FDS */
|
||||||
|
|
||||||
|
#ifndef SPARE_RECV_FD
|
||||||
|
int
|
||||||
|
ancil_recv_fd(int sock, int *fd)
|
||||||
|
{
|
||||||
|
ANCIL_FD_BUFFER(1) buffer;
|
||||||
|
|
||||||
|
return(ancil_recv_fds_with_buffer(sock, fd, 1, &buffer) == 1 ? 0 : -1);
|
||||||
|
}
|
||||||
|
#endif /* SPARE_RECV_FD */
|
|
@ -0,0 +1,92 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* libancillary - black magic on Unix domain sockets
|
||||||
|
* (C) Nicolas George
|
||||||
|
* fd_send.c - sending file descriptors
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _XPG4_2 /* Solaris sucks */
|
||||||
|
# define _XPG4_2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#if defined(__FreeBSD__)
|
||||||
|
# include <sys/param.h> /* FreeBSD sucks */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ancillary.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
ancil_send_fds_with_buffer(int sock, const int *fds, unsigned n_fds, void *buffer)
|
||||||
|
{
|
||||||
|
struct msghdr msghdr;
|
||||||
|
char nothing = '!';
|
||||||
|
struct iovec nothing_ptr;
|
||||||
|
struct cmsghdr *cmsg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
nothing_ptr.iov_base = ¬hing;
|
||||||
|
nothing_ptr.iov_len = 1;
|
||||||
|
msghdr.msg_name = NULL;
|
||||||
|
msghdr.msg_namelen = 0;
|
||||||
|
msghdr.msg_iov = ¬hing_ptr;
|
||||||
|
msghdr.msg_iovlen = 1;
|
||||||
|
msghdr.msg_flags = 0;
|
||||||
|
msghdr.msg_control = buffer;
|
||||||
|
msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds;
|
||||||
|
cmsg = CMSG_FIRSTHDR(&msghdr);
|
||||||
|
cmsg->cmsg_len = msghdr.msg_controllen;
|
||||||
|
cmsg->cmsg_level = SOL_SOCKET;
|
||||||
|
cmsg->cmsg_type = SCM_RIGHTS;
|
||||||
|
for(i = 0; i < n_fds; i++)
|
||||||
|
((int *)CMSG_DATA(cmsg))[i] = fds[i];
|
||||||
|
return(sendmsg(sock, &msghdr, 0) >= 0 ? 0 : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SPARE_SEND_FDS
|
||||||
|
int
|
||||||
|
ancil_send_fds(int sock, const int *fds, unsigned n_fds)
|
||||||
|
{
|
||||||
|
ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer;
|
||||||
|
|
||||||
|
assert(n_fds <= ANCIL_MAX_N_FDS);
|
||||||
|
return(ancil_send_fds_with_buffer(sock, fds, n_fds, &buffer));
|
||||||
|
}
|
||||||
|
#endif /* SPARE_SEND_FDS */
|
||||||
|
|
||||||
|
#ifndef SPARE_SEND_FD
|
||||||
|
int
|
||||||
|
ancil_send_fd(int sock, int fd)
|
||||||
|
{
|
||||||
|
ANCIL_FD_BUFFER(1) buffer;
|
||||||
|
|
||||||
|
return(ancil_send_fds_with_buffer(sock, &fd, 1, &buffer));
|
||||||
|
}
|
||||||
|
#endif /* SPARE_SEND_FD */
|
|
@ -0,0 +1,112 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* libancillary - black magic on Unix domain sockets
|
||||||
|
* (C) Nicolas George
|
||||||
|
* test.c - testing and example program
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include "ancillary.h"
|
||||||
|
|
||||||
|
void child_process(int sock)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
int fds[3], nfds;
|
||||||
|
char b[] = "This is on the received fd!\n";
|
||||||
|
|
||||||
|
if(ancil_recv_fd(sock, &fd)) {
|
||||||
|
perror("ancil_recv_fd");
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
printf("Received fd: %d\n", fd);
|
||||||
|
}
|
||||||
|
write(fd, b, sizeof(b));
|
||||||
|
close(fd);
|
||||||
|
sleep(2);
|
||||||
|
|
||||||
|
nfds = ancil_recv_fds(sock, fds, 3);
|
||||||
|
if(nfds < 0) {
|
||||||
|
perror("ancil_recv_fds");
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
printf("Received %d/3 fds : %d %d %d.\n", nfds,
|
||||||
|
fds[0], fds[1], fds[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parent_process(int sock)
|
||||||
|
{
|
||||||
|
int fds[2] = { 1, 2 };
|
||||||
|
|
||||||
|
if(ancil_send_fd(sock, 1)) {
|
||||||
|
perror("ancil_send_fd");
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
printf("Sent fd.\n");
|
||||||
|
}
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
if(ancil_send_fds(sock, fds, 2)) {
|
||||||
|
perror("ancil_send_fds");
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
printf("Sent two fds.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int sock[2];
|
||||||
|
|
||||||
|
if(socketpair(PF_UNIX, SOCK_STREAM, 0, sock)) {
|
||||||
|
perror("socketpair");
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
printf("Established socket pair: (%d, %d)\n", sock[0], sock[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(fork()) {
|
||||||
|
case 0:
|
||||||
|
close(sock[0]);
|
||||||
|
child_process(sock[1]);
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
perror("fork");
|
||||||
|
exit(1);
|
||||||
|
default:
|
||||||
|
close(sock[1]);
|
||||||
|
parent_process(sock[0]);
|
||||||
|
wait(NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return(0);
|
||||||
|
}
|
After Width: | Height: | Size: 480 B |
After Width: | Height: | Size: 802 B |
After Width: | Height: | Size: 744 B |
After Width: | Height: | Size: 967 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 616 B |
After Width: | Height: | Size: 713 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 553 B |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 852 B |
After Width: | Height: | Size: 990 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 294 B |
After Width: | Height: | Size: 390 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 342 B |
After Width: | Height: | Size: 358 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 319 B |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 647 B |
After Width: | Height: | Size: 606 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 357 B |
After Width: | Height: | Size: 517 B |
After Width: | Height: | Size: 607 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 458 B |
After Width: | Height: | Size: 475 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 364 B |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 883 B |
After Width: | Height: | Size: 535 B |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 618 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 747 B |
After Width: | Height: | Size: 970 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 716 B |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 1.0 KiB |
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_pressed="true" >
|
||||||
|
<shape>
|
||||||
|
<solid
|
||||||
|
android:color="@color/panel_background" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#ffffff" />
|
||||||
|
<corners
|
||||||
|
android:radius="6dp" />
|
||||||
|
<padding
|
||||||
|
android:left="10dp"
|
||||||
|
android:top="10dp"
|
||||||
|
android:right="10dp"
|
||||||
|
android:bottom="10dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/panel_background_dark"
|
||||||
|
android:endColor="@color/panel_background_main"
|
||||||
|
android:angle="270" />
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="#aaaaaa" />
|
||||||
|
<corners
|
||||||
|
android:radius="6dp" />
|
||||||
|
<padding
|
||||||
|
android:left="10dp"
|
||||||
|
android:top="10dp"
|
||||||
|
android:right="10dp"
|
||||||
|
android:bottom="10dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_pressed="true" >
|
||||||
|
<shape>
|
||||||
|
<solid
|
||||||
|
android:color="@color/panel_background" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#ffffff" />
|
||||||
|
<corners
|
||||||
|
android:radius="6dp" />
|
||||||
|
<padding
|
||||||
|
android:left="10dp"
|
||||||
|
android:top="10dp"
|
||||||
|
android:right="10dp"
|
||||||
|
android:bottom="10dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/panel_background_dark"
|
||||||
|
android:endColor="@color/panel_background_main"
|
||||||
|
android:angle="270" />
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="#555555" />
|
||||||
|
<corners
|
||||||
|
android:radius="6dp" />
|
||||||
|
<padding
|
||||||
|
android:left="10dp"
|
||||||
|
android:top="10dp"
|
||||||
|
android:right="10dp"
|
||||||
|
android:bottom="10dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_pressed="true" >
|
||||||
|
<shape>
|
||||||
|
<solid
|
||||||
|
android:color="@color/bright_green" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#ffffff" />
|
||||||
|
<corners
|
||||||
|
android:radius="6dp" />
|
||||||
|
<padding
|
||||||
|
android:left="10dp"
|
||||||
|
android:top="10dp"
|
||||||
|
android:right="10dp"
|
||||||
|
android:bottom="10dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<solid
|
||||||
|
android:color="@color/dark_green" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#ffffff" />
|
||||||
|
<corners
|
||||||
|
android:radius="6dp" />
|
||||||
|
<padding
|
||||||
|
android:left="10dp"
|
||||||
|
android:top="10dp"
|
||||||
|
android:right="10dp"
|
||||||
|
android:bottom="10dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.9 KiB |