-
-
Save RikkaW/be3fe4178903702c54ec73b2fc1187fe to your computer and use it in GitHub Desktop.
token = new Binder(); | |
try { | |
Context _context = android.app.ActivityThread.systemMain().getSystemContext(); | |
final Context context = new ContextWrapper(_context) { | |
@Override | |
public Object getSystemService(String name) { | |
if (Context.WINDOW_SERVICE.equals(name)) { | |
WindowManager wm = (WindowManager) super.getSystemService(name); | |
if (wm != null) { | |
((android.view.WindowManagerImpl) wm).setDefaultToken(token); | |
} | |
return wm; | |
} | |
return super.getSystemService(name); | |
} | |
}; | |
context.setTheme(android.R.style.Theme_Material_Light); | |
CharSequence label = context.getPackageManager().getApplicationLabel(context.getPackageManager().getApplicationInfo("com.android.settings", 0)); | |
LogUtils.i("label " + label); | |
android.widget.Toast.makeText(context, "test", Toast.LENGTH_LONG).show(); | |
LogUtils.i("toast"); | |
android.view.WindowManager wm = context.getSystemService(WindowManager.class); | |
LogUtils.i("class: " + wm.getClass().getName()); | |
//((android.view.WindowManagerImpl) wm).setDefaultToken(token); | |
LinearLayout root = new LinearLayout(context); | |
root.setBackground(new ColorDrawable(0xffffffff)); | |
TextView textView = new TextView(context); | |
textView.setText("test"); | |
root.addView(textView); | |
Button button = new Button(context); | |
button.setSoundEffectsEnabled(false); | |
button.setText("test"); | |
button.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
Toast.makeText(context, "?!", Toast.LENGTH_LONG).show(); | |
} | |
}); | |
root.addView(button); | |
WindowManager.LayoutParams params = new WindowManager.LayoutParams( | |
WindowManager.LayoutParams.WRAP_CONTENT, | |
WindowManager.LayoutParams.WRAP_CONTENT, | |
WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG, | |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, | |
PixelFormat.TRANSLUCENT); | |
params.gravity = Gravity.CENTER; | |
params.x = 0; | |
params.y = 0; | |
wm.addView(root, params); | |
} catch (Throwable tr) { | |
LogUtils.e(tr.getMessage(), tr); | |
} |
Thanks @RikkaW for your information and advices.
Such knowledge is completely new to me and it is quite interesting. I sometime play with Android API but since I knew about the app_process
magic from scrcpy
tool, I was very impresive. I want to learn more about it and find out what else it can do... but looks there is very few online information for beginers. By following and trying some projects like this, the answers of @twaik and yours help me learn a lot new knowledge about it. Thank you.
If posible, I hope you could point me to where to learn and play with aidl API for beginers.
@RikkaW I have some problems with creating some overlay from process started with app_process
in application sandbox of other app.
I know "window" service is inaccessible from processes started this way, but I am sending IBinder of that service from Activity via AIDL and injecting it to ServiceManager's sCache
manually, so WindowManagerGlobal can access it.
I am trying to create overlay with this code:
public void window(IBinder windowManager) {
try {
Field sCache = Class.forName("android.os.ServiceManager").getDeclaredField("sCache");
sCache.setAccessible(true);
Method m = sCache.getType().getDeclaredMethod("put", Object.class, Object.class);
m.invoke(sCache.get(null), "window", windowManager);
handler.post(() -> {
Context context = ctx;
WindowManager wm = context.getSystemService(WindowManager.class);
Binder token = new Binder();
context = new ContextWrapper(context) {
@Override
public Object getSystemService(String name) {
if (Context.WINDOW_SERVICE.equals(name)) {
WindowManager wm = (WindowManager) super.getSystemService(name);
if (wm != null)
((android.view.WindowManagerImpl) wm).setDefaultToken(token);
return wm;
}
return super.getSystemService(name);
}
};
Button button = new Button(context);
button.setSoundEffectsEnabled(false);
button.setText("test");
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.CENTER;
params.x = 0;
params.y = 0;
wm.addView(button, params);
});
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
But I am getting only this unclear exception
android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@e51bfa1 -- permission denied for window type 2038
at android.view.ViewRootImpl.setView(ViewRootImpl.java:1092)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:409)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:109)
at com.termux.x11.CmdEntryPoint.lambda$windowManager$3(CmdEntryPoint.java:269)
at com.termux.x11.CmdEntryPoint.$r8$lambda$MIV7qHiVNsG1buhNHJnI7A8pV3A(CmdEntryPoint.java:0)
at com.termux.x11.CmdEntryPoint$$ExternalSyntheticLambda1.run(R8$$SyntheticClass:0)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at com.termux.x11.CmdEntryPoint.main(CmdEntryPoint.java:62)
at java.lang.reflect.Method.invoke(Native Method)
at com.termux.x11.Loader.main(Loader.java:26)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:399)
Both activity I am sending IBinder from and application where I want to start this snippet have "Draw over apps" permission but it does not help.
I tried to make a proxy for IBinder to make my snippet inherit Uid of another application (which owns Activity) with adding this code to ContextWrapper
public UserHandle getUser() {
try {
return (UserHandle) UserHandle.class.getDeclaredMethod("of", int.class).invoke(null, Binder.getCallingUid() / 100000);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
public int getUserId() {
return Binder.getCallingUid() / 100000;
}
and wrapping "window"'s IBinder with
Binder proxy = new Binder() {
IBinder target = (IBinder) Class.forName("android.os.ServiceManager").getDeclaredMethod("getService", String.class).invoke(null, "window");
@Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return target.transact(code, data, reply, flags);
}
};
But it does not seem to work, I am getting the same exception.
Seems like I found a reason in system-wide logcat.
03-26 13:39:36.231 536 2972 E AppOps : noteOperation
03-26 13:39:36.231 536 2972 E AppOps : java.lang.SecurityException: Specified package android under uid 10162 but it is really 1000
03-26 13:39:36.231 536 2972 E AppOps : at com.android.server.appop.AppOpsService.verifyAndGetBypass(AppOpsService.java:3874)
03-26 13:39:36.231 536 2972 E AppOps : at com.android.server.appop.AppOpsService.noteOperationUnchecked(AppOpsService.java:3030)
03-26 13:39:36.231 536 2972 E AppOps : at com.android.server.appop.AppOpsService.noteOperationImpl(AppOpsService.java:3018)
03-26 13:39:36.231 536 2972 E AppOps : at com.android.server.appop.AppOpsService.noteOperation(AppOpsService.java:3001)
03-26 13:39:36.231 536 2972 E AppOps : at android.app.AppOpsManager.noteOpNoThrow(AppOpsManager.java:7382)
03-26 13:39:36.231 536 2972 E AppOps : at com.android.server.policy.PhoneWindowManager.checkAddPermission(PhoneWindowManager.java:2150)
03-26 13:39:36.231 536 2972 E AppOps : at com.android.server.wm.WindowManagerService.addWindow(WindowManagerService.java:1383)
03-26 13:39:36.231 536 2972 E AppOps : at com.android.server.wm.Session.addToDisplayAsUser(Session.java:176)
03-26 13:39:36.231 536 2972 E AppOps : at android.view.IWindowSession$Stub.onTransact(IWindowSession.java:679)
03-26 13:39:36.231 536 2972 E AppOps : at com.android.server.wm.Session.onTransact(Session.java:139)
03-26 13:39:36.231 536 2972 E AppOps : at android.os.Binder.execTransactInternal(Binder.java:1154)
03-26 13:39:36.231 536 2972 E AppOps : at android.os.Binder.execTransact(Binder.java:1123)
Ok, I figured it out.
public void windowManager(IBinder binder) {
try {
Field sCache = Class.forName("android.os.ServiceManager").getDeclaredField("sCache");
sCache.setAccessible(true);
Method m = sCache.getType().getDeclaredMethod("put", Object.class, Object.class);
m.invoke(sCache.get(null), "window", binder);
handler.post(() -> {
Context context = android.app.ActivityThread.systemMain().getSystemContext();
WindowManager wm = context.getSystemService(WindowManager.class);
Binder token = new Binder();
context = new ContextWrapper(context) {
@Override
public Object getSystemService(String name) {
if (Context.WINDOW_SERVICE.equals(name)) {
WindowManager wm = (WindowManager) super.getSystemService(name);
if (wm != null)
((android.view.WindowManagerImpl) wm).setDefaultToken(token);
return wm;
}
return super.getSystemService(name);
}
@Override
public Looper getMainLooper() {
return Looper.myLooper();
}
};
View view = new View(context) {
final Paint paint = new Paint();
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
}
};
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
100, 100, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.OPAQUE);
params.gravity = Gravity.CENTER;
params.x = 0;
params.y = 0;
params.packageName = "com.termux";
wm.addView(view, params);
});
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
And it works on Android 11.
Thanks @RikkaW.
And unfortunately I am getting this on Android 12.
java.lang.NullPointerException
at android.os.Parcel.createExceptionOrNull(Parcel.java:2431)
at android.os.Parcel.createException(Parcel.java:2409)
at android.os.Parcel.readException(Parcel.java:2392)
at android.os.Parcel.readException(Parcel.java:2334)
at com.android.internal.app.IAppOpsService$Stub$Proxy.reportRuntimeAppOpAccessMessageAndGetConfig(IAppOpsService.java:1666)
at android.app.AppOpsManager$1.reportStackTraceIfNeeded(AppOpsManager.java:290)
at android.app.AppOpsManager$1.onNoted(AppOpsManager.java:265)
at android.app.AppOpsManager.readAndLogNotedAppops(AppOpsManager.java:9359)
at android.os.Parcel.readExceptionCode(Parcel.java:2356)
at android.os.Parcel.readException(Parcel.java:2331)
at android.view.IWindowSession$Stub$Proxy.addToDisplayAsUser(IWindowSession.java:1374)
at android.view.ViewRootImpl.setView(ViewRootImpl.java:1119)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:399)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:133)
at com.termux.x11.CmdEntryPoint.lambda$windowManager$3(CmdEntryPoint.java:286)
at com.termux.x11.CmdEntryPoint.$r8$lambda$MIV7qHiVNsG1buhNHJnI7A8pV3A(CmdEntryPoint.java:0)
at com.termux.x11.CmdEntryPoint$$ExternalSyntheticLambda1.run(R8$$SyntheticClass:0)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at com.termux.x11.CmdEntryPoint.main(CmdEntryPoint.java:71)
at java.lang.reflect.Method.invoke(Native Method)
at com.termux.x11.Loader.main(Loader.java:26)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:355)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.internal.util.AnnotationValidations.validate(AnnotationValidations.java:129)
at android.app.SyncNotedAppOp.<init>(SyncNotedAppOp.java:227)
at android.app.SyncNotedAppOp$1.createFromParcel(SyncNotedAppOp.java:243)
at android.app.SyncNotedAppOp$1.createFromParcel(SyncNotedAppOp.java:235)
at com.android.internal.app.IAppOpsService$Stub.onTransact(IAppOpsService.java:728)
Any suggestions?
Change opPackageName? I don't remember:(
Btw, this will never work starting from Android 14 QPR 3 or Android 15, as addView now checks if the calling process is a valid Android app process.
Maybe there is some workaround for this? The most important thing I need is to draw something as an overlay from background process.
I am not writing viruses. Simply some oems put code to limit GPU performance of background apps.
Maybe SurfaceControl.Transactions?
From @huynhtanloc2612:
Hi @twaik You was correct. After I made subclass with a separated file MyContext.class, above error disappeared.
However, after code run, it has new below error. Do you have any advice?
java.lang.SecurityException: Given calling package android does not match caller's uid 2000 at ........
Late reply, I know, but the solution to this issue is to wrap your context to override getOpPackageName()
, like so:
Context context = new ContextWrapper(_context) {
/* @Override getSystemService() here if you want, not necessary for Toasts */
@Override
public String getOpPackageName() {
return "com.android.shell"; // aka ADB shell package name
}
};
I successfully managed to get a toast to show on Android 14 with the following:
Context _context = ActivityThread.systemMain().getSystemContext();
Context context = new ContextWrapper(_context) {
@Override
public String getOpPackageName() {
// I actually used "com.termux" here, because I was playing around with
// this in Termux, but, no matter.
return "com.android.shell"; // aka ADB shell package name
}
};
Toast.makeText(context, "test", Toast.LENGTH_LONG).show();
Thanks @JacobTDC for your comment
I've tried your suggestion
public class ShowToast {
public static void main(String[] args) {
Binder token = new Binder();
Looper.prepareMainLooper();
try{
Context _context = ActivityThread.systemMain().getSystemContext();
Context context = new ContextWrapper(_context) {
@Override
public String getOpPackageName() {
return "com.android.shell"; // aka ADB shell package name
}
};
Toast.makeText(context, "test", Toast.LENGTH_LONG).show();
} catch (Throwable tr) {
tr.printStackTrace(System.err);
}
}
}
but I still got the below error on Galaxy phone with Android 14
$ adb shell app_process -cp /data/local/tmp/toast.dex / com.htl.toast14.ShowToast
java.lang.SecurityException: Given calling package android does not match caller's uid 2000
at android.os.Parcel.createExceptionOrNull(Parcel.java:3069)
at android.os.Parcel.createException(Parcel.java:3053)
at android.os.Parcel.readException(Parcel.java:3036)
at android.os.Parcel.readException(Parcel.java:2978)
at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:6393)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:8160)
at android.app.ContextImpl$ApplicationContentResolver.acquireProvider(ContextImpl.java:3838)
at android.content.ContentResolver.acquireProvider(ContentResolver.java:2539)
at android.provider.Settings$ContentProviderHolder.getProvider(Settings.java:3328)
at android.provider.Settings$NameValueCache.getStringForUser(Settings.java:3595)
at android.provider.Settings$System.getStringForUser(Settings.java:4362)
at android.provider.Settings$System.getIntForUser(Settings.java:4493)
at android.provider.Settings$System.getInt(Settings.java:4465)
at android.widget.Toast.checkGameHomeAllowList(Toast.java:1108)
at android.widget.Toast.show(Toast.java:313)
at com.htl.toast14.ShowToast.main(ShowToast.java:28)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:395)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.am.ContentProviderHelper.getContentProvider(ContentProviderHelper.java:148)
at com.android.server.am.ActivityManagerService.getContentProvider(ActivityManagerService.java:8700)
at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:3091)
at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3202)
at android.os.Binder.execTransactInternal(Binder.java:1375)
Do you have any idea?
And can you show your full code?
Thanks.
@huynhtanloc2612, the following works for me, I tested it in ADB as well.
$ adb shell app_process -cp /data/local/tmp/toast.dex /system/bin/ { class/package path }.ShowToast
public class ShowToast {
public static final void main(String[] args) {
Looper.prepare();
ActivityThread activityThread = ActivityThread.systemMain();
Context _context = activityThread.getSystemContext();
Context context = new ContextWrapper(_context) {
@Override
public String getOpPackageName() {
return "com.android.shell";
}
};
// Maybe create a `new Handler(Looper.myLooper())` and wrap this
// in `handler.post(() -> { ... }); Looper.loop();` if it doesn't work?
// If you do, don't forget to add `Looper.myLooper().quit[Safely]()`
// inside the `handler.post(() -> { ... });`.
Toast.makeText(context, "test", Toast.LENGTH_LONG).show();
}
}
If that doesn't work, maybe try something like this for creating and wrapping the context:
static Context createPackageContext(String packageName) {
Context sysCtx = ActivityThread.systemMain().getSystemContext();
Context _context;
try {
_context = (Context) Context.class
.getMethod("createPackageContextAsUser", new Class[] {
String.class,
int.class,
UserHandle.class })
.invoke(sysCtx, packageName, 0, Process.myUserHandle());
} catch (Exception exc) {
throw new RuntimeException(exc);
}
return new ContextWrapper(_context) {
@Override
public String getOpPackageName() {
return packageName;
}
};
}
What device are you using? I see you're using a Galaxy. That could make a difference, but I'm not sure. I'm using a Google Pixel 8 Pro.
Thanks @JacobTDC ,
Try your above suggestions but the same error happens when it executes Toast.makeText(...).show()
java.lang.SecurityException: Given calling package android does not match caller's uid 2000
I think it requires Android app process as the note of @RikkaW "addView requires the calling process an Android app process"
Toast is a window internally; you can show a window which looks like Toast instead. Windows type can be used by app_process is so limited, most of the window types, including
TYPE_TOAST
, do not work. So, it's not worth trying anymore.On Android 12+, toast is shown through SystemUI rather than the app itself. The API is
INotificationManager#enqueueTextToast
, I haven't tried if app_process can use.Btw, if you want to play app_process magic (and other magic), you MUST learn how things work internally.