0%

Android外部imgui绘制--JNIRoot

前几天听说了个 jniroot, 貌似很厉害的样子
https://github.com/topjohnwu/libsu

想着能不能适配 imgui.
干就完事了.

改进后的 libsu 模板在这里
https://github.com/PShocker/libsutemplate

1. 新建 Native C++ 项目,把 libsu 的依赖全部加进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
android {
compileOptions {
// This library uses Java 8 features, this is required
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
def libsuVersion = '4.0.3'

// The core module is used by all other components
implementation "com.github.topjohnwu.libsu:core:${libsuVersion}"

// Optional: APIs for creating root services
implementation "com.github.topjohnwu.libsu:service:${libsuVersion}"

// Optional: For com.topjohnwu.superuser.io classes
implementation "com.github.topjohnwu.libsu:io:${libsuVersion}"

// Optional: Bundle prebuilt BusyBox binaries
implementation "com.github.topjohnwu.libsu:busybox:${libsuVersion}"
}

若报错,把 maven { url ‘https://jitpack.io’ } 添加到 settings.gradle 里

1
2
3
4
5
6
7
8
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}

2. 新建 aidl 文件,Rebuild 生成 Stub 文件

1
2
3
4
5
6
7
8
9
interface IImGuiService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void init_ImGui(inout Surface surface);
void update_ImGui(int width, int height);
void set_ImGui_Orientation(int orientation);
}

新建 AIDLService 类,继承 RootService.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class AIDLService extends RootService {

static {
// Only load the library when this class is loaded in a root process.
// The classloader will load this class (and call this static block) in the non-root
// process because we accessed it when constructing the Intent to send.
// Add this check so we don't unnecessarily load native code that'll never be used.
if (Process.myUid() == 0)
System.loadLibrary("imgui");
}

public native void native_init_ImGui(Surface surface); //imgui初始化,读取输入,渲染.
public native void native_update_ImGui(int width, int height);
public native void native_set_ImGui_Orientation(int orientation); //设置屏幕方向


class ImGui extends IImGuiService.Stub {

@Override
public void init_ImGui(Surface surface) throws RemoteException {
native_init_ImGui(surface);
}

@Override
public void update_ImGui(int width, int height) throws RemoteException {
native_update_ImGui(width,height);
}


@Override
public void set_ImGui_Orientation(int orientation) throws RemoteException {
native_set_ImGui_Orientation(orientation);
}
}

@Override
public IBinder onBind(@NonNull Intent intent) {
return new ImGui();
}
}

3. 新建 ImGuiView 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class ImGuiView extends SurfaceView implements SurfaceHolder.Callback{
public ImGuiView(Context context) {
super(context);
setZOrderOnTop(true);
SurfaceHolder holder = getHolder();
holder.setFormat(PixelFormat.TRANSLUCENT);
getHolder().addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
requestFocusFromTouch();
}

@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
Log.d("Shocker","surfaceCreated"); //这里需要新线程在native里完成初始化,读取输入和渲染
new Thread(new Runnable() {
@Override
public void run() {
try {
MainActivity.ImGui.init_ImGui(surfaceHolder.getSurface());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}).start();
}

@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
....... //处理屏幕旋转事件
}

@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {

}
}

4. 新建 AIDLConnection 类,在 MainActivity 的 OnCreate 里绑定 RootService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static IImGuiService ImGui;
class AIDLConnection implements ServiceConnection {

private final boolean isDaemon;

AIDLConnection(boolean b) {
isDaemon = b;
}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ImGui= IImGuiService.Stub.asInterface(service);
Log.d("Shocker","Connected");
}

@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d("Shocker","Disconnected");

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protected void addFullView() {
ImGuiView imGuiView=new ImGuiView(this);
params = new WindowManager.LayoutParams(
getLayoutType(),
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_FULLSCREEN,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.LEFT | Gravity.TOP; // 调整悬浮窗显示的停靠位置为左侧置顶

params.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE|View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;

// 以屏幕左上角为原点,设置x、y初始值(10,10),相对于gravity
params.x = 0;
params.y = 0;
// 设置悬浮窗口长宽数据
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
if (Build.VERSION.SDK_INT >= 28) {
params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES ;
}
windowManager.addView(imGuiView, params);
}
1
2
3
4
5
6
7
8
9
10
11
@Override
protected void onCreate(Bundle savedInstanceState) {
..............
RootService.bind(new Intent(this, AIDLService.class), new AIDLConnection(false));
t.setOnClickListener(view -> {
if (!start){
addFullView(); //添加悬浮窗
start=true;
}
});
}

5.native 完成 imgui 初始化,循环读取输入和渲染

1
2
3
4
5
6
7
8
9
10
Java_com_shocker_imgui_AIDLService_native_1init_1ImGui(JNIEnv *env, jobject thiz, jobject surface) {
ANativeWindow* w = ANativeWindow_fromSurface(env, (jobject)surface);
init(w); //初始化imgui
ANativeWindow_release(w);

while (true){
handleInputEvent();
tick();
}
}