Files
opengapps/scripts/inc.sourceshelper.sh
T
2022-01-17 22:27:15 +01:00

275 lines
13 KiB
Bash
Executable File

#This file is part of The Open GApps script of @mfonville.
#
# The Open GApps scripts are 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.
#
# These scripts are 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.
#
LOWESTAPI_all="19"
LOWESTAPI_arm="19"
LOWESTAPI_arm64="21"
LOWESTAPI_x86="19"
LOWESTAPI_x86_64="21"
LOWESTAPI_leanback="22"
GOOGLECERT="Issuer: C=US, ST=C(A|alifornia), L=Mountain View, O=(Google((|,) Inc(|.)|)|Android), OU=(Google((|,) Inc(|.)|)|Android), CN="
#IMPORTCERTS="" #if this value is set to non-zero, new certificates matching above regexp will be imported
INCOMPLETEFILES=1
INVALIDCERT=2
UNSIGNEDFILES=3
APKTOOLFAILED=11
checktools bc
install -d "$CACHE"
touch "$CACHE/signaturecache.txt"
addsignaturetocache() {
if [ ! -s "$CACHE/signaturecache.txt" ]; then
echo -e " " >> "$CACHE/signaturecache.txt" # our sed replacement does not work on empty file
fi
sha1sum="$(sha1sum "$1" | cut -d ' ' -f 1)"
sed -i "\#^[0-9a-f]\{40\}=$1#{h;s#[0-9a-f]\{40\}=#$sha1sum=#};\${x;/^\$/{s##$sha1sum=$1#;H};x}" "$CACHE/signaturecache.txt"
}
getapkproperties(){
apkproperties="$(aapt dump badging "$1" 2>/dev/null)"
name="$(echo "$apkproperties" | grep -a "application-label:" | sed 's/application-label://g' | sed "s/'//g")"
package="$(echo "$apkproperties" | awk '/package:/ {print $2}' | sed s/name=//g | sed s/\'//g | awk '{print tolower($0)}')"
versionname="$(echo "$apkproperties" | awk -F="'" '/versionName=/ {print $4}' | sed "s/'.*//g")"
versioncode="$(echo "$apkproperties" | awk -F="'" '/versionCode=/ {print $3}' | sed "s/'.*//g")"
sdkversion="$(echo "$apkproperties" | grep -a "sdkVersion:" | sed 's/sdkVersion://' | sed "s/'//g")"
compatiblescreens="$(echo "$apkproperties" | grep -a "compatible-screens:'")" #the ' is added to prevent detection of lines that only have compatiblescreens but without any values
native="$(echo "$apkproperties" | grep -av "alt-native-code:" | grep -a "native-code:" | sed 's/native-code://g' | sed "s/'//g") " # add a space at the end
altnative="$(echo "$apkproperties" | grep -a "alt-native-code:" | sed 's/alt-native-code://g' | sed "s/'//g") " # add a space at the end
leanback="$(echo "$apkproperties" | grep -a "android.software.leanback" | grep -v "\-not\-required" | awk -F [.\'] '{print $(NF-1)}')" # 'leanback'
vrmode="$(echo "$apkproperties" | grep -a "android.software.vr.mode" | grep -v "\-not\-required" | awk -F [.\'] '{print $(NF-2)$(NF-1)}')" # 'vrmode'
watch="$(echo "$apkproperties" | grep -a "android.hardware.type.watch" | awk -F [.\'] '{print $(NF-1)}')" # 'watch'
ram="$(echo "$apkproperties" | grep -a "android.hardware.ram" | grep -v "\-not\-required" | awk -F [.\'] '{print $(NF-1)}')" # 'low' => means Android Go (https://developer.android.com/docs/quality-guidelines/build-for-billions/device-capacity#androidgo)
dialer_go_experience="$(echo "$apkproperties" | grep -a "com.google.android.apps.dialer.GO_EXPERIENCE" | grep -v "\-not\-required" | awk -F [.\'] '{print $(NF-1)}')" # 'GO_EXPERIENCE' => means Android Go
case "$versionname" in
*leanback*) leanback="leanback";;
esac
if [ -n "$leanback" ]; then
case "$package" in
*inputmethod*) ;; #if package is an inputmethod, it will have leanback as feature described, but we don't want it recognized as such
*.leanback) ;; #if package already has leanback at the end of its name, we don't need to add it ourselves
*) package="$package.$leanback";; #special leanback versions need a different packagename
esac
fi
sdkversionhacks
case $package in
"com.android.vending" |\
"com.android.vending.leanback" |\
"com.google.android.androidforwork" |\
"com.google.android.apps.gcs" |\
"com.google.android.apps.mediashell.leanback" |\
"com.google.android.apps.nexuslauncher" |\
"com.google.android.apps.pixelmigrate" |\
"com.google.android.apps.restore" |\
"com.google.android.apps.wellbeing" |\
"com.google.android.apps.tv.launcherx.leanback" |\
"com.google.android.as" |\
"com.google.android.backuptransport" |\
"com.google.android.carriersetup" |\
"com.google.android.configupdater" |\
"com.google.android.contacts" |\
"com.google.android.dialer" |\
"com.google.android.ext.services" |\
"com.google.android.feedback" |\
"com.google.android.gms" |\
"com.google.android.gms.leanback" |\
"com.google.android.gms.setup" |\
"com.google.android.googlequicksearchbox" |\
"com.google.android.gsf" |\
"com.google.android.gsf.login" |\
"com.google.android.katniss.leanback" |\
"com.google.android.leanbacklauncher.leanback" |\
"com.google.android.leanbacklauncher.recommendations.leanback" |\
"com.google.android.onetimeinitializer" |\
"com.google.android.packageinstaller" |\
"com.google.android.pano.packageinstaller" |\
"com.google.android.partnersetup" |\
"com.google.android.setupwizard" |\
"com.google.android.storagemanager" |\
"com.google.android.tag" |\
"com.google.android.tungsten.overscan" |\
"com.google.android.tungsten.setupwraith" |\
"com.google.android.tv.leanback" |\
"com.google.android.tv.remote" |\
"com.google.android.tv.remotepairing" |\
"com.google.android.tv.remote.service.leanback") type="priv-app";;
*) type="app";;
esac
# Put the Go edition apps in a dir suffixed by "-go"
if [ "a$ram" == "alow" ] \
|| [ "a$dialer_go_experience" == "aGO_EXPERIENCE" ] \
|| [ "$package" == "com.google.android.apps.cameralite" ] \
|| [ "$package" == "com.google.android.apps.mapslite" ] \
|| [ "$package" == "com.google.android.apps.navlite" ] \
|| [ "$package" == "com.google.android.apps.nbu.files" ] \
|| [ "$package" == "com.google.android.apps.photosgo" ] \
|| [ "$package" == "com.google.android.apps.searchlite" ] \
|| [ "$package" == "com.google.android.apps.youtube.mango" ]; then
type="${type}${GO_DIR_SUFFIX}"
fi
#we do this on purpose after the priv-app detection to emulate the priv-app of the normal app
if [ -n "$watch" ]; then
case "$package" in
com.android.vending* |\
com.google.android.apps.enterprise.dmagent* |\
com.google.android.apps.fitness* |\
com.google.android.apps.maps* |\
com.google.android.apps.messaging* |\
com.google.android.apps.walletnfcrel* |\
com.google.android.calculator* |\
com.google.android.deskclock* |\
com.google.android.gms* |\
com.google.android.googlequicksearchbox* |\
com.google.android.inputmethod.latin* |\
com.google.android.marvin.talkback* |\
com.google.android.keep* |\
com.google.android.music* |\
com.google.android.talk*)
package="$package.watch" ;; # special watch versions need a different packagename
*) ;; # Otherwise ignore the watch flag
esac
fi
if [ -n "$vrmode" ]; then
case "$package" in
com.google.android.apps.photos* |\
com.google.android.videos*)
package="$package.vrmode" ;; # special vrmode versions need a different packagename
*) ;; # Otherwise ignore the vrmode flag
esac
fi
if [ -n "$STUB" ]; then
package="$package.$STUB" # xxx.stub
fi
if [ -n "$BETA" ]; then
package="$package.$BETA" # xxx.stub.beta
fi
stub="" #make sure value is initialized
beta="" #make sure value is initialized
case "$1" in
*.stub/*) stub="stub" #report stub status as a property
case "$package" in
*.stub);;
*) package="$package.stub";; # set .stub in package name if not set yet
esac;;
esac
case "$1" in
*.beta/*) beta="beta" #report beta status as a property
case "$package" in
*.beta);;
*) package="$package.beta";; # set .beta in package name if not set yet
esac;;
esac
if [ "$(echo $compatiblescreens)" = "" ]; then # we can't use -z here, because there can be a linecontrol character or such in it
dpis="nodpi"
else
dpis=$(echo "$compatiblescreens" | grep "compatible-screens:" | grep -oE "/([0-9][0-9])[0-9]" | sort -u | tr -d '\012\015' | tr '/' '-' | cut -c 2-)
fi
}
getarchitecturesfromlib() {
# Some packages don't have native-code specified, but are still depending on it
# If multiple architectures are found; we assume it to be only compatible with the highest architecture and not multi-arch
architectures=""
libfiles=$(unzip -qqql "$1" "lib/*" | tr -s ' ' | cut -d ' ' -f5-)
for lib in $libfiles; do
#this gives all files found in the lib-folder(s), check their paths for which architectures' libs are included
arch="$(echo "$lib" | awk 'BEGIN { FS = "/" } ; {print $2}')" #add a space at the end
if ! echo "$architectures" | grep -q "$arch "; then #only add if this architecture is not yet in the list; use a space to distinguish substrings (e.g. x86 vs x86_64)
architectures="$architectures$arch "
fi
done
if [ -z "$architectures" ]; then #If the package really has no native code
architectures="all"
fi
}
getsignaturefromcache() {
sha1sum="$(sha1sum "$1" | cut -d ' ' -f 1)"
return $(grep -oqE "^$sha1sum=$1\$" "$CACHE/signaturecache.txt")
}
getsetupwizardproduct() {
# Setupwizard has various variations, depending on product type. We need to decompile the APK to find this value
# this function is not part of the regular getapkproperties script because it is heavy and only necessary when adding an APK
tmpframedir="$(mktemp -d)"
tmpapkdir="$(mktemp -d)"
if java -jar "$APKTOOL" -q d -b -f -s -o "$tmpapkdir" -p "$tmpframedir" "$1"; then
product="$(grep '<string name="product">' "$tmpapkdir/res/values/strings.xml" | sed -r 's#.*<string name="product">([^<]*)</string>#\1#')"
rm -rf "$tmpapkdir"
rm -rf "$tmpframedir"
else
return $APKTOOLFAILED
fi
}
verifyapk() {
# Check if we have a verification "ok" in signaturecache
if getsignaturefromcache "$1"; then
return 0 # it is in cache
fi
notinzip=""
if importcert "$1" "$2"; then #always import, because sometimes new certificates are supplied but it would never be detected because the exitcode of jarsigner -verify would be 0, because the existing certificates would suffice
if ! timeout 1m jarsigner -verify -keystore "$CERTIFICATES/opengapps.keystore" -strict "$1" 1>/dev/null 2>&1; then # timeout added because sometimes jarsigner can get stuck for an unknown reason
return $UNSIGNEDFILES #contains files not signed by Google. APK not imported
fi
else
return $INVALIDCERT #no valid Google certificate. Certificate and APK not imported
fi
manifestlist="$(unzip -p "$1" "META-INF/MANIFEST.MF" | sed ':a;N;$!ba;s/\r\n //g' | tr -d '\r' | awk -F' ' '/Name:/ {print $NF}')"
ziplist="$(unzip -Z -1 "$1")"
RSAFILE="META-INF/CERT"
unzip -l "$1" | grep -q "$RSAFILE.RSA"
if [ "$?" != "0" ]; then
RSAFILE="META-INF/GOOGPLAY"
fi
notinzip="$(printf "%s\n%s\n" "$manifestlist" "$ziplist" | grep -vxF -e "$RSAFILE.RSA" -e "$RSAFILE.SF" -e "META-INF/MANIFEST.MF" | sort | uniq -u)"
if [ -n "$notinzip" ]; then
return $INCOMPLETEFILES #files were mentioned in the signed manifest but are not present in the APK
fi
# add the "ok" to signaturecache
addsignaturetocache "$1"
}
importcert() {
RSAFILE="META-INF/CERT"
unzip -l "$1" | grep -q "$RSAFILE.RSA"
if [ "$?" != "0" ]; then
RSAFILE="META-INF/GOOGPLAY"
fi
unzip -p "$1" "$RSAFILE.RSA" | openssl pkcs7 -inform DER -print_certs -text | grep -q -E "$GOOGLECERT" || return 1 #Certificate is not issued by Google.
alias="$(unzip -p "$1" "$RSAFILE.RSA" | openssl pkcs7 -inform DER -print_certs -text | awk -F' ' '/Serial Number:/ {if(NF==2){getline nextline;gsub(/[ \t:]/,"",nextline);print "ibase=16;",toupper(nextline)}else{print "ibase=10;",$(NF-1)}}' | bc)"
if ! timeout 1m keytool -list -keystore "$CERTIFICATES/opengapps.keystore" -storepass "opengapps" -noprompt -alias "$alias" 1>/dev/null 2>&1; then
if [ -n "$IMPORTCERTS" ]; then #set this variable in your environment if you want to permit the script to update the keystore
unzip -p "$1" "$RSAFILE.RSA" | openssl pkcs7 -inform DER -print_certs -text | keytool -importcert -keystore "$CERTIFICATES/opengapps.keystore" -storepass "opengapps" -noprompt -alias "$alias" 1>/dev/null 2>&1
if [ -n "$2" ]; then #silent mode if value is set
echo "Certificate with alias $alias is signed by Google and added to the keystore"
fi
elif [ -z "$2" ]; then #only output if no silent mode value is set
echo "APK contains a new Google certificate not yet available in the keystore, please contact Open GApps maintainer to get it included"
fi
fi
return 0
}