Unable to add window android.view.ViewRoot$W@44da9

2019-01-01 10:57发布

问题:

I have prefer this post for example but I got the error at adding viewgroup into the windowmanager object, I have used the same class for the Service as posted into the question with no change where I can mistake I didn\'t gettting it

WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(mView, params); // here

when I add view to the WindowManger

here is my manifest file

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"
      package=\"com.searce.testoverlay\"
      android:versionCode=\"1\"
      android:versionName=\"1.0\">
    <uses-sdk android:minSdkVersion=\"7\" />

    <application android:icon=\"@drawable/icon\" android:label=\"@string/app_name\">
        <activity android:name=\"TestOverlayActivity\"
                      android:label=\"@string/app_name\">
                <intent-filter>
                    <action android:name=\"android.intent.action.MAIN\" />
                    <category android:name=\"android.intent.category.LAUNCHER\" />
                </intent-filter>
            </activity>
        <service android:enabled=\"true\" android:name=\".HUD\"></service>
    </application>
</manifest>

error

09-27 18:49:23.561: ERROR/AndroidRuntime(653): Uncaught handler: thread main exiting due to uncaught exception
09-27 18:49:23.571: ERROR/AndroidRuntime(653): java.lang.RuntimeException: Unable to create service com.searce.testoverlay.HUD: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRoot$W@44da9bc0 -- permission denied for this window type
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.app.ActivityThread.handleCreateService(ActivityThread.java:2790)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.app.ActivityThread.access$3200(ActivityThread.java:119)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1917)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.os.Handler.dispatchMessage(Handler.java:99)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.os.Looper.loop(Looper.java:123)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.app.ActivityThread.main(ActivityThread.java:4363)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at java.lang.reflect.Method.invokeNative(Native Method)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at java.lang.reflect.Method.invoke(Method.java:521)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at dalvik.system.NativeStart.main(Native Method)
09-27 18:49:23.571: ERROR/AndroidRuntime(653): Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRoot$W@44da9bc0 -- permission denied for this window type
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.view.ViewRoot.setView(ViewRoot.java:492)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at com.searce.testoverlay.HUD.onCreate(HUD.java:41)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     at android.app.ActivityThread.handleCreateService(ActivityThread.java:2780)
09-27 18:49:23.571: ERROR/AndroidRuntime(653):     ... 10 more

回答1:

Try using this permission in AndroidManifest.

android.permission.SYSTEM_ALERT_WINDOW

on API >= 23 see



回答2:

\"@ceph3us do you know how to achieve it for >=M? ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW}...\"

  1. SYSTEM_ALERT_WINDOW PERMISSION on API >= 23 (Draw over other apps etc):

    • no longer appears in App\'s Permissions screen.
    • it doesn\'t even appear in the strangely confusing new \"All permissions\" screen
  2. Calling Activity.requestPermissions() with this permission,

    • will not show any dialog for the user to Allow/Deny.
    • instead, the Activity.onRequestPermissionsResult() callback will be called immediately with a denied flag.

Solution:

If the app targets API level 23 or higher, the app user must explicitly grant this permission to the app through a permission management screen. The app requests the user\'s approval by sending an intent with action ACTION_MANAGE_OVERLAY_PERMISSION. The app can check whether it has this authorization by calling Settings.canDrawOverlays()

example code:

/** code to post/handler request for permission */
public final static int REQUEST_CODE = -1010101; *(see edit II)*

public void checkDrawOverlayPermission() {
    /** check if we already  have permission to draw over other apps */
    if (!Settings.canDrawOverlays(Context)) {
        /** if not construct intent to request permission */
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse(\"package:\" + getPackageName()));
        /** request permission via start activity for result */
        startActivityForResult(intent, REQUEST_CODE);
    }
}

@Override 
protected void onActivityResult(int requestCode, int resultCode,  Intent data) {
    /** check if received result code 
        is equal our requested code for draw permission  */
    if (requestCode == REQUEST_CODE) {
       / ** if so check once again if we have permission */
       if (Settings.canDrawOverlays(this)) {
           // continue here - permission was granted 
       }
    }
}

\"And how can the user disable this permission ? It doesn\'t show in the permissions in settings->apps->\"MyApp\"->permissions. Also...any explanation as to why this permission is different from the others in the way we request for it? – Anonymous Feb 12 at 21:01\"

There are a couple of permissions that don\'t behave like normal and dangerous permissions. SYSTEM_ALERT_WINDOW and WRITE_SETTINGS are particularly sensitive, so most apps should not use them. If an app needs one of these permissions, it must declare the permission in the manifest, and send an intent requesting the user\'s authorization. The system responds to the intent by showing a detailed management screen to the user.

Special Permissions

edit II:

I used this code in an Activity extending FragmentActivity and I got Exception java.lang.IllegalArgumentException: Can only use lower 16 bits for requestCode because request code used is not in the range of 0.. 65535. You might consider changing your request code to an appropriate value. – mtsahakis

as it sais:

request code must be in the range of 0.. 65535.

this is becaus:

  • integer in java is represented by 32 bits
  • you are allowed to use lower 16 bits for requestCode
  • other bits are used in request processing

so for example:

integer value:  5463             ///hi 16 bits //   |    // lo 16 bits //
as binary string will look like: 0000 0000 0000 0000 0001 0101 0101 0111 

simple use code in given range

edit III:

for apps targeting AOSP API 26 (android oreo / 8+ )

Apps that use the SYSTEM_ALERT_WINDOW permission can no longer use the following window types to display alert windows above other apps and system windows:

TYPE_PHONE TYPE_PRIORITY_PHONE TYPE_SYSTEM_ALERT TYPE_SYSTEM_OVERLAY TYPE_SYSTEM_ERROR

Instead, apps must use a new window type called TYPE_APPLICATION_OVERLAY.

TYPE_APPLICATION_OVERLAY

Window type: Application overlay windows are displayed above all activity windows (types between FIRST_APPLICATION_WINDOW and LAST_APPLICATION_WINDOW) but below critical system windows like the status bar or IME.

The system may change the position, size, or visibility of these windows at anytime to reduce visual clutter to the user and also manage resources.

Requires SYSTEM_ALERT_WINDOW permission.

The system will adjust the importance of processes with this window type to reduce the chance of the low-memory-killer killing them. In multi-user systems shows only on the owning user’s screen.

WindowManager.LayoutParams wLp = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
      ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
      : WindowManager.LayoutParams.TYPE_PHONE;

Window.setAttributes(WindowManager.LayoutParams)


回答3:

Following ceph3us answer to add an Alert Dialog, this worked fine

final AlertDialog dialog = dialogBuilder.create();
                final Window dialogWindow = dialog.getWindow();
                final WindowManager.LayoutParams dialogWindowAttributes = dialogWindow.getAttributes();

                // Set fixed width (280dp) and WRAP_CONTENT height
                final WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
                lp.copyFrom(dialogWindowAttributes);
                lp.width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 280, getResources().getDisplayMetrics());
                lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
                dialogWindow.setAttributes(lp);

                // Set to TYPE_SYSTEM_ALERT so that the Service can display it
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    dialogWindow.setType(WindowManager.LayoutParams.TYPE_TOAST);
                }
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    dialogWindow.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
                }
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
                {
                    dialogWindow.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                }
                dialog.show();

But using TYPE_SYSTEM_ALERT might trigger Google takedown policy of apps using dangerous permissions. Make sure you have a valid justification in case google requires it.



回答4:

You can change your target SDK to 22 or less, then it also works on API 23.

Change it in Gradle.Build.