emulator on android: crashes but APKs are building
This commit is contained in:
+6
-7
@@ -7,7 +7,7 @@ default-run = "emulator"
|
|||||||
[lib]
|
[lib]
|
||||||
name = "dsa_rs"
|
name = "dsa_rs"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
type = "cdylib"
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "emulator"
|
name = "emulator"
|
||||||
@@ -25,17 +25,16 @@ toml = { version = "0.8.23", optional = true }
|
|||||||
serde = { version = "1.0.219", features = ["derive"], optional = true }
|
serde = { version = "1.0.219", features = ["derive"], optional = true }
|
||||||
egui_file = "0.22.1"
|
egui_file = "0.22.1"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["config"]
|
||||||
|
discord-rpc = ["dep:discord-presence"]
|
||||||
|
config = ["dep:toml", "dep:serde"]
|
||||||
|
|
||||||
# Add support for Android for the fun of it.
|
# Add support for Android for the fun of it.
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
winit = { version = "0.30.11", features = ["android-native-activity"] }
|
winit = { version = "0.30.11", features = ["android-native-activity"] }
|
||||||
# jni = "0.21.1"
|
# jni = "0.21.1"
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies.eframe]
|
[target.'cfg(target_os = "android")'.dependencies.eframe]
|
||||||
version = "0.31.1"
|
version = "0.31.1"
|
||||||
features = ["android-native-activity"]
|
features = ["android-native-activity"]
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["config"]
|
|
||||||
discord-rpc = ["dep:discord-presence"]
|
|
||||||
config = ["dep:toml", "dep:serde"]
|
|
||||||
|
|||||||
@@ -13,3 +13,117 @@
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
pub mod emulator;
|
pub mod emulator;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
sync::{
|
||||||
|
Arc,
|
||||||
|
mpsc::{Receiver, Sender},
|
||||||
|
},
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
use winit::platform::android::{EventLoopBuilderExtAndroid, activity::AndroidApp};
|
||||||
|
|
||||||
|
use crate::emulator::{
|
||||||
|
misc::rpc::RpcClient,
|
||||||
|
system::{
|
||||||
|
emulator::run_emulator,
|
||||||
|
memory::MainStore,
|
||||||
|
model::{Command, State},
|
||||||
|
processor::Processor,
|
||||||
|
},
|
||||||
|
ui::{
|
||||||
|
control_unit::ControlPanel, display::Display, editor::Editor,
|
||||||
|
interface::EmulatorUI, memory_inspector::MemoryInspector,
|
||||||
|
stack_inspector::StackInspector,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub fn android_main(app: AndroidApp) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
use crate::emulator::{config::Config, misc::rpc::get_rpc_client_or_none};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
// Initialize channels and read in configuration.
|
||||||
|
let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel();
|
||||||
|
let (state_sender, state_reciever) = std::sync::mpsc::channel();
|
||||||
|
let config = Config::load(Path::new(".dsa.emulator.toml"))?;
|
||||||
|
|
||||||
|
// Setup RPC if enabled.
|
||||||
|
let (rpc_sender, rpc_reciever) = std::sync::mpsc::channel();
|
||||||
|
|
||||||
|
let rpc_client =
|
||||||
|
get_rpc_client_or_none(&config, rpc_sender, rpc_reciever)?.map(Arc::new);
|
||||||
|
|
||||||
|
setup_emulator(cmd_receiver, state_sender, rpc_client);
|
||||||
|
|
||||||
|
let ui = setup_ui(cmd_sender, state_reciever);
|
||||||
|
|
||||||
|
// Run UI.
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
let options = eframe::NativeOptions {
|
||||||
|
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
|
||||||
|
event_loop_builder: Some(Box::new(move |builder| {
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
builder.with_android_app(app);
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
eframe::run_native(
|
||||||
|
"DSA Simulator (Damn Simple Architecture 🔥)",
|
||||||
|
options,
|
||||||
|
Box::new(move |cc| {
|
||||||
|
cc.egui_ctx.set_visuals(egui::Visuals::default());
|
||||||
|
Ok(Box::new(ui))
|
||||||
|
}),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_emulator(
|
||||||
|
cmd_receiver: Receiver<Command>,
|
||||||
|
state_sender: Sender<State>,
|
||||||
|
rpc_client: Option<Arc<RpcClient>>,
|
||||||
|
) {
|
||||||
|
let main_store = MainStore::new();
|
||||||
|
let processor = Processor::new(Box::new(main_store), vec![]);
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
run_emulator(&cmd_receiver, &state_sender, processor, rpc_client.as_ref());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the [`EmulatorUI`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn setup_ui(
|
||||||
|
cmd_sender: Sender<Command>,
|
||||||
|
state_reciever: Receiver<State>,
|
||||||
|
) -> EmulatorUI {
|
||||||
|
let mut ui = EmulatorUI::new(cmd_sender.clone(), state_reciever);
|
||||||
|
|
||||||
|
// Create UI modules.
|
||||||
|
let control_unit = ControlPanel::new(cmd_sender.clone());
|
||||||
|
|
||||||
|
ui.add_component(Box::new(control_unit));
|
||||||
|
|
||||||
|
let mem_inspector = MemoryInspector::new(cmd_sender.clone());
|
||||||
|
ui.add_component(Box::new(mem_inspector));
|
||||||
|
|
||||||
|
let stack_inspector = StackInspector::new();
|
||||||
|
ui.add_component(Box::new(stack_inspector));
|
||||||
|
|
||||||
|
let editor = Editor::new(cmd_sender);
|
||||||
|
ui.add_component(Box::new(editor));
|
||||||
|
|
||||||
|
let display = Display::new();
|
||||||
|
ui.add_component(Box::new(display));
|
||||||
|
|
||||||
|
let history = emulator::ui::history::History::new();
|
||||||
|
ui.add_component(Box::new(history));
|
||||||
|
|
||||||
|
ui
|
||||||
|
}
|
||||||
|
|||||||
+4
-107
@@ -1,26 +1,7 @@
|
|||||||
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{
|
|
||||||
path::Path,
|
|
||||||
sync::mpsc::{Receiver, Sender},
|
|
||||||
thread,
|
|
||||||
};
|
|
||||||
|
|
||||||
use dsa_rs::emulator::misc::rpc::{RpcClient, get_rpc_client_or_none};
|
use dsa_rs::emulator::{config::Config, misc::rpc::get_rpc_client_or_none};
|
||||||
|
|
||||||
use dsa_rs::emulator::{
|
|
||||||
config::Config,
|
|
||||||
system::{
|
|
||||||
emulator::run_emulator,
|
|
||||||
memory::MainStore,
|
|
||||||
model::{Command, State},
|
|
||||||
processor::Processor,
|
|
||||||
},
|
|
||||||
ui::{
|
|
||||||
control_unit::ControlPanel, display::Display, editor::Editor,
|
|
||||||
interface::EmulatorUI, memory_inspector::MemoryInspector,
|
|
||||||
stack_inspector::StackInspector,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// Initialize channels and read in configuration.
|
// Initialize channels and read in configuration.
|
||||||
@@ -34,9 +15,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let rpc_client =
|
let rpc_client =
|
||||||
get_rpc_client_or_none(&config, rpc_sender, rpc_reciever)?.map(Arc::new);
|
get_rpc_client_or_none(&config, rpc_sender, rpc_reciever)?.map(Arc::new);
|
||||||
|
|
||||||
setup_emulator(cmd_receiver, state_sender, rpc_client);
|
dsa_rs::setup_emulator(cmd_receiver, state_sender, rpc_client);
|
||||||
|
|
||||||
let ui = setup_ui(cmd_sender, state_reciever);
|
let ui = dsa_rs::setup_ui(cmd_sender, state_reciever);
|
||||||
|
|
||||||
// Run UI.
|
// Run UI.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
@@ -56,87 +37,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_emulator(
|
|
||||||
cmd_receiver: Receiver<Command>,
|
|
||||||
state_sender: Sender<State>,
|
|
||||||
rpc_client: Option<Arc<RpcClient>>,
|
|
||||||
) {
|
|
||||||
let main_store = MainStore::new();
|
|
||||||
let processor = Processor::new(Box::new(main_store), vec![]);
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
|
||||||
run_emulator(&cmd_receiver, &state_sender, processor, rpc_client.as_ref());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates the [`EmulatorUI`].
|
|
||||||
fn setup_ui(cmd_sender: Sender<Command>, state_reciever: Receiver<State>) -> EmulatorUI {
|
|
||||||
let mut ui = EmulatorUI::new(cmd_sender.clone(), state_reciever);
|
|
||||||
|
|
||||||
// Create UI modules.
|
|
||||||
let control_unit = ControlPanel::new(cmd_sender.clone());
|
|
||||||
|
|
||||||
ui.add_component(Box::new(control_unit));
|
|
||||||
|
|
||||||
let mem_inspector = MemoryInspector::new(cmd_sender.clone());
|
|
||||||
ui.add_component(Box::new(mem_inspector));
|
|
||||||
|
|
||||||
let stack_inspector = StackInspector::new();
|
|
||||||
ui.add_component(Box::new(stack_inspector));
|
|
||||||
|
|
||||||
let editor = Editor::new(cmd_sender.clone());
|
|
||||||
ui.add_component(Box::new(editor));
|
|
||||||
|
|
||||||
let display = Display::new();
|
|
||||||
ui.add_component(Box::new(display));
|
|
||||||
|
|
||||||
let history = dsa_rs::emulator::ui::history::History::new();
|
|
||||||
ui.add_component(Box::new(history));
|
|
||||||
|
|
||||||
ui
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
use winit::platform::android::{EventLoopBuilderExtAndroid, activity::AndroidApp};
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
fn android_main(app: AndroidApp) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
// Initialize channels and read in configuration.
|
|
||||||
let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel();
|
|
||||||
let (state_sender, state_reciever) = std::sync::mpsc::channel();
|
|
||||||
let config = Config::load(Path::new(".dsa.emulator.toml"))?;
|
|
||||||
|
|
||||||
// Setup RPC if enabled.
|
|
||||||
let (rpc_sender, rpc_reciever) = std::sync::mpsc::channel();
|
|
||||||
|
|
||||||
let rpc_client =
|
|
||||||
get_rpc_client_or_none(&config, rpc_sender, rpc_reciever)?.map(Arc::new);
|
|
||||||
|
|
||||||
setup_emulator(cmd_receiver, state_sender, rpc_client);
|
|
||||||
|
|
||||||
let ui = setup_ui(cmd_sender, state_reciever);
|
|
||||||
|
|
||||||
// Run UI.
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
let options = eframe::NativeOptions {
|
|
||||||
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
|
|
||||||
event_loop_builder: Some(Box::new(move |builder| {
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
builder.with_android_app(app);
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
eframe::run_native(
|
|
||||||
"DSA Simulator (Damn Simple Architecture 🔥)",
|
|
||||||
options,
|
|
||||||
Box::new(move |cc| {
|
|
||||||
cc.egui_ctx.set_visuals(egui::Visuals::default());
|
|
||||||
Ok(Box::new(ui))
|
|
||||||
}),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,17 +1,28 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<?xml-model href="http://schemas.android.com/apk/res/android"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.dsa.emulator">
|
package="com.dsa.emulator"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0">
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="35" />
|
||||||
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
|
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
|
||||||
<!-- <uses-permission android:name="android.permission.INTERNET" /> -->
|
<!-- <uses-permission android:name="android.permission.INTERNET" /> -->
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="DSA Emulator"
|
android:label="DSA Emulator"
|
||||||
android:hasCode="false">
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:hasCode="false"
|
||||||
|
android:hardwareAccelerated="true">
|
||||||
<activity
|
<activity
|
||||||
android:name="android.app.NativeActivity"
|
android:name="android.app.NativeActivity"
|
||||||
android:label="DSA Emulator"
|
android:label="DSA Emulator"
|
||||||
android:exported="true">
|
android:exported="true"
|
||||||
|
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||||
|
android:screenOrientation="unspecified"
|
||||||
|
android:launchMode="singleInstance">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.app.lib_name"
|
android:name="android.app.lib_name"
|
||||||
android:value="dsa_rs" />
|
android:value="dsa_rs" />
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
|
||||||
|
$env:ANDROID_HOME = "C:\Users\jacob\AppData\Local\Android\Sdk"
|
||||||
|
$TOOL_PREFIX = "$env:ANDROID_HOME\build-tools\35.0.1"
|
||||||
|
|
||||||
|
# Only really works on Windows, for aarch64.
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
New-Item -ItemType Directory -Force -Path "..\target\apk_build\lib\arm64-v8a"
|
||||||
|
New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\values"
|
||||||
|
New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-hdpi"
|
||||||
|
New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-mdpi"
|
||||||
|
New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-xhdpi"
|
||||||
|
New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-xxhdpi"
|
||||||
|
|
||||||
|
# Copy the shared library
|
||||||
|
Copy-Item "..\target\aarch64-linux-android\release\libdsa_rs.so" "..\target\apk_build\lib\arm64-v8a\"
|
||||||
|
|
||||||
|
# Copy the manifest
|
||||||
|
Copy-Item "..\resources\emulator\AndroidManifest.xml" "..\target\apk_build\AndroidManifest.xml"
|
||||||
|
|
||||||
|
# Copy the icons
|
||||||
|
Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-hdpi\ic_launcher.png"
|
||||||
|
Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-mdpi\ic_launcher.png"
|
||||||
|
Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-xhdpi\ic_launcher.png"
|
||||||
|
Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-xxhdpi\ic_launcher.png"
|
||||||
|
|
||||||
|
# Create strings.xml
|
||||||
|
@"
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">DSA Emulator</string>
|
||||||
|
</resources>
|
||||||
|
"@ | Out-File -FilePath "..\target\apk_build\res\values\strings.xml" -Encoding utf8
|
||||||
|
|
||||||
|
# Change to build directory
|
||||||
|
Push-Location "..\target\apk_build"
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Compile resources
|
||||||
|
& "$TOOL_PREFIX\aapt2.exe" compile --dir res -o compiled_resources.zip
|
||||||
|
|
||||||
|
# Link resources
|
||||||
|
& "$TOOL_PREFIX\aapt2.exe" link -o unaligned.apk `
|
||||||
|
-I "$env:ANDROID_HOME\platforms\android-35\android.jar" `
|
||||||
|
--manifest AndroidManifest.xml `
|
||||||
|
compiled_resources.zip
|
||||||
|
|
||||||
|
# Add native libraries to APK
|
||||||
|
& "C:\Program Files\7-Zip\7z.exe" a -tzip unaligned.apk lib\*
|
||||||
|
|
||||||
|
# Align APK
|
||||||
|
& "$TOOL_PREFIX\zipalign.exe" -v 4 unaligned.apk aligned.apk
|
||||||
|
|
||||||
|
# Generate debug keystore if it doesn't exist
|
||||||
|
if (-not (Test-Path "debug.keystore")) {
|
||||||
|
& keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 -storepass android -keypass android -dname "CN=Android Debug,O=Android,C=US"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sign APK
|
||||||
|
& "$TOOL_PREFIX\apksigner.bat" sign --ks debug.keystore --ks-key-alias androiddebugkey --ks-pass pass:android --key-pass pass:android --out dsa_emulator.apk aligned.apk
|
||||||
|
|
||||||
|
# Copy final APK
|
||||||
|
Copy-Item "dsa_emulator.apk" "..\dsa_emulator.apk"
|
||||||
|
|
||||||
|
Write-Host "APK created successfully at: ..\target\dsa_emulator.apk" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Build failed: $_"
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
# Return to original directory
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
export ANDROID_HOME="/mnt/c/Users/jacob/AppData/Local/Android/Sdk"
|
||||||
|
export TOOL_PREFIX="$ANDROID_HOME/build-tools/35.0.1"
|
||||||
|
|
||||||
|
# Only really works on Linux, for aarch64.
|
||||||
|
|
||||||
|
mkdir -p ../target/apk_build/lib/arm64-v8a
|
||||||
|
mkdir -p ../target/apk_build/res/values
|
||||||
|
mkdir -p ../target/apk_build/res/mipmap-hdpi
|
||||||
|
mkdir -p ../target/apk_build/res/mipmap-mdpi
|
||||||
|
mkdir -p ../target/apk_build/res/mipmap-xhdpi
|
||||||
|
mkdir -p ../target/apk_build/res/mipmap-xxhdpi
|
||||||
|
|
||||||
|
# Copy the shared library.
|
||||||
|
cp ../target/aarch64-linux-android/release/libdsa_rs.so ../target/apk_build/lib/arm64-v8a/
|
||||||
|
|
||||||
|
# Copy the manifest.
|
||||||
|
cp AndroidManifest.xml ../target/apk_build/AndroidManifest.xml
|
||||||
|
|
||||||
|
cat << EOF > ../target/apk_build/res/values/strings.xml
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">DSA Emulator</string>
|
||||||
|
</resources>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
pushd ../target/apk_build
|
||||||
|
|
||||||
|
$TOOL_PREFIX/aapt2 compile --dir res -o compiled_resources.zip
|
||||||
|
$TOOL_PREFIX/aapt2 link -o unaligned.apk \
|
||||||
|
-I "$ANDROID_HOME/platforms/android-35/android.jar" \
|
||||||
|
--manifest AndroidManifest.xml \
|
||||||
|
compiled_resources.zip
|
||||||
|
|
||||||
|
zip -r unaligned.apk lib/
|
||||||
|
|
||||||
|
$TOOL_PREFIX/zipalign -v 4 unaligned.apk aligned.apk
|
||||||
|
|
||||||
|
keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 -storepass android -keypass android -dname "CN=Android Debug,O=Android,C=US"
|
||||||
|
|
||||||
|
$TOOL_PREFIX/apksigner sign --ks debug.keystore --ks-key-alias androiddebugkey --ks-pass pass:android --key-pass pass:android --out dsa_emulator.apk aligned.apk
|
||||||
|
|
||||||
|
cp dsa_emulator.apk ../dsa_emulator.apk
|
||||||
|
|
||||||
|
popd
|
||||||
Reference in New Issue
Block a user