Compare commits
18 Commits
basedoc
...
split_work
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3582592d2c | ||
|
|
337d6c2fd3 | ||
|
|
2eced46a37 | ||
|
|
1a80333747 | ||
|
|
fb61b779a6 | ||
|
|
5899af6a2f | ||
|
|
cbca79b95b | ||
|
|
02586cf21f | ||
|
|
80a6fa7a01 | ||
|
|
6b769675fa | ||
|
|
e5aa2ce01f | ||
|
|
cbc638c6ba | ||
|
|
abc1be4872 | ||
|
|
f1b2d6bbbb | ||
|
|
90926d40ad | ||
|
|
b4caa483dd | ||
|
|
87da137238 | ||
|
|
b3f626feee |
20
FAQ.md
20
FAQ.md
@@ -7,7 +7,7 @@ Here are the common reported problems and their status.
|
|||||||
If you encounter any error, the first step is to upgrade to the latest version.
|
If you encounter any error, the first step is to upgrade to the latest version.
|
||||||
|
|
||||||
|
|
||||||
## `adb` issues
|
## `adb` and USB issues
|
||||||
|
|
||||||
`scrcpy` execute `adb` commands to initialize the connection with the device. If
|
`scrcpy` execute `adb` commands to initialize the connection with the device. If
|
||||||
`adb` fails, then scrcpy will not work.
|
`adb` fails, then scrcpy will not work.
|
||||||
@@ -133,6 +133,21 @@ Try with another USB cable or plug it into another USB port. See [#281] and
|
|||||||
[#283]: https://github.com/Genymobile/scrcpy/issues/283
|
[#283]: https://github.com/Genymobile/scrcpy/issues/283
|
||||||
|
|
||||||
|
|
||||||
|
## HID/OTG issues on Windows
|
||||||
|
|
||||||
|
On Windows, if `scrcpy --otg` (or `--hid-keyboard`/`--hid-mouse`) results in:
|
||||||
|
|
||||||
|
> ERROR: Could not find any USB device
|
||||||
|
|
||||||
|
(or if only unrelated USB devices are detected), there might be drivers issues.
|
||||||
|
|
||||||
|
Please read [#3654], in particular [this comment][#3654-comment1] and [the next
|
||||||
|
one][#3654-comment2].
|
||||||
|
|
||||||
|
[#3654]: https://github.com/Genymobile/scrcpy/issues/3654
|
||||||
|
[#3654-comment1]: https://github.com/Genymobile/scrcpy/issues/3654#issuecomment-1369278232
|
||||||
|
[#3654-comment2]: https://github.com/Genymobile/scrcpy/issues/3654#issuecomment-1369295011
|
||||||
|
|
||||||
|
|
||||||
## Control issues
|
## Control issues
|
||||||
|
|
||||||
@@ -153,8 +168,7 @@ The default text injection method is [limited to ASCII characters][text-input].
|
|||||||
A trick allows to also inject some [accented characters][accented-characters],
|
A trick allows to also inject some [accented characters][accented-characters],
|
||||||
but that's all. See [#37].
|
but that's all. See [#37].
|
||||||
|
|
||||||
Since scrcpy v1.20 on Linux, it is possible to simulate a [physical
|
Since scrcpy v1.20, it is possible to simulate a [physical keyboard][hid] (HID).
|
||||||
keyboard][hid] (HID).
|
|
||||||
|
|
||||||
[text-input]: https://github.com/Genymobile/scrcpy/issues?q=is%3Aopen+is%3Aissue+label%3Aunicode
|
[text-input]: https://github.com/Genymobile/scrcpy/issues?q=is%3Aopen+is%3Aissue+label%3Aunicode
|
||||||
[accented-characters]: https://blog.rom1v.com/2018/03/introducing-scrcpy/#handle-accented-characters
|
[accented-characters]: https://blog.rom1v.com/2018/03/introducing-scrcpy/#handle-accented-characters
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -1,4 +1,4 @@
|
|||||||
# scrcpy (v1.25)
|
# scrcpy (v2.0)
|
||||||
|
|
||||||
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
|
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
|
||||||
|
|
||||||
@@ -35,15 +35,15 @@ Its features include:
|
|||||||
- [OTG mode](doc/hid-otg.md#otg)
|
- [OTG mode](doc/hid-otg.md#otg)
|
||||||
- and more…
|
- and more…
|
||||||
|
|
||||||
## Requirements
|
## Prerequisites
|
||||||
|
|
||||||
The Android device requires at least API 21 (Android 5.0).
|
The Android device requires at least API 21 (Android 5.0).
|
||||||
|
|
||||||
[Audio forwarding](doc/audio.md) is supported from API 30 (Android 11).
|
[Audio forwarding](doc/audio.md) is supported from API 30 (Android 11).
|
||||||
|
|
||||||
Make sure you [enabled adb debugging][enable-adb] on your device(s).
|
Make sure you [enabled USB debugging][enable-adb] on your device(s).
|
||||||
|
|
||||||
[enable-adb]: https://developer.android.com/studio/command-line/adb.html#Enabling
|
[enable-adb]: https://developer.android.com/studio/debug/dev-options#enable
|
||||||
|
|
||||||
On some devices, you also need to enable [an additional option][control] `USB
|
On some devices, you also need to enable [an additional option][control] `USB
|
||||||
debugging (Security Settings)` (this is an item different from `USB debugging`)
|
debugging (Security Settings)` (this is an item different from `USB debugging`)
|
||||||
@@ -90,10 +90,11 @@ documented in the following pages:
|
|||||||
|
|
||||||
- [Introducing scrcpy][article-intro]
|
- [Introducing scrcpy][article-intro]
|
||||||
- [Scrcpy now works wirelessly][article-tcpip]
|
- [Scrcpy now works wirelessly][article-tcpip]
|
||||||
|
- [Scrcpy 2.0, with audio][article-scrcpy2]
|
||||||
|
|
||||||
[article-intro]: https://blog.rom1v.com/2018/03/introducing-scrcpy/
|
[article-intro]: https://blog.rom1v.com/2018/03/introducing-scrcpy/
|
||||||
[article-tcpip]: https://www.genymotion.com/blog/open-source-project-scrcpy-now-works-wirelessly/
|
[article-tcpip]: https://www.genymotion.com/blog/open-source-project-scrcpy-now-works-wirelessly/
|
||||||
|
[article-scrcpy2]: https://blog.rom1v.com/2023/03/scrcpy-2-0-with-audio/
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ BEGIN
|
|||||||
VALUE "LegalCopyright", "Romain Vimont, Genymobile"
|
VALUE "LegalCopyright", "Romain Vimont, Genymobile"
|
||||||
VALUE "OriginalFilename", "scrcpy.exe"
|
VALUE "OriginalFilename", "scrcpy.exe"
|
||||||
VALUE "ProductName", "scrcpy"
|
VALUE "ProductName", "scrcpy"
|
||||||
VALUE "ProductVersion", "1.25"
|
VALUE "ProductVersion", "2.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
|
|||||||
|
|
||||||
float avg = sc_average_get(&ap->avg_buffering);
|
float avg = sc_average_get(&ap->avg_buffering);
|
||||||
int diff = ap->target_buffering - avg;
|
int diff = ap->target_buffering - avg;
|
||||||
if (abs(diff) < ap->sample_rate / 1000) {
|
if (abs(diff) < (int) ap->sample_rate / 1000) {
|
||||||
// Do not compensate for less than 1ms, the error is just noise
|
// Do not compensate for less than 1ms, the error is just noise
|
||||||
diff = 0;
|
diff = 0;
|
||||||
} else if (diff < 0 && buffered_samples < ap->target_buffering) {
|
} else if (diff < 0 && buffered_samples < ap->target_buffering) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "demuxer.h"
|
#include "demuxer.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <libavutil/channel_layout.h>
|
||||||
#include <libavutil/time.h>
|
#include <libavutil/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|||||||
@@ -816,7 +816,7 @@ sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event) {
|
|||||||
bool relative_mode = sc_screen_is_relative_mode(screen);
|
bool relative_mode = sc_screen_is_relative_mode(screen);
|
||||||
|
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case SC_EVENT_SCREEN_INIT_SIZE:
|
case SC_EVENT_SCREEN_INIT_SIZE: {
|
||||||
// The initial size is passed via screen->frame_size
|
// The initial size is passed via screen->frame_size
|
||||||
bool ok = sc_screen_init_size(screen);
|
bool ok = sc_screen_init_size(screen);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
@@ -824,6 +824,7 @@ sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
case SC_EVENT_NEW_FRAME: {
|
case SC_EVENT_NEW_FRAME: {
|
||||||
bool ok = sc_screen_update_frame(screen);
|
bool ok = sc_screen_update_frame(screen);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
|||||||
@@ -210,6 +210,9 @@ sc_v4l2_sink_open(struct sc_v4l2_sink *vs, const AVCodecContext *ctx) {
|
|||||||
goto error_avformat_free_context;
|
goto error_avformat_free_context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The codec is from the v4l2 encoder, not from the decoder
|
||||||
|
ostream->codecpar->codec_id = encoder->id;
|
||||||
|
|
||||||
int ret = avio_open(&vs->format_ctx->pb, vs->device_name, AVIO_FLAG_WRITE);
|
int ret = avio_open(&vs->format_ctx->pb, vs->device_name, AVIO_FLAG_WRITE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOGE("Failed to open output device: %s", vs->device_name);
|
LOGE("Failed to open output device: %s", vs->device_name);
|
||||||
|
|||||||
@@ -233,10 +233,10 @@ install` must be run as root)._
|
|||||||
|
|
||||||
#### Option 2: Use prebuilt server
|
#### Option 2: Use prebuilt server
|
||||||
|
|
||||||
- [`scrcpy-server-v1.25`][direct-scrcpy-server]
|
- [`scrcpy-server-v2.0`][direct-scrcpy-server]
|
||||||
<sub>SHA-256: `ce0306c7bbd06ae72f6d06f7ec0ee33774995a65de71e0a83813ecb67aec9bdb`</sub>
|
<sub>SHA-256: `9e241615f578cd690bb43311000debdecf6a9c50a7082b001952f18f6f21ddc2`</sub>
|
||||||
|
|
||||||
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.25/scrcpy-server-v1.25
|
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v2.0/scrcpy-server-v2.0
|
||||||
|
|
||||||
Download the prebuilt server somewhere, and specify its path during the Meson
|
Download the prebuilt server somewhere, and specify its path during the Meson
|
||||||
configuration:
|
configuration:
|
||||||
|
|||||||
@@ -107,10 +107,10 @@ with the device IP address you found)_.
|
|||||||
7. Run `scrcpy` as usual.
|
7. Run `scrcpy` as usual.
|
||||||
8. Run `adb disconnect` once you're done.
|
8. Run `adb disconnect` once you're done.
|
||||||
|
|
||||||
Since Android 11, a [Wireless debugging option][adb-wireless] allows to bypass
|
Since Android 11, a [wireless debugging option][adb-wireless] allows to bypass
|
||||||
having to physically connect your device directly to your computer.
|
having to physically connect your device directly to your computer.
|
||||||
|
|
||||||
[adb-wireless]: https://developer.android.com/studio/command-line/adb#connect-to-a-device-over-wi-fi-android-11+
|
[adb-wireless]: https://developer.android.com/studio/command-line/adb#wireless-android11-command-line
|
||||||
|
|
||||||
|
|
||||||
## Autostart
|
## Autostart
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ _See [build.md](build.md) to build and install the app manually._
|
|||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
|
_Make sure that your device meets the [prerequisites](/README.md#prerequisites)._
|
||||||
|
|
||||||
Once installed, run from a terminal:
|
Once installed, run from a terminal:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ _See [build.md](build.md) to build and install the app manually._
|
|||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
|
_Make sure that your device meets the [prerequisites](/README.md#prerequisites)._
|
||||||
|
|
||||||
Once installed, run from a terminal:
|
Once installed, run from a terminal:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
|
|||||||
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
|
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
|
||||||
| Pinch-to-zoom | <kbd>Ctrl</kbd>+_click-and-move_
|
| Pinch-to-zoom | <kbd>Ctrl</kbd>+_click-and-move_
|
||||||
| Drag & drop APK file | Install APK from computer
|
| Drag & drop APK file | Install APK from computer
|
||||||
| Drag & drop non-APK file | [Push file to device](#push-file-to-device)
|
| Drag & drop non-APK file | [Push file to device](control.md#push-file-to-device)
|
||||||
|
|
||||||
_¹Double-click on black borders to remove them._
|
_¹Double-click on black borders to remove them._
|
||||||
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
||||||
|
|||||||
@@ -4,11 +4,14 @@
|
|||||||
|
|
||||||
Download the [latest release]:
|
Download the [latest release]:
|
||||||
|
|
||||||
- [`scrcpy-win64-v1.25.zip`][direct-win64]
|
- [`scrcpy-win64-v2.0.zip`][direct-win64] (64-bit)
|
||||||
<sub>SHA-256: `db65125e9c65acd00359efb7cea9c05f63cc7ccd5833000cd243cc92f5053028`</sub>
|
<sub>SHA-256: `ae4c8d37a496b43f8974ba8f07f708e22a9570ba0cddc3dc3a36edbccd4d2a20`</sub>
|
||||||
|
- [`scrcpy-win32-v2.0.zip`][direct-win32] (32-bit)
|
||||||
|
<sub>SHA-256: `15d98c02cb0e0bbd84f8b5d54991e0f6925569b1286a86a40743944fcb1c2d8c`</sub>
|
||||||
|
|
||||||
[release]: https://github.com/Genymobile/scrcpy/releases/latest
|
[latest release]: https://github.com/Genymobile/scrcpy/releases/latest
|
||||||
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.25/scrcpy-win64-v1.25.zip
|
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v2.0/scrcpy-win64-v2.0.zip
|
||||||
|
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v2.0/scrcpy-win32-v2.0.zip
|
||||||
|
|
||||||
and extract it.
|
and extract it.
|
||||||
|
|
||||||
@@ -35,6 +38,8 @@ _See [build.md](build.md) to build and install the app manually._
|
|||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
|
_Make sure that your device meets the [prerequisites](/README.md#prerequisites)._
|
||||||
|
|
||||||
Scrcpy is a command line application: it is mainly intended to be executed from
|
Scrcpy is a command line application: it is mainly intended to be executed from
|
||||||
a terminal with command line arguments.
|
a terminal with command line arguments.
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
BUILDDIR=build-auto
|
BUILDDIR=build-auto
|
||||||
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v1.25/scrcpy-server-v1.25
|
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v2.0/scrcpy-server-v2.0
|
||||||
PREBUILT_SERVER_SHA256=ce0306c7bbd06ae72f6d06f7ec0ee33774995a65de71e0a83813ecb67aec9bdb
|
PREBUILT_SERVER_SHA256=9e241615f578cd690bb43311000debdecf6a9c50a7082b001952f18f6f21ddc2
|
||||||
|
|
||||||
echo "[scrcpy] Downloading prebuilt server..."
|
echo "[scrcpy] Downloading prebuilt server..."
|
||||||
wget "$PREBUILT_SERVER_URL" -O scrcpy-server
|
wget "$PREBUILT_SERVER_URL" -O scrcpy-server
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
project('scrcpy', 'c',
|
project('scrcpy', 'c',
|
||||||
version: '1.25',
|
version: '2.0',
|
||||||
meson_version: '>= 0.48',
|
meson_version: '>= 0.48',
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=c11',
|
'c_std=c11',
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ android {
|
|||||||
applicationId "com.genymobile.scrcpy"
|
applicationId "com.genymobile.scrcpy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 33
|
targetSdkVersion 33
|
||||||
versionCode 12500
|
versionCode 20000
|
||||||
versionName "1.25"
|
versionName "2.0"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
SCRCPY_DEBUG=false
|
SCRCPY_DEBUG=false
|
||||||
SCRCPY_VERSION_NAME=1.25
|
SCRCPY_VERSION_NAME=2.0
|
||||||
|
|
||||||
PLATFORM=${ANDROID_PLATFORM:-33}
|
PLATFORM=${ANDROID_PLATFORM:-33}
|
||||||
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-33.0.0}
|
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-33.0.0}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public final class AudioCapture {
|
|||||||
@TargetApi(Build.VERSION_CODES.N)
|
@TargetApi(Build.VERSION_CODES.N)
|
||||||
public int read(ByteBuffer directBuffer, int size, MediaCodec.BufferInfo outBufferInfo) {
|
public int read(ByteBuffer directBuffer, int size, MediaCodec.BufferInfo outBufferInfo) {
|
||||||
int r = recorder.read(directBuffer, size);
|
int r = recorder.read(directBuffer, size);
|
||||||
if (r < 0) {
|
if (r <= 0) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public final class AudioEncoder implements AsyncProcessor {
|
|||||||
InputTask task = inputTasks.take();
|
InputTask task = inputTasks.take();
|
||||||
ByteBuffer buffer = mediaCodec.getInputBuffer(task.index);
|
ByteBuffer buffer = mediaCodec.getInputBuffer(task.index);
|
||||||
int r = capture.read(buffer, READ_SIZE, bufferInfo);
|
int r = capture.read(buffer, READ_SIZE, bufferInfo);
|
||||||
if (r < 0) {
|
if (r <= 0) {
|
||||||
throw new IOException("Could not read audio: " + r);
|
throw new IOException("Could not read audio: " + r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,15 +81,15 @@ public final class Server {
|
|||||||
// But only apply when strictly necessary, since workarounds can cause other issues:
|
// But only apply when strictly necessary, since workarounds can cause other issues:
|
||||||
// - <https://github.com/Genymobile/scrcpy/issues/940>
|
// - <https://github.com/Genymobile/scrcpy/issues/940>
|
||||||
// - <https://github.com/Genymobile/scrcpy/issues/994>
|
// - <https://github.com/Genymobile/scrcpy/issues/994>
|
||||||
boolean mustFillAppInfo = Build.BRAND.equalsIgnoreCase("meizu");
|
if (Build.BRAND.equalsIgnoreCase("meizu")) {
|
||||||
|
Workarounds.fillAppInfo();
|
||||||
|
}
|
||||||
|
|
||||||
// Before Android 11, audio is not supported.
|
// Before Android 11, audio is not supported.
|
||||||
// Since Android 12, we can properly set a context on the AudioRecord.
|
// Since Android 12, we can properly set a context on the AudioRecord.
|
||||||
// Only on Android 11 we must fill app info for the AudioRecord to work.
|
// Only on Android 11 we must fill the application context for the AudioRecord to work.
|
||||||
mustFillAppInfo |= audio && Build.VERSION.SDK_INT == Build.VERSION_CODES.R;
|
if (audio && Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
|
||||||
|
Workarounds.fillAppContext();
|
||||||
if (mustFillAppInfo) {
|
|
||||||
Workarounds.fillAppInfo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AsyncProcessor> asyncProcessors = new ArrayList<>();
|
List<AsyncProcessor> asyncProcessors = new ArrayList<>();
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ import java.lang.reflect.Constructor;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
public final class Workarounds {
|
public final class Workarounds {
|
||||||
|
|
||||||
|
private static Class<?> activityThreadClass;
|
||||||
|
private static Object activityThread;
|
||||||
|
|
||||||
private Workarounds() {
|
private Workarounds() {
|
||||||
// not instantiable
|
// not instantiable
|
||||||
}
|
}
|
||||||
@@ -28,18 +32,25 @@ public final class Workarounds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
||||||
public static void fillAppInfo() {
|
private static void fillActivityThread() throws Exception {
|
||||||
try {
|
if (activityThread == null) {
|
||||||
// ActivityThread activityThread = new ActivityThread();
|
// ActivityThread activityThread = new ActivityThread();
|
||||||
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
|
activityThreadClass = Class.forName("android.app.ActivityThread");
|
||||||
Constructor<?> activityThreadConstructor = activityThreadClass.getDeclaredConstructor();
|
Constructor<?> activityThreadConstructor = activityThreadClass.getDeclaredConstructor();
|
||||||
activityThreadConstructor.setAccessible(true);
|
activityThreadConstructor.setAccessible(true);
|
||||||
Object activityThread = activityThreadConstructor.newInstance();
|
activityThread = activityThreadConstructor.newInstance();
|
||||||
|
|
||||||
// ActivityThread.sCurrentActivityThread = activityThread;
|
// ActivityThread.sCurrentActivityThread = activityThread;
|
||||||
Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
|
Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
|
||||||
sCurrentActivityThreadField.setAccessible(true);
|
sCurrentActivityThreadField.setAccessible(true);
|
||||||
sCurrentActivityThreadField.set(null, activityThread);
|
sCurrentActivityThreadField.set(null, activityThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
||||||
|
public static void fillAppInfo() {
|
||||||
|
try {
|
||||||
|
fillActivityThread();
|
||||||
|
|
||||||
// ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData();
|
// ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData();
|
||||||
Class<?> appBindDataClass = Class.forName("android.app.ActivityThread$AppBindData");
|
Class<?> appBindDataClass = Class.forName("android.app.ActivityThread$AppBindData");
|
||||||
@@ -59,6 +70,16 @@ public final class Workarounds {
|
|||||||
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
||||||
mBoundApplicationField.setAccessible(true);
|
mBoundApplicationField.setAccessible(true);
|
||||||
mBoundApplicationField.set(activityThread, appBindData);
|
mBoundApplicationField.set(activityThread, appBindData);
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
// this is a workaround, so failing is not an error
|
||||||
|
Ln.d("Could not fill app info: " + throwable.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
||||||
|
public static void fillAppContext() {
|
||||||
|
try {
|
||||||
|
fillActivityThread();
|
||||||
|
|
||||||
Application app = Application.class.newInstance();
|
Application app = Application.class.newInstance();
|
||||||
Field baseField = ContextWrapper.class.getDeclaredField("mBase");
|
Field baseField = ContextWrapper.class.getDeclaredField("mBase");
|
||||||
@@ -71,7 +92,7 @@ public final class Workarounds {
|
|||||||
mInitialApplicationField.set(activityThread, app);
|
mInitialApplicationField.set(activityThread, app);
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
// this is a workaround, so failing is not an error
|
// this is a workaround, so failing is not an error
|
||||||
Ln.d("Could not fill app info: " + throwable.getMessage());
|
Ln.d("Could not fill app context: " + throwable.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user