mirror of
https://github.com/opengapps/opengapps.git
synced 2025-11-08 07:54:31 +00:00
275 lines
13 KiB
Bash
Executable File
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
|
|
}
|