Skip to content

Commit b517fbc

Browse files
committed
Add stealth mode to disguise app identity
Switch launcher icon, app name, and notification appearance to look like a mundane utility (Calculator, Weather, Notes, Clock). Also removes dead sponsor banner code and assets.
1 parent 4931bb5 commit b517fbc

27 files changed

Lines changed: 737 additions & 142 deletions

app/src/main/AndroidManifest.xml

Lines changed: 89 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,95 @@
5252
android:theme="@style/Theme.Psiphon.NoActionBar"
5353
android:exported="true"
5454
android:launchMode="singleTask">
55-
<intent-filter>
56-
<action android:name="android.intent.action.MAIN" />
57-
<category android:name="android.intent.category.LAUNCHER" />
58-
</intent-filter>
59-
<intent-filter>
60-
<action android:name="android.intent.action.VIEW" />
61-
<category android:name="android.intent.category.DEFAULT" />
62-
<category android:name="android.intent.category.BROWSABLE" />
63-
<!-- Accepts URIs in the form of "psiphon://settings” -->
64-
<data
65-
android:scheme="shirokhorshid"
66-
android:host="settings" />
67-
</intent-filter>
68-
</activity>
69-
<activity-alias
70-
android:name="com.psiphon3.psiphonlibrary.TunnelIntentsHandler"
71-
android:exported="false"
72-
android:targetActivity="com.psiphon3.MainActivity" />
55+
<intent-filter>
56+
<action android:name="android.intent.action.VIEW" />
57+
<category android:name="android.intent.category.DEFAULT" />
58+
<category android:name="android.intent.category.BROWSABLE" />
59+
<!-- Accepts URIs in the form of "shirokhorshid://settings" -->
60+
<data
61+
android:scheme="shirokhorshid"
62+
android:host="settings" />
63+
</intent-filter>
64+
</activity>
65+
<activity-alias
66+
android:name="com.psiphon3.psiphonlibrary.TunnelIntentsHandler"
67+
android:exported="false"
68+
android:targetActivity="com.psiphon3.MainActivity" />
69+
70+
<!-- Disguise: Default identity (Shir o Khorshid) -->
71+
<activity-alias
72+
android:name=".LauncherDefault"
73+
android:label="@string/app_name"
74+
android:icon="@mipmap/ic_launcher"
75+
android:roundIcon="@mipmap/ic_launcher_round"
76+
android:targetActivity=".MainActivity"
77+
android:exported="true"
78+
android:enabled="true">
79+
<intent-filter>
80+
<action android:name="android.intent.action.MAIN" />
81+
<category android:name="android.intent.category.LAUNCHER" />
82+
</intent-filter>
83+
</activity-alias>
84+
85+
<!-- Disguise: Calculator -->
86+
<activity-alias
87+
android:name=".LauncherCalculator"
88+
android:label="@string/disguise_name_calculator"
89+
android:icon="@mipmap/ic_disguise_calculator"
90+
android:roundIcon="@mipmap/ic_disguise_calculator"
91+
android:targetActivity=".MainActivity"
92+
android:exported="true"
93+
android:enabled="false">
94+
<intent-filter>
95+
<action android:name="android.intent.action.MAIN" />
96+
<category android:name="android.intent.category.LAUNCHER" />
97+
</intent-filter>
98+
</activity-alias>
99+
100+
<!-- Disguise: Weather -->
101+
<activity-alias
102+
android:name=".LauncherWeather"
103+
android:label="@string/disguise_name_weather"
104+
android:icon="@mipmap/ic_disguise_weather"
105+
android:roundIcon="@mipmap/ic_disguise_weather"
106+
android:targetActivity=".MainActivity"
107+
android:exported="true"
108+
android:enabled="false">
109+
<intent-filter>
110+
<action android:name="android.intent.action.MAIN" />
111+
<category android:name="android.intent.category.LAUNCHER" />
112+
</intent-filter>
113+
</activity-alias>
114+
115+
<!-- Disguise: Notes -->
116+
<activity-alias
117+
android:name=".LauncherNotes"
118+
android:label="@string/disguise_name_notes"
119+
android:icon="@mipmap/ic_disguise_notes"
120+
android:roundIcon="@mipmap/ic_disguise_notes"
121+
android:targetActivity=".MainActivity"
122+
android:exported="true"
123+
android:enabled="false">
124+
<intent-filter>
125+
<action android:name="android.intent.action.MAIN" />
126+
<category android:name="android.intent.category.LAUNCHER" />
127+
</intent-filter>
128+
</activity-alias>
129+
130+
<!-- Disguise: Clock -->
131+
<activity-alias
132+
android:name=".LauncherClock"
133+
android:label="@string/disguise_name_clock"
134+
android:icon="@mipmap/ic_disguise_clock"
135+
android:roundIcon="@mipmap/ic_disguise_clock"
136+
android:targetActivity=".MainActivity"
137+
android:exported="true"
138+
android:enabled="false">
139+
<intent-filter>
140+
<action android:name="android.intent.action.MAIN" />
141+
<category android:name="android.intent.category.LAUNCHER" />
142+
</intent-filter>
143+
</activity-alias>
73144
<activity
74145
android:name=".FeedbackActivity"
75146
android:parentActivityName=".MainActivity"

app/src/main/java/com/psiphon3/MainActivity.java

Lines changed: 0 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@
2525
import android.content.Context;
2626
import android.content.Intent;
2727
import android.content.pm.PackageManager;
28-
import android.graphics.Bitmap;
29-
import android.graphics.BitmapFactory;
30-
import android.graphics.Color;
3128
import android.graphics.drawable.AnimationDrawable;
3229
import android.net.Uri;
3330
import android.nfc.NfcAdapter;
@@ -48,7 +45,6 @@
4845
import android.view.View;
4946
import android.view.ViewGroup;
5047
import android.widget.Button;
51-
import android.widget.ImageView;
5248
import android.widget.ProgressBar;
5349
import android.widget.TextView;
5450
import android.widget.Toast;
@@ -79,13 +75,8 @@
7975
import net.grandcentrix.tray.AppPreferences;
8076
import net.grandcentrix.tray.core.ItemNotFoundException;
8177

82-
import java.io.File;
83-
import java.io.FileOutputStream;
84-
import java.io.IOException;
8578
import java.util.ArrayList;
8679
import java.util.Collections;
87-
import java.util.Comparator;
88-
import java.util.HashMap;
8980
import java.util.LinkedHashSet;
9081
import java.util.List;
9182
import java.util.Locale;
@@ -109,8 +100,6 @@ public MainActivity() {
109100

110101
public static final String INTENT_EXTRA_PREVENT_AUTO_START = "com.psiphon3.MainActivity.PREVENT_AUTO_START";
111102
private static final String CURRENT_TAB = "currentTab";
112-
private static final String BANNER_FILE_NAME = "bannerImage";
113-
114103
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
115104
private Button toggleButton;
116105
private ProgressBar connectionProgressBar;
@@ -121,7 +110,6 @@ public MainActivity() {
121110
private AppPreferences multiProcessPreferences;
122111
private ViewPager viewPager;
123112
private PsiphonTabLayout tabLayout;
124-
private ImageView banner;
125113
private boolean isFirstRun = true;
126114
private AlertDialog upstreamProxyErrorAlertDialog;
127115
private MenuItem psiphonBumpHelpItem;
@@ -226,10 +214,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
226214
// Schedule db maintenance
227215
LogsMaintenanceWorker.schedule(getApplicationContext());
228216

229-
// Banner removed in Shir o Khorshid rebrand
230-
// banner = findViewById(R.id.banner);
231-
// setUpBanner();
232-
233217
// Set up custom Toolbar as ActionBar
234218
Toolbar toolbar = findViewById(R.id.main_toolbar);
235219
setSupportActionBar(toolbar);
@@ -954,80 +938,6 @@ public static boolean shouldLoadInEmbeddedWebView(String url) {
954938
return true;
955939
}
956940

957-
private void setUpBanner() {
958-
// Play Store Build instances should use existing banner from previously installed APK
959-
// (if present). To enable this, non-Play Store Build instances write their banner to
960-
// a private file.
961-
try {
962-
Bitmap bitmap = getBannerBitmap();
963-
if (!EmbeddedValues.IS_PLAY_STORE_BUILD) {
964-
saveBanner(bitmap);
965-
}
966-
967-
// If we successfully got the banner image set it and it's background
968-
if (bitmap != null) {
969-
banner.setImageBitmap(bitmap);
970-
banner.setBackgroundColor(getMostCommonColor(bitmap));
971-
}
972-
} catch (IOException e) {
973-
// Ignore failure
974-
}
975-
}
976-
977-
private void saveBanner(Bitmap bitmap) throws IOException {
978-
if (bitmap == null) {
979-
return;
980-
}
981-
982-
FileOutputStream out = openFileOutput(BANNER_FILE_NAME, Context.MODE_PRIVATE);
983-
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
984-
out.close();
985-
}
986-
987-
private Bitmap getBannerBitmap() {
988-
if (EmbeddedValues.IS_PLAY_STORE_BUILD) {
989-
File bannerImageFile = new File(getFilesDir(), BANNER_FILE_NAME);
990-
if (bannerImageFile.exists()) {
991-
return BitmapFactory.decodeFile(bannerImageFile.getAbsolutePath());
992-
}
993-
}
994-
995-
return BitmapFactory.decodeResource(getResources(), R.drawable.banner);
996-
}
997-
998-
private int getMostCommonColor(Bitmap bitmap) {
999-
if (bitmap == null) {
1000-
return Color.WHITE;
1001-
}
1002-
1003-
int width = bitmap.getWidth();
1004-
int height = bitmap.getHeight();
1005-
int size = width * height;
1006-
int pixels[] = new int[size];
1007-
1008-
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
1009-
1010-
HashMap<Integer, Integer> colorMap = new HashMap<>();
1011-
1012-
for (int i = 0; i < pixels.length; i++) {
1013-
int color = pixels[i];
1014-
if (colorMap.containsKey(color)) {
1015-
colorMap.put(color, colorMap.get(color) + 1);
1016-
} else {
1017-
colorMap.put(color, 1);
1018-
}
1019-
}
1020-
1021-
ArrayList<Map.Entry<Integer, Integer>> entries = new ArrayList<>(colorMap.entrySet());
1022-
Collections.sort(entries, new Comparator<Map.Entry<Integer, Integer>>() {
1023-
@Override
1024-
public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
1025-
return o2.getValue().compareTo(o1.getValue());
1026-
}
1027-
});
1028-
return entries.get(0).getKey();
1029-
}
1030-
1031941
public void selectTabByTag(@NonNull Object tag) {
1032942
viewPager.post(() -> {
1033943
for (int i = 0; i < tabLayout.getTabCount(); i++) {

app/src/main/java/com/psiphon3/OptionsTabFragment.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import androidx.lifecycle.ViewModelProvider;
1616
import androidx.preference.Preference;
1717

18+
import com.psiphon3.psiphonlibrary.DisguiseManager;
1819
import com.psiphon3.psiphonlibrary.LocalizedActivities;
1920
import com.psiphon3.psiphonlibrary.MoreOptionsPreferenceActivity;
2021
import com.psiphon3.psiphonlibrary.ProxyOptionsPreferenceActivity;
@@ -448,7 +449,8 @@ private void updateMoreSettingsFromPreferences() {
448449
new SharedPreferencesImport(requireContext(), prefName, getString(R.string.unsafeTrafficAlertsPreference), getString(R.string.unsafeTrafficAlertsPreference)),
449450
new SharedPreferencesImport(requireContext(), prefName, getString(R.string.disableTimeoutsPreference), getString(R.string.disableTimeoutsPreference)),
450451
new SharedPreferencesImport(requireContext(), prefName, getString(R.string.nfcBumpPreference), getString(R.string.nfcBumpPreference)),
451-
new SharedPreferencesImport(requireContext(), prefName, getString(R.string.protocolSelectionPreference), getString(R.string.protocolSelectionPreference))
452+
new SharedPreferencesImport(requireContext(), prefName, getString(R.string.protocolSelectionPreference), getString(R.string.protocolSelectionPreference)),
453+
new SharedPreferencesImport(requireContext(), prefName, DisguiseManager.PREF_STEALTH_NOTIFICATIONS, DisguiseManager.PREF_STEALTH_NOTIFICATIONS)
452454
);
453455
}
454456
}

0 commit comments

Comments
 (0)