Settings app for f21 fixed Accessibility Crash

I patched the MtkSettings2.apk (6.1.3 @sh7411usa version) so that it doesn’t crash when you try to turn on accessibility for Voice Access (or any other apps that were having that issue).

I’ll try to post a guide later for those who want to patch the settings app from other versions.

Settings App (replace the app in /system_ext/priv-app/MtkSettings2/)

1 Like

Can you detail what you did? I’ll include it in future updates

Sure, I’ll try write it up later today. There were 3 issues that needed to be fixed.

  1. The crash was caused by settings trying to build the voice access screen out of order (certain apps like voice access have a special screen with elements in accessibility settings). Once I got that fixed, there was another issue.
  2. The toggle to turn on accessibility was not showing. The settings app has an “invisible toggle” flag for certain types of apps (depending on the target sdk amongst other factors), so I made the check that decides what kind of toggle to apply always return normal toggle.
  3. this was not so important but I fixed it anyway. When you turned on accessibility for voice access, settings would crash ( but the accessibility did turn on). This is because there is an introduction screen that the settings app couldn’t construct, so I made I not try to construct it, and it just shows the “got it” button.
1 Like

I had ai write it up

Issue 1: Settings crashes when opening the Voice Access screen

In ToggleFeaturePreferenceFragment.onViewCreated(), a PreferenceCategory (key general_categories) is created and configured, but never attached to the fragment’s PreferenceScreen before another preference is added into it. Adding a child to an unattached PreferenceGroup throws a NullPointerException in PreferenceManager.getNextId().

File: smali/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.smali
Method: onViewCreated(Landroid/view/View;Landroid/os/Bundle;)V
Fix: Insert a call to attach the category to the screen (p1) right after it’s titled, before initShortcutPreference is called:

sget v2, Lcom/android/settings/R$string;->accessibility_screen_option:I

invoke-virtual {v0, v2}, Landroidx/preference/Preference;->setTitle(I)V

invoke-virtual {p1, v0}, Landroidx/preference/PreferenceGroup;->addPreference(Landroidx/preference/Preference;)Z

invoke-direct {p0, p2}, Lcom/android/settings/accessibility/ToggleFeaturePreferenceFragment;->initShortcutPreference(Landroid/os/Bundle;)V

(Only the addPreference line is new, the lines around it already exist and are shown for placement reference.)

Issue 2: No toggle switch appears on the Voice Access screen

Cause: Settings classifies every accessibility service into one of 3 fragment types via AccessibilityUtil.getAccessibilityServiceFragmentType(), based on targetSdkVersion and whether the service declares the accessibility-button flag. Voice Access was being classified as “Invisible Toggle” (no switch shown, expects shortcut-only interaction) instead of the normal “Toggle” type.

File: smali/com/android/settings/accessibility/AccessibilityUtil.smali
Method: getAccessibilityServiceFragmentType(Landroid/accessibilityservice/AccessibilityServiceInfo;)I

Fix: Replace the entire method body to always return type 2 (normal Toggle) for every accessibility service:

.method static getAccessibilityServiceFragmentType(Landroid/accessibilityservice/AccessibilityServiceInfo;)I
    .registers 5

    const/4 v1, 0x2

    return v1
.end method

Note: this device has no volume keys, so forcing away the “Volume Shortcut Toggle” classification (type 0) has no downside here.

Issue 3: Settings crashes when actually flipping the toggle on

Cause: Enabling the toggle triggers a tutorial dialog showing the accessibility-shortcut gesture animation. AccessibilityGestureNavigationTutorial.createShortcutNavigationContentView() builds this content based on the device’s navigation mode, and an internal Preconditions.checkArgument() throws IllegalArgumentException: Unexpected tutorial pages size — this device’s nav mode doesn’t produce a page count the check expects.

File: smali/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.smali
Method: createAccessibilityTutorialDialog(Landroid/content/Context;I)Landroidx/appcompat/app/AlertDialog;

Fix: Delete the 3 lines that build and attach the crashing content view, leaving the dialog’s title/button/creation intact:

Remove:

invoke-static {p0, p1}, Lcom/android/settings/accessibility/AccessibilityGestureNavigationTutorial;->createShortcutNavigationContentView(Landroid/content/Context;I)Landroid/view/View;

move-result-object p0

invoke-virtual {v0, p0}, Landroidx/appcompat/app/AlertDialog$Builder;->setView(Landroid/view/View;)Landroidx/appcompat/app/AlertDialog$Builder;

Resulting method:

.method static createAccessibilityTutorialDialog(Landroid/content/Context;I)Landroidx/appcompat/app/AlertDialog;
    .registers 3

    new-instance v0, Landroidx/appcompat/app/AlertDialog$Builder;

    invoke-direct {v0, p0}, Landroidx/appcompat/app/AlertDialog$Builder;-><init>(Landroid/content/Context;)V

    sget p0, Lcom/android/settings/R$string;->accessibility_tutorial_dialog_button:I

    sget-object p1, Lcom/android/settings/accessibility/AccessibilityGestureNavigationTutorial;->mOnClickListener:Landroid/content/DialogInterface$OnClickListener;

    invoke-virtual {v0, p0, p1}, Landroidx/appcompat/app/AlertDialog$Builder;->setNegativeButton(ILandroid/content/DialogInterface$OnClickListener;)Landroidx/appcompat/app/AlertDialog$Builder;

    invoke-virtual {v0}, Landroidx/appcompat/app/AlertDialog$Builder;->create()Landroidx/appcompat/app/AlertDialog;

    move-result-object p0

    return-object p0
.end method

This produces a minimal dialog (dismiss button only, no tutorial illustration) instead of crashing. onCreateDialog in both ToggleFeaturePreferenceFragment and ToggleAccessibilityServicePreferenceFragment call into this method but don’t need any changes themselves.

2 Likes

Thank you! Very detailed!

1 Like

Curious though - most of us here used voice access for voice typing and tapping on devices that didn’t otherwise support voice typing and did not have touch screens. Seeing as the F21 has both, why do we need voice access?

Telling it to call someone

Gotcha - so voice commands in a kosher way (you want to avoid google assistant).

Yup, I’m lazy like that, and I like being able to launch it with a physical key. I remapped the menu key ( /system/usr/keylayout/Generic.kl 139 MENU to 139 FOCUS)

2 Likes

Excellent piece of info!