Исключение WindowManagerBadTokenException для наложения AccessibilityService

#android #xamarin #xamarin.android

#Android #xamarin #xamarin.android

Вопрос:

Я создаю службу специальных возможностей с использованием Xamarin и Visual Studio, следуя этой кодовой таблице (Java Android SDK): https://codelabs.developers.google.com/codelabs/developing-android-a11y-service/#0

Служба специальных возможностей запущена и запущена, и она находится в разделе «Настройки»> «Специальные возможности» на моем устройстве Android.

Обслуживание:

     [Service(Label = "Input Utility", Permission = Manifest.Permission.BindAccessibilityService)]
    [IntentFilter(new string[] { "android.accessibilityservice.AccessibilityService" })]
    [MetaData("android.accessibilityservice", Resource = "@xml/config")]
    public class TapService : AccessibilityService
    {
        FrameLayout mLayout;
        public override void OnAccessibilityEvent(AccessibilityEvent e)
        {
            
        }

        public override void OnInterrupt()
        {
            
        }

        public override bool OnUnbind(Intent intent)
        {
            return base.OnUnbind(intent);
        }

        protected override void OnServiceConnected()
        {
            
            base.OnServiceConnected();

            IWindowManager wm = Application.Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
            mLayout = new FrameLayout(this);
            WindowManagerLayoutParams lp = new WindowManagerLayoutParams();
            lp.Type = WindowManagerTypes.AccessibilityOverlay;
            lp.Format = Format.Translucent;
            lp.Flags |= WindowManagerFlags.NotFocusable;
            lp.Width = WindowManagerLayoutParams.WrapContent;
            lp.Height = WindowManagerLayoutParams.WrapContent;
            lp.Gravity = GravityFlags.Top;
            LayoutInflater inflater = (LayoutInflater)Application.Context.GetSystemService(Context.LayoutInflaterService);
            inflater.Inflate(Resource.Layout.InputBar, mLayout);
            wm.AddView(mLayout, lp);

        }
    }
 

Файл макета (InputBar.xml ):

 <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <Button
        android:id="@ id/power"
        android:text="Power"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@ id/volume_up"
        android:text="Volume"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@ id/scroll"
        android:text="Scroll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@ id/swipe"
        android:text="Swipe"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>


</LinearLayout>

 

Файл метаданных (config.xml ):

 <?xml version="1.0" encoding="utf-8" ?> 
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
   android:settingsActivity="true"
   android:accessibilityFeedbackType="feedbackGeneric"
   android:accessibilityFlags="flagDefault"
   android:canPerformGestures="true"
   android:canRetrieveWindowContent="true" />
 

Однако я получаю сообщение об ошибке, когда пытаюсь добавить наложение на экран wm.AddView(mLayout, lp); , оно выдает мне эту ошибку: Android.Views.WindowManagerBadTokenException Message=Unable to add window -- token null is not valid; is your activity running? .

Я понимаю, что в официальной codelab он говорит мне о запуске следующим образом: введите описание изображения здесь

Я подозреваю, что это может быть источником ошибки, которую я получаю, однако я не на Android studio, я использую Visual Studio и не знаю, что с этим делать.

Чтобы воспроизвести мою ошибку, просто клонируйте мой репозиторий github, запустите его, затем, пока он запущен, перейдите в Настройки> Доступность вашего эмулятора Android и включите службу специальных возможностей «Служба ввода».

Репозиторий Github: https://github.com/tatapuchi/XamarinAccessibilityService

Комментарии:

1. Параметры запуска — это настройка активности запуска. Чтобы устранить эту проблему, попробуйте получить LayoutInflater из системной службы. Проверьте код: IWindowManager wm = Application.Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>(); ... //LayoutInflater inflater = LayoutInflater.From(this); LayoutInflater inflater = (LayoutInflater)Application.Context.GetSystemService(Context.LayoutInflaterService); inflater.Inflate(Resource.Layout.InputBar, mLayout); wm.AddView(mLayout, lp);

2. @JarvanZhang-MSFT Я пытался использовать этот код, но я все еще получаю одно и то же сообщение об ошибке, должен ли я изменить действие при запуске или что-то в этом роде? Я не знаю, как это сделать (установка MainActivity MainLauncher в false приводит к тому, что приложение не работает)

Ответ №1:

Чтобы протестировать эту функцию, пожалуйста, убедитесь Settings.CanDrawOverlays , что она включена. Попробуйте определить статус перед запуском службы.

 using Android.Provider;
...

if (!Settings.CanDrawOverlays(this))
{
    StartActivityForResult(new Intent(Settings.ActionManageOverlayPermission, Android.Net.Uri.Parse("package:"   PackageName)), 0);
}
else
{
    StartService(new Intent(this, typeof(DemoService)));
}

...
public override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);

    if (requestCode == 0)
    {
        if (!Settings.CanDrawOverlays(this))
        {
        }
        else
        {
            StartService(new Intent(this, typeof(DemoService)));
        }
    }
}
 

