Skip to content

Instantly share code, notes, and snippets.

@RikkaW
Last active October 2, 2024 14:10
Show Gist options
  • Save RikkaW/be3fe4178903702c54ec73b2fc1187fe to your computer and use it in GitHub Desktop.
Save RikkaW/be3fe4178903702c54ec73b2fc1187fe to your computer and use it in GitHub Desktop.
show window in app_process (impossible to work from Android 14 QPR 3 or Android 15, as addView requires the calling process an Android app process)
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);
}
@twaik
Copy link

twaik commented Mar 26, 2024

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?

@RikkaW
Copy link
Author

RikkaW commented Mar 26, 2024

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.

@twaik
Copy link

twaik commented Mar 26, 2024

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?

@JacobTDC
Copy link

JacobTDC commented Sep 30, 2024

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();

@huynhtanloc2612
Copy link

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.

@JacobTDC
Copy link

JacobTDC commented Oct 1, 2024

@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.

@huynhtanloc2612
Copy link

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"

@D-R-99
Copy link

D-R-99 commented Oct 2, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment