Termux bash scripts
This post aims to maintain a growing list of bash scripts that can be run on the andorid terminal to perform tasks not available through the User Interface or an application.
Some may work over adb with adb shell
Tested them with root directly on termux.
Search for GitHub repos that match a search query and contain apk releases.
- Parse search query as argument.
- Look for matches in repo names, descriptions, and read.me files.
- Perform search in repos started from the more recent ones.
- Return repos that contain apk releases.
- Github api limits query matches to 1000 so a max of 1000 matched repos will be checked for apk files.
- Usage: save the script to file and run python script.py “search_query” requires python obviously and a Github token (the limit without a token is 100 queries per hour)
- Requires Python
pkg install python
and requestspip install requests
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import requests
import argparse
import base64
from datetime import datetime, timedelta
# Hardcoded GitHub personal access token
GITHUB_TOKEN = "your_personal_access_token" # Replace with your actual token
def search_repos_and_check_apk(search_query):
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json"
}
# Get the date 1 year ago from today
one_year_ago = datetime.now() - timedelta(days=365)
# Set the base search URL and parameters
search_url = "https://api.github.com/search/repositories"
per_page = 100 # Set to the maximum number of repositories per page
total_repos_checked = 0
total_repos_with_apk = 0
matching_repos = {}
page = 1
max_repos = 1000 # Limit to GitHub's maximum search results
while total_repos_checked < max_repos:
# Search for repositories with the query in their description
search_params = {
"q": f"{search_query} in:description",
"sort": "updated",
"order": "desc",
"per_page": per_page,
"page": page
}
response = requests.get(search_url, headers=headers, params=search_params)
if response.status_code != 200:
print(f"Error fetching repositories: {response.status_code}")
break
repositories = response.json().get("items", [])
if not repositories:
print("No more repositories found.")
break
# Check for README.md and APK releases in each repository
for repo in repositories:
total_repos_checked += 1
repo_full_name = repo["full_name"]
repo_updated_at = datetime.strptime(repo["updated_at"], "%Y-%m-%dT%H:%M:%SZ")
repo_archived = repo["archived"]
# Exclude repositories that are archived or not updated within the last year
if repo_archived or repo_updated_at < one_year_ago:
continue
# Fetch repository contents to find README.md
contents_url = f"https://api.github.com/repos/{repo_full_name}/contents"
contents_response = requests.get(contents_url, headers=headers)
if contents_response.status_code == 200:
contents = contents_response.json()
readme_content = None
# Look for README.md in repository contents
for item in contents:
if item["name"].lower() == "readme.md" and item["type"] == "file":
readme_url = item["url"]
readme_response = requests.get(readme_url, headers=headers)
if readme_response.status_code == 200:
readme_data = readme_response.json()
readme_content = base64.b64decode(readme_data["content"]).decode("utf-8")
if search_query.lower() in readme_content.lower():
break
# Check for APK releases in each repository
releases_url = f"https://api.github.com/repos/{repo_full_name}/releases"
releases_response = requests.get(releases_url, headers=headers)
if releases_response.status_code == 200:
releases = releases_response.json()
if releases:
for release in releases:
for asset in release.get("assets", []):
if asset["name"].endswith(".apk"):
if repo_full_name not in matching_repos or release["published_at"] > matching_repos[repo_full_name]["release_date"]:
matching_repos[repo_full_name] = {
"repo_name": repo["name"],
"repo_url": repo["html_url"],
"description": repo["description"],
"release_tag": release["tag_name"],
"release_date": release["published_at"],
"apk_name": asset["name"],
"apk_download_url": asset["browser_download_url"]
}
total_repos_with_apk += 1
# Move to the next page
page += 1
if len(repositories) < per_page:
# If the number of repositories fetched is less than per_page, there are no more pages
break
# Display results
if not matching_repos:
print("No repositories with APK releases found.")
else:
# Sort matching repos by release date
sorted_repos = sorted(matching_repos.values(), key=lambda x: x["release_date"], reverse=True)
for repo in sorted_repos:
print(f"Repo: {repo['repo_name']} ({repo['repo_url']})")
print(f"Description: {repo['description']}")
print(f"APK: {repo['apk_name']} - Released on: {repo['release_date']}")
print(f"Download URL: {repo['apk_download_url']}")
print("====================================")
# Print totals
print(f"Total repositories checked: {total_repos_checked}")
print(f"Total repositories with APK releases: {total_repos_with_apk}")
if name == "main":
# Setup command line argument parsing
parser = argparse.ArgumentParser(description="Search GitHub repositories for APK releases.")
parser.add_argument("search_query", type=str, help="The search query for the repository description and README.md.")
args = parser.parse_args()
# Call the search function with the command-line argument
search_repos_and_check_apk(search_query=args.search_query)
Print a list of installed apps by ap name.and package name categorized by install source.
Save scipt and bash script.sh > list.txt if you want the list saved to a file
My install sources are: “Aurora/Play Store Apps” “com.android.vending” “Aurora Droid Apps” “com.aurora.adroid” “Obtainium Apps” “dev.imranr.obtainium” “APK Installs” “com.android.packageinstaller” “Unknown” “null”
refine accordingly to match yours
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/bin/bash
# Define a temporary file to hold the raw data
temp_file=$(mktemp)
# Extract APK file paths, package names, and installation methods
pm list packages -3 -f -i | sed -n 's|package:\(/data/app/[^/]\+/[^/]\+/base.apk\)=\([^ ]\+\) installer=\([^ ]\+\)|\1 \2 \3|p' | while read apk package installer; do
# Extract application name using aapt
app_name=$(aapt dump badging "$apk" 2>/dev/null | grep 'application: label=' | awk -F"'" '{print $2}')
# Format application name based on word count
if [ $(echo "$app_name" | awk '{print NF}') -le 1 ]; then
short_name="$app_name"
else
short_name=$(echo "$app_name" | awk '{print $1 "_" $2}')
fi
# Print application name, package name, and installer to the temporary file
echo "$short_name $package $installer" >> "$temp_file"
done
# Define function to print section with separator
print_section() {
local section_title="$1"
local installer_filter="$2"
echo "===================================="
echo "$section_title:"
echo "------------------------------------"
grep "$installer_filter" "$temp_file" | awk '{print $1, $2}'
echo
}
# Print sections with visual separators
print_section "Aurora/Play Store Apps (com.android.vending)" 'com.android.vending'
print_section "Aurora Droid Apps (com.aurora.adroid)" 'com.aurora.adroid'
print_section "Obtainium Apps (dev.imranr.obtainium)" 'dev.imranr.obtainium'
print_section "APK Installs (com.android.packageinstaller)" 'com.android.packageinstaller'
# Print unknown category with proper handling
echo "===================================="
echo "Unknown (null):"
echo "------------------------------------"
grep -v 'com.android.vending\|com.aurora.adroid\|dev.imranr.obtainium\|com.android.packageinstaller' "$temp_file" | awk '{print $1, $2}'
# Clean up temporary file
rm "$temp_file"
List all packages that grants a specific permission e.g. for signature spoofing
1
2
3
4
5
6
for package in $(su -c 'pm list packages' | cut -d':' -f2); do
result=$(su -c "dumpsys package $package 2>/dev/null | grep FAKE_PACKAGE_SIGNATURE")
if [[ $result =~ FAKE_PACKAGE_SIGNATURE ]]; then
echo "$package: $(echo $result | awk -F'android.permission.FAKE_PACKAGE_SIGNATURE:' '{print $2}')"
fi
done
doze
Test if doze is enabled
su -c dumpsys deviceidle enabled
Should return 1
Enable doze
su -c dumpsys deviceidle enable
su -c dumpsys deviceidle force-idle
since Android 12, doze flags have been migrated to DeviceConfig with device_idle as the namespace. To change the configuration, use adb shell device_config put device_idle [KEY] [VALUE]
e.g. device_config put device_idle sensing_to 0
Some recomended values
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env bash
device_config reset trusted_defaults device_idle
device_config put device_idle light_after_inactive_to 30000
device_config put device_idle light_pre_idle_to 120000
device_config put device_idle light_idle_to 300000
device_config put device_idle light_idle_factor 2
device_config put device_idle light_max_idle_to 900000
device_config put device_idle light_idle_maintenance_min_budget 30000
device_config put device_idle light_idle_maintenance_max_budget 180000
device_config put device_idle inactive_to 900000
device_config put device_idle sensing_to 0
device_config put device_idle locating_to 0
device_config put device_idle motion_inactive_to 0
device_config put device_idle idle_after_inactive_to 900000
device_config put device_idle idle_pending_to 60000
device_config put device_idle max_idle_pending_to 120000
device_config put device_idle idle_pending_factor 2
device_config put device_idle idle_to 900000
device_config put device_idle max_idle_to 21600000
device_config put device_idle idle_factor 2
device_config put device_idle wait_for_unlock true
Make changes persistent
su -c device_config set_sync_disabled_for_tests persistent
Read: https://developer.android.com/training/monitoring-device-state/doze-standby
restrict backround battery abusive apps
su -c device_config put activity_manager bg_auto_restrict_abusive_apps 1
su -c device_config put activity_manager bg_current_drain_auto_restrict_abusive_apps_enabled 1
Read:https://source.android.com/docs/core/power/trackers
batterystats
To get a detailed power estimate
su -c 'dumpsys batterystats' | awk '/Statistics since last charge:/,/idle/'
Get screen off discharge rate. Based on Time on battery screen off: and Screen off discharge: sections from dumpsys batterystats
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
result1=$(su -c 'dumpsys batterystats | grep "Time on battery screen off" | awk -F"[hms ]+" "/Time on battery screen off:/ {print \$8*60 + \$9 + \$10/60}"')
result2=$(su -c 'dumpsys batterystats | grep "Screen off discharge" | awk "/Screen off discharge:/ {print \$4}"')
# Checking if both results are non-empty before division
if [[ -n $result1 && -n $result2 ]]; then
result3=$(awk -v r2="$result2" -v r1="$result1" 'BEGIN {printf "%.2f", r2 * 60 / r1}')
result=$(awk -v r3="$result3" 'BEGIN {printf "%.2f", r3 * 100 / 5000}')
echo $result
else
echo "One or both of the results are empty."
fi
Get screen on discharge rate. Based on “Screen on:” and “Screen on discharge:” sections from dumpsys batterystats
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
result1=$(su -c 'dumpsys batterystats | grep "Screen on:" | awk -F"[hms ]+" "/Screen on:/ {print \$4*60 + \$5 + \$6/60; exit}"')
result2=$(su -c 'dumpsys batterystats | grep "Screen on discharge:" | awk "/Screen on discharge:/ {print \$4}"')
# Checking if both results are non-empty before division
if [[ -n $result1 && -n $result2 ]]; then
result3=$(awk -v r2="$result2" -v r1="$result1" 'BEGIN {printf "%.2f", r2 * 60 / r1}')
result=$(awk -v r3="$result3" 'BEGIN {printf "%.2f", r3 * 100 / 5000}')
echo $result
else
echo "One or both of the results are empty."
fi
disable wifi multicast
Some router have wifi multicast enabled and no ui to disable it. It can lead to kernel wakelocks and battery drain.
Check battery drain by WiFi interface
su -c dumpsys wifi |grep mAh
Monitor the interface for multicast
su -c ifconfig wlan0
Disable it
su -c ifconfig wlan0 -multicast
Enable it (just saying)
su -c ifconfig wlan0 multicast
Multicast parameters filepath
su -c cat /proc/net/igmp |grep wlan
Check if it’s enabled in the compressed Kernel configuration under /proc/config.gz
su -c zcat /proc/config.gz |grep CONFIG_IP_MULTICAST
Disabled does not preserve reboot.
Run a script on boot .
With termux-boot:
-
Download, unpackage and install from GitHub actions artifacts https://github.com/termux/termux-boot/actions
-
Add su -c ifconfig wlan0 -multicast in a .sh file under ~/.termux/boot/
ifconfig is deprecated you could use ip:
pkg install iproute2
su -c ip link show
Reveals more interfaces using multicast:
su -c 'ip link show | grep MULTICAST'
- wifi-aware0 (nearby devices)
- p2p0 (WiFi direct)
- gretap (tunel interface)
- erspan (wtf is this?)
bugreport
Open a root terminal and type su -c bugreportz
device will vibrate and it will output a zip file under/data/user_de/0/com.android.shell/files/bugreports
Upload and analize it at https://bathist.dev
Read:https://developer.android.com/topic/performance/power/setup-battery-historian
List permissions of an apk file
aapt d permissions /filepath/file.apk