Обновить:

Вот соответствующий код об образце:

 public class MainActivity : AppCompatActivity
{
    public AccessibilityServiceBinder binder;
    public AccessibilityServiceConnection accessibilityServiceConnection;
    public bool isBound = false;
    bool isBoundAC = false;
    Intent ServiceIntent;

    protected override void OnStart()
    {
        base.OnStart();
        if (!isBoundAC)
        {
            accessibilityServiceConnection = new AccessibilityServiceConnection(this);
            isBoundAC = BindService(ServiceIntent, accessibilityServiceConnection, Bind.AutoCreate);
            if (isBoundAC)
            {
                Toast.MakeText(this, "AccessibilityService is Bound", ToastLength.Long);
            }
        }
    }

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        SetContentView(Resource.Layout.activity_main);

        ServiceIntent = new Intent(this, typeof(TapService));
        ServiceIntent.SetPackage("com.companyname.app19_3_1");

        FindViewById<Button>(Resource.Id.btn).Click  = async delegate
        {
            if (!Settings.CanDrawOverlays(this))
            {
                StartActivityForResult(new Intent(Settings.ActionManageOverlayPermission, Android.Net.Uri.Parse("package:"   PackageName)), 0);
            }
        };
    }
}

public class AccessibilityServiceBinder : Binder
{
    TapService service;
    public AccessibilityServiceBinder(TapService service)
    {
        this.service = service;
    }
    public TapService GetAccessibilityService()
    {
        return service;
    }
}

public class AccessibilityServiceConnection : Java.Lang.Object, IServiceConnection
{
    MainActivity activity;
    AccessibilityServiceBinder binder;
    public AccessibilityServiceBinder Binder
    {
        get
        {
            return (AccessibilityServiceBinder)binder;
        }
    }
    public AccessibilityServiceConnection(MainActivity activity)
    {
        this.activity = activity;
    }
    public void OnServiceConnected(ComponentName name, IBinder service)
    {
        var accessibilityServiceBinder = service as AccessibilityServiceBinder;
        if (accessibilityServiceBinder != null)
        {
            activity.binder = accessibilityServiceBinder;
            activity.isBound = true;
            this.binder = accessibilityServiceBinder;
        }
    }
    public void OnServiceDisconnected(ComponentName name)
    {
        activity.isBound = false;
    }
}


[Service(Label = "Input Utility", Permission = Manifest.Permission.BindAccessibilityService, Enabled = true)]
[IntentFilter(new string[] { "android.accessibilityservice.AccessibilityService" })]
[MetaData("android.accessibilityservice", Resource = "@xml/config")]
public class TapService : AccessibilityService
{
    FrameLayout mLayout;
    ...
    protected override void OnServiceConnected()
    {
        base.OnServiceConnected();

        IWindowManager wm = GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
        WindowManagerLayoutParams lp = new WindowManagerLayoutParams();
        lp.Type = WindowManagerTypes.AccessibilityOverlay;
        lp.Format = Format.Translucent;
        lp.Flags |= WindowManagerFlags.NotFocusable;
        lp.Width = WindowManagerLayoutParams.WrapContent;
        lp.Height = WindowManagerLayoutParams.WrapContent;
        lp.Gravity = GravityFlags.Top;
        LayoutInflater inflater = (LayoutInflater)GetSystemService(Context.LayoutInflaterService);
        View view = inflater.Inflate(Resource.Layout.layoutfloat, null);
        wm.AddView(view, lp);
    }
}
 

Комментарии:

1. Я не уверен, что здесь есть «Настройки», я не могу найти для этого правильную директиву using, также я явно не запускаю службу таким образом, я думаю, что это делается полностью с помощью атрибутов, потому что класс TapService и мой файл метаданных — это все, что есть. Я даже ничего не добавил к своей MainActivity 🙁 . Я также не могу найти директиву using для startActivityForResult :/ .

2. Параметр «Настройки» взят из Android. Библиотека поставщика, добавьте using Android.Provider; пространство имен для использования метода. И я обновил код, пожалуйста, проверьте это.

3. Хорошо, спасибо за разъяснение. Я последовал инструкциям, добавив директиву using и поместил первый бит в мой onCreate() непосредственно перед вызовом LoadApplication(); . Я также добавил второй бит в свой onRequestPermissionsResult(); теперь, когда я запускаю, мне предлагается перейти на системную страницу «Отображение поверх других приложений». Однако эта страница продолжает загружаться бесконечно и ничего не делает, когда я перехожу в Настройки> Специальные возможности, чтобы включить свою службу, я получаю ту же ошибку. Я что-то делаю не так?

4. Я тестировал функцию в обычном сервисе, поэтому вместо этого я использую тип ApplicationOverlay. Попробуйте использовать этот тип для тестирования в вашем проекте.

5. Спасибо, что поделились соответствующим кодом, я тестирую WindowManger в AccessibilityService и обновлю его позже.