Skip to content

Commit 9fd3008

Browse files
Fix APK signing: narrow FixRuntimePackAssetTypes to .so files only, add apksigner safety net, fix DiscardScreen merge conflict
Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/4f02e4a3-e88b-422b-9009-a7eb331ff567 Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
1 parent 9189f36 commit 9fd3008

3 files changed

Lines changed: 66 additions & 15 deletions

File tree

.github/workflows/release.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,55 @@ jobs:
148148
echo "Found APK: $APK ($APK_SIZE_MB MB)"
149149
echo "apk_path=$APK" >> "$GITHUB_OUTPUT"
150150
151+
# .NET 10 Android SDK may skip debug-signing for Release publish builds.
152+
# Verify the APK is signed; if not, sign it with apksigner using the debug
153+
# keystore so the APK can be sideloaded without INSTALL_PARSE_FAILED_NO_CERTIFICATES.
154+
- name: Verify and sign APK if needed
155+
run: |
156+
APK="${{ steps.find_apk.outputs.apk_path }}"
157+
APKSIGNER="$ANDROID_HOME/build-tools/$(ls "$ANDROID_HOME/build-tools" | sort -V | tail -1)/apksigner"
158+
ZIPALIGN="$ANDROID_HOME/build-tools/$(ls "$ANDROID_HOME/build-tools" | sort -V | tail -1)/zipalign"
159+
160+
if "$APKSIGNER" verify "$APK" 2>/dev/null; then
161+
echo "APK is already signed ✓"
162+
else
163+
echo "::warning::APK is not signed. Signing with debug keystore..."
164+
165+
# Generate debug keystore if it doesn't exist
166+
DEBUG_KS="$HOME/.android/debug.keystore"
167+
if [ ! -f "$DEBUG_KS" ]; then
168+
mkdir -p "$HOME/.android"
169+
keytool -genkeypair -v \
170+
-keystore "$DEBUG_KS" \
171+
-storepass android \
172+
-keypass android \
173+
-alias androiddebugkey \
174+
-keyalg RSA -keysize 2048 -validity 10000 \
175+
-dname "CN=Android Debug,O=Android,C=US"
176+
fi
177+
178+
# Zipalign first (required before apksigner v2 signing)
179+
ALIGNED_APK="${APK%.apk}-aligned.apk"
180+
"$ZIPALIGN" -f -p 4 "$APK" "$ALIGNED_APK"
181+
mv "$ALIGNED_APK" "$APK"
182+
183+
# Sign with debug keystore (v1 + v2 + v3 schemes)
184+
"$APKSIGNER" sign \
185+
--ks "$DEBUG_KS" \
186+
--ks-pass pass:android \
187+
--key-pass pass:android \
188+
--ks-key-alias androiddebugkey \
189+
"$APK"
190+
191+
# Verify signature
192+
if "$APKSIGNER" verify --print-certs "$APK"; then
193+
echo "APK signed successfully ✓"
194+
else
195+
echo "::error::APK signing failed"
196+
exit 1
197+
fi
198+
fi
199+
151200
- name: Upload APK artifact
152201
uses: actions/upload-artifact@v7
153202
with:

osu.Android.props

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,18 @@
7777
<DisableFody>true</DisableFody>
7878
</PropertyGroup>
7979

80-
<!-- Fix for .NET 10 Android AOT misclassifying non-managed assets in runtime pack.
81-
This is critical for Android 16 (API 36) Release/AOT builds where the linker expects
82-
all non-managed assets to be marked as native before the optimization/signing phases.
83-
Scoped to Release only — running this during Debug builds can interfere with how the
84-
Android SDK classifies signing-related items, leading to APKs with null certificate
85-
arrays (INSTALL_PARSE_FAILED_NO_CERTIFICATES). -->
80+
<!-- Fix for .NET 10 Android AOT misclassifying native .so assets in runtime pack.
81+
Only .so files need the AssetType override — the previous broader filter
82+
(all non-DLL, non-PDB) incorrectly reclassified signing metadata and config
83+
files, which corrupted the APK signature (INSTALL_PARSE_FAILED_NO_CERTIFICATES).
84+
Scoped to Release only to avoid interfering with Debug builds. -->
8685
<Target Name="FixRuntimePackAssetTypes" AfterTargets="ResolveRuntimePackAssets;ComputeFilesToPublish;ComputeResolvedFilesToPublishList"
8786
Condition="'$(Configuration)' == 'Release'">
8887
<ItemGroup>
89-
<RuntimePackAsset Update="@(RuntimePackAsset)" Condition="'%(Extension)' != '.dll' AND '%(Extension)' != '.pdb'">
88+
<RuntimePackAsset Update="@(RuntimePackAsset)" Condition="'%(Extension)' == '.so'">
9089
<AssetType>native</AssetType>
9190
</RuntimePackAsset>
92-
<ResolvedFileToPublish Update="@(ResolvedFileToPublish)" Condition="'%(Extension)' != '.dll' AND '%(Extension)' != '.pdb'">
91+
<ResolvedFileToPublish Update="@(ResolvedFileToPublish)" Condition="'%(Extension)' == '.so'">
9392
<AssetType>native</AssetType>
9493
</ResolvedFileToPublish>
9594
</ItemGroup>

osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/DiscardScreen.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ public partial class DiscardScreen : RankedPlaySubScreen
4949

5050
private const int card_play_samples = 2;
5151
private Sample?[]? cardPlaySamples;
52+
53+
/// <summary>
54+
/// Whether the local user has discarded cards.
55+
/// </summary>
56+
private bool hasDiscardedCards;
57+
5258
private Sample? timeRunningOutSample;
5359
private SampleChannel? timeRunningOutSampleChannel;
5460

@@ -147,13 +153,10 @@ protected override void LoadComplete()
147153
}
148154

149155
private bool shouldPlayWarningSample
150-
{
151-
get => matchInfo.Stage.Value == RankedPlayStage.CardDiscard
152-
&& stageDuration > TimeSpan.FromSeconds(warning_time_threshold)
153-
&& stageEndTime - DateTimeOffset.Now < TimeSpan.FromSeconds(warning_time_threshold)
154-
&& !field;
155-
set;
156-
}
156+
=> matchInfo.Stage.Value == RankedPlayStage.CardDiscard
157+
&& stageDuration > TimeSpan.FromSeconds(warning_time_threshold)
158+
&& stageEndTime - DateTimeOffset.Now < TimeSpan.FromSeconds(warning_time_threshold)
159+
&& !hasDiscardedCards;
157160

158161
protected override void Update()
159162
{

0 commit comments

Comments
 (0)