How I played Sherlock Holmes or how to prevent the system application from crashing with an error

In my program I use calling phone settings.

Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
startActivity(intent);

This is a request to open the desired location in the system settings of the phone

The rushers may say that I forgot to include this call in the try-catch and they will be wrong.

The mistake was not that there is no print service on the phone, but much more.

The phone opens a list of all installed services as expected.

But after clicking on the name of any service, even by service, we get a crash from the android itself.

Hidden text

Screenshots under the spoiler

But such a global error could not remain in the release. If we try to get into the settings in the normal way (swap across the screen from above – gear – look for a print – and so on)

Hidden text

Everything works. But what is it? The appearance is different!

It turns out that the vendor did not completely redo or customize the standard settings since there is an alternative from it.

The first thought is to look for a custom intent (for those in a hurry Error there is a standard one used)

I start googling about Realme UI and some custom intents with printing – empty. About Color OS – empty.

Complete lack of understanding, but where to dig further.

I decide to look at the packages.

> adb shell pm list packages | grep setting

package:com.android.settings.intelligence
package:com.oplus.wirelesssettings
package:com.android.settings
package:com.android.providers.settings

>adb shell pm list packages | grep print  
package:com.google.android.printservice.recommendation
package:com.android.systemui.overlay.fingerprint.anim.lgsy
package:com.android.systemui.overlay.fingerprint.anim.jslz
package:com.android.systemui.overlay.fingerprint.anim.jhsy
package:com.android.printspooler
package:com.android.systemui.overlay.fingerprint.anim.xklc
package:com.android.systemui.overlay.fingerprint.anim.ccyh
package:com.android.systemui.overlay.fingerprint.anim.nlgs
package:com.smart.printer.print.photos.documents.printing.pictures
package:com.android.systemui.overlay.fingerprint.anim.tyjw

No ideas are added. But HERE is a code somewhere that works. But nothing similar to the settings is visible from realmi itself.

!!! So how do you find out which application is at the top of the activity stack? Google

adb shell dumpsys window | grep Focused.
  mTopFocusedDisplayId=0
  mFocusedApp=ActivityRecord{37325a8 u0 ru.a402d.printservice/.ui.MainActivity t4128}
      mLastFocusedRootTask=Task{a1d27a7 #4128 type=standard A=10319:ru.a402d.printservice}
    mFocusedWindow=Window{22f4af7 u0 Application Error: com.android.settings}
    DO DUMP StrategyManager : { StrategyIgnoreSleepKey StrategyShutdown StrategyLaunchIntercomCustom StrategyCustomizeIgnoreVirtualKey StrategyUnusedPhoneDetection StrategyBlackscreenshot StrategyPowerKeyEndCall StrategyDisableBottomKey StrategyVolumeKeyLaunchCamera StrategyIngoreKeyInFocusedWindow}
    mQueueingInterceptorsCopy : [120:com.android.server.policy.KeyLongPressBaseStrategy$1@64b501c, 130:com.android.server.policy.StrategyCustomizeIgnoreVirtualKey$1@9f7c21a, 170:com.android.server.policy.StrategyDisableBottomKey$2@221d425, 190:com.android.server.policy.StrategyIngoreKeyInFocusedWindow$1@a8c9fa]
    mDispatchingInterceptorsCopy : [120:com.android.server.policy.KeyLongPressBaseStrategy$1@64b501c, 130:com.android.server.policy.StrategyCustomizeIgnoreVirtualKey$1@9f7c21a, 170:com.android.server.policy.StrategyDisableBottomKey$2@221d425, 190:com.android.server.policy.StrategyIngoreKeyInFocusedWindow$1@a8c9fa]
  mFocusedApp=ActivityRecord{461677f u0 com.android.printspooler/.ui.settings.SettingsEntranceActivity t4127}
      mLastFocusedRootTask=Task{9e02046 #4127 type=standard A=1000:com.android.settings.root}
    mFocusedWindow=Window{f727c67 u0 com.android.printspooler/com.android.printspooler.ui.settings.SettingsEntranceActivity}
  mTopFocusedDisplayId=0

It turns out that from the settings we, unnoticed by ourselves, get into the print spooler com.android.printspooler

What then is in his manifesto? We'll watch

adb shell pm path com.android.printspooler            
package:/system_ext/app/PrintSpooler/PrintSpooler.apk

We found out where it is, now let’s download it to us

adb pull /system_ext/app/PrintSpooler/PrintSpooler.apk     

For a quick look at the application manifest, you can simply open the apk in android studio.

        <activity
            android:theme="@ref/0x7f11002a"
            android:label="@ref/0x7f100135"
            android:name="com.android.printspooler.ui.settings.SettingsEntranceActivity"
            android:exported="true"
            android:screenOrientation="-1"
            android:configChanges="0x40003dfe"
            android:windowSoftInputMode="0x22">

.....

            <intent-filter
                android:priority="1">

                <action
                    android:name="android.settings.ACTION_PRINT_SETTINGS" />

                <category
                    android:name="android.intent.category.DEFAULT" />
            </intent-filter>

   ...
          
        </activity>

It turns out there is no special intent.

Part two. How to call the required component

And we’ll start solving it by modifying our application manifest

    <queries>
      ...
        <intent>
            <action
                android:name="android.settings.ACTION_PRINT_SETTINGS" />
            <category
                android:name="android.intent.category.DEFAULT" />
        </intent>
      ...
    </queries>

https://developer.android.com/guide/topics/manifest/queries-element

Starting with API 30 (11th Android), for security purposes, access to the list of installed applications has been reduced and you can now resolve the intentions available for execution only by mentioning them in the manifest.

By default, the most suitable program is launched through startActivity. There is a way to offer the user to choose from several

PackageManager packageManager = requireActivity().getPackageManager();
Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
// Create intent to show chooser
Intent chooser = Intent.createChooser(intent, getString(R.string.open));
if (intent.resolveActivity(packageManager) != null) {
    startActivity(chooser);
}

This method shows the choice, but only if the user did not select it the previous time [v] always use.

In my case, the dialogue was not shown, and the screen still opened leading to a crash.

Okay, let's check that several components resolve

PackageManager packageManager = requireActivity().getPackageManager();
Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);

List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent,
                    PackageManager.GET_RESOLVED_FILTER);

if (resolveInfos.size() > 1) {
   // ок их тут много
}  

You can find out which one you need to launch like this:

String packageName = resolveInfo.activityInfo.applicationInfo.packageName;
intent.setPackage(packageName);

Time to crutch

Because of such miracles of phone manufacturers, and simply because of differences in Android versions, the real code is overgrown with backups.

I make the following assumptions. If there is more than one handler for an event, then the manufacturer introduced it intentionally and you need to use it. In case there are three or more of them, I will take the first one that is different from the standard one.

The final code looks like this.

PackageManager packageManager = requireActivity().getPackageManager();
Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);

List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent,
                    PackageManager.GET_RESOLVED_FILTER);

 try {
    if (resolveInfos.size() > 1) {
       for (ResolveInfo resolveInfo : resolveInfos) {
          if (resolveInfo.activityInfo != null) {
                String packageName = resolveInfo.activityInfo.applicationInfo.packageName;
                 if (!"com.android.settings".equals(packageName)) {
                      intent.setPackage(packageName);
                       break;
                 }
          }
       }
   }
} catch (Exception ignored) {}

try {
   startActivity(intent);
} catch (Exception e) {
   Toast.makeText(requireActivity(), R.string.Oopppsss, Toast.LENGTH_SHORT).show();
}

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *