5

I have developed an Android application which is based on an accessibility service. But I have a problem and I can't find any help on the Internet. When I install the application on any device, evidently it doesn't start to work until:

  1. I go to accessibility settings
  2. find its accessibility service
  3. enter into it and push the switch to turn the service on

The problem is a bit complex: After I have enabled the accessibility service, I can see in accessibility settings screen that the service says "enabled". In fact the application is working. But then if I enter into the service, there is a switch on the top and right that displays as OFF. Why? It has no sense that the service is enabled and working and the switch displays as OFF.

I have tried with another services as Talkback that comes built-in. When I turn ON the switch, go back to the accessibility settings screen and then enter again into the Talkback service, the switch keeps ON. Why it doesn't work properly with my service?

Again, I explain that, although the activation switch doesn't work properly for my service, my application indeed works, the only problem is that switch, which confuses the user showing OFF when my service is already ON.

Is there any reason for which this could be happening? Perhaps I missed something like telling the system when the service gets enabled or something like that?

My code of the service has nothing unusual, is similar to any other accessibility service except for my custom operations when an event comes up.

Here is the application's manifest XML file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="bembibre.attractive"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="21" />

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
    <uses-permission android:name="android.permission.READ_CALENDAR" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.READ_CALL_LOG" />
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.READ_SMS" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="bembibre.attractive.activities.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="bembibre.attractive.activities.HelpActivity" >
        </activity>
        <activity android:name="bembibre.attractive.activities.NotesActivity" >
        </activity>

        <!-- BroadCastReceiver's -->
        <receiver
            android:name=".NotificationsWidget"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_provider" />
        </receiver>
        <receiver
            android:name="bembibre.attractive.events.receivers.DateChangedReceiver"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.TIMEZONE_CHANGED" />
                <action android:name="android.intent.action.TIME_SET" />
            </intent-filter>
        </receiver>
        <receiver android:name="bembibre.attractive.events.scheduling.OnAlarmReceiver" />
        <receiver android:name="bembibre.attractive.events.receivers.CalendarChangedReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.PROVIDER_CHANGED" />

                <data android:scheme="content" />
                <data android:host="com.android.calendar" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="bembibre.attractive.events.receivers.MissedCallsChangedReceiver"
            android:enabled="true" >
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </receiver>
        <receiver android:name="bembibre.attractive.events.receivers.SmsReceiver" >
            <intent-filter android:priority="500" >
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>

        <!-- Servicios -->
        <service android:name="bembibre.attractive.ui.WidgetPaintingService" />
        <service android:name="bembibre.attractive.events.wakelocks.DateChangedCpuLockTask" />
        <service android:name="bembibre.attractive.events.wakelocks.UpdateWhatsAppDataCpuLockTask" />
        <service android:name="bembibre.attractive.events.wakelocks.CalendarChangedCpuLockTask" />
        <service android:name="bembibre.attractive.events.wakelocks.MissedCallsChangedCpuLockTask" />
        <service android:name="bembibre.attractive.events.wakelocks.SmsChangedCpuLockTask" />
        <service android:name="bembibre.attractive.events.wakelocks.UpdateAllDataCpuLockTask" />
        <service
            android:name="bembibre.attractive.events.EventsAccessibilityService"
            android:label="@string/accessibility_service_label"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility_service_config" />

            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
        </service>
    </application>
</manifest>

The manifest declares the accessibility service and it refers to the following configuration file:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
/>

Here is the code of the Java Class that implements the accessibility service:

package bembibre.attractive.events;

import java.util.ArrayList;
import java.util.List;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.os.Build;
import android.view.accessibility.AccessibilityEvent;
import android.widget.RemoteViews;
import bembibre.attractive.events.wakelocks.CpuLockTask;
import bembibre.attractive.events.wakelocks.DeleteAllWhatsAppDataCpuLockTask;
import bembibre.attractive.events.wakelocks.MissedCallsChangedCpuLockTask;
import bembibre.attractive.events.wakelocks.SmsChangedCpuLockTask;
import bembibre.attractive.events.wakelocks.UpdateWhatsAppDataCpuLockTask;
import bembibre.attractive.logging.Logger;
import bembibre.attractive.logic.ApplicationPackages;
import bembibre.attractive.logic.whatsapp.WhatsAppNotificationContent;
import bembibre.attractive.logic.whatsapp.WhatsAppNotificationExtractionStrategy;
import bembibre.attractive.utils.AppUtils;
import bembibre.attractive.utils.ArrayUtils;

/**
 * Clase que representa un servicio que debe estar activo todo el tiempo para que el widget de notificaciones funcione
 * correctamente. Este servicio se encarga de capturar distintos eventos que hacen que aparezcan datos en el widget.
 * 
 * @author misines
 * 
 */
public class EventsAccessibilityService extends AccessibilityService {

    private static final List<Integer> OPEN_WINDOW_EVENTS = new ArrayList<Integer>();

    private static final List<WhatsAppNotificationExtractionStrategy> STRATEGIES = new ArrayList<WhatsAppNotificationExtractionStrategy>();
    static {
        /*
         * Añadimos las estrategias para la extracción de contenido de notificaciones de WhatsApp.
         */
        // Aún no dispongo de las estrategias necesarias...
    }

    @SuppressLint("InlinedApi")
    @Override
    public void onServiceConnected() {
        AccessibilityServiceInfo info = new AccessibilityServiceInfo();

        OPEN_WINDOW_EVENTS.add(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
        OPEN_WINDOW_EVENTS.add(AccessibilityEvent.TYPE_VIEW_FOCUSED);
        // if (Build.VERSION.SDK_INT >= 14) {
        // OPEN_WINDOW_EVENTS.add(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
        // OPEN_WINDOW_EVENTS.add(AccessibilityEvent.TYPE_VIEW_SCROLLED);
        // }
        // else {
        // OPEN_WINDOW_EVENTS.add(2048);
        // OPEN_WINDOW_EVENTS.add(4096);
        // }

        // Set the type of events that this service wants to listen to. Others
        // won't be passed to this service.
        int eventTypes = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;
        for (Integer openWindowEvent : OPEN_WINDOW_EVENTS) {
            eventTypes = eventTypes | openWindowEvent;
        }
        info.eventTypes = eventTypes;

        // If you only want this service to work with specific applications, set their
        // package names here. Otherwise, when the service is activated, it will listen
        // to events from all applications.
        info.packageNames = ApplicationPackages.getMergedPackages(ApplicationPackages.PKG_WHATSAPP,
                ApplicationPackages.PKG_CALLS, ApplicationPackages.PKG_SMS);

        // Set the type of feedback your service will provide.
        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_VISUAL;

        // Default services are invoked only if no package-specific ones are present
        // for the type of AccessibilityEvent generated. This service *is*
        // application-specific, so the flag isn't necessary. If this was a
        // general-purpose service, it would be worth considering setting the
        // DEFAULT flag.

        // info.flags = AccessibilityServiceInfo.DEFAULT;

        info.notificationTimeout = 100;

        this.setServiceInfo(info);

    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        int eventType = event.getEventType();
        String packageName = event.getPackageName().toString();
        Logger.log("Evento de accesibilidad detectado de tipo: " + Integer.valueOf(eventType).toString()
                + ", aplicación: " + packageName);
        if (isOpenWindowEvent(eventType)) {
            if (ArrayUtils.containsIgnoreCase(ApplicationPackages.PKG_WHATSAPP, packageName)) {
                Logger.log("Se ha abierto la aplicación WhatsApp");
                CpuLockTask.execute(this, DeleteAllWhatsAppDataCpuLockTask.class);
            }

            /*
             * En llamadas y mensajes tenemos que introducir un retardo porque sino suele pasar que la recolección de
             * datos se produce antes de que el sistema haya marcado las llamadas y mensajes como leídos.
             */
            if (ArrayUtils.containsIgnoreCase(ApplicationPackages.PKG_CALLS, packageName)) {
                Logger.log("Se ha abierto la aplicación de las llamadas.");
                CpuLockTask.execute(this, MissedCallsChangedCpuLockTask.class, AppUtils.SLEEP_BEFORE_RECOLLECTION);
            }
            if (ArrayUtils.containsIgnoreCase(ApplicationPackages.PKG_SMS, packageName)) {
                Logger.log("Se ha abierto la aplicación de los SMSs.");
                CpuLockTask.execute(this, SmsChangedCpuLockTask.class, AppUtils.SLEEP_BEFORE_RECOLLECTION);
            }
        }
        if (((ArrayUtils.containsIgnoreCase(ApplicationPackages.PKG_WHATSAPP, packageName)) && (eventType == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED))) {
            this.processWhatsAppNotificacion(event);
        }
    }

    @Override
    public void onInterrupt() {

    }

    private boolean isOpenWindowEvent(int event) {
        boolean result;
        if (OPEN_WINDOW_EVENTS.contains(event)) {
            result = true;
        } else {
            result = false;
        }
        return result;
    }

    @SuppressLint("NewApi")
    private void processWhatsAppNotificacion(AccessibilityEvent event) {
        Notification notification;
        try {
            notification = ((Notification) event.getParcelableData());
        } catch (ClassCastException e) {
            notification = null;
        }
        if (notification == null) {
            Logger.log("Se ha recibido una notificación de WhatsApp pero no es de clase \"Notification\" o está vacía.");
        } else {
            WhatsAppNotificationContent content = null;
            int index = 1;
            for (WhatsAppNotificationExtractionStrategy strategy : STRATEGIES) {
                content = strategy.extract(notification);
                if (content != null) {
                    Logger.log("Éxito en estrategia de extracción " + index + ".");
                    break;
                }
                index++;
            }
            if (content == null) {
                Logger.log("Se ha recibido una notificación de WhatsApp pero la vista está vacía o ninguna de las estrategias de extracción han funcionado.");
            } else {
                Logger.log("Se ha recibido una notificación de WhatsApp analizable.");
                CpuLockTask.execute(this, UpdateWhatsAppDataCpuLockTask.class, 0, content);
            }
        }
    }
}

I am so sorry that the code's comments are in Spanish. That is because it is my native language.

user3289695
  • 740
  • 1
  • 9
  • 20

3 Answers3

0

it happens to me with Greenify on a Xiaomi redmi note 3 (marshmallow),

I did :

Settings->apps setting->permissions-> autostart

, and allowing Greenify. It worked for me.

Sachith Muhandiram
  • 2,819
  • 10
  • 45
  • 94
0

My best guess is Android Studio is disabling the service every time you run the app.

Don't know the exact reason. Try opening the app manually, it will work.

Vivek
  • 566
  • 5
  • 6
-2

In my case, oppo f1s, I found out one solution:

  1. First of all, your device should be rooted, the best way to do that is through resetting firmware to factory or updating to newer oppo system image, then installing custom recovery BIOS or whatever its name is, then rooting under Recovery mode using Magisc. All this is described in some xda-developers forum topic. But that is not our issue for now.

  2. To prevent accessibility permission to be revoked from our service or app, we have to go Developer Options, by browsing Settings >> Device Info, then clicking about 8 times or more on Build Version.

  3. Now, when we have become a developer for good, we go back and then we navigate towards Settings >> Advanced Settings >> Developer Settings and enable Developer Options.

  4. Open section Running Services, browse for the application named Security Center, inside which we can see 4 running services, then STOP service named CONTROL_VERIFY_SERVICE (of course on your own risk).

That is it, now just go to Settings >> Advanced Settings >> Accessibility and enable accessibility for your application and enjoy it.

paulina_glab
  • 2,467
  • 2
  • 16
  • 25