Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ddd4d4f authored by Rhed Jao's avatar Rhed Jao
Browse files

Improve package visibility gap for TextServicesManager

Two APIs in TextServicesManager expose the installed spell checker
packages to the caller without holding the query package permission.
  - getSpellCheckerService
  - getEnabledSpellCheckers

This cl filter the access to spell checkers from the caller by rules
of package visibilty.

Bug: 212287294
Bug: 212287061
Test: atest CtsAppEnumerationTestCases
Test: atest android.view.inputmethod.cts.SpellCheckerTest
Change-Id: I109231ca438db3a1df95a54322548bb11c026a78
parent 5b0d25ef
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -303,6 +303,10 @@ public final class TextServicesManager {
    /**
     * Retrieve the list of currently enabled spell checkers.
     *
     * <p> Note: The results are filtered by the rules of
     * <a href="/training/basics/intents/package-visibility">package visibility</a>, except for
     * the currently active spell checker.
     *
     * @return The list of currently enabled spell checkers.
     */
    @UserHandleAware
+44 −8
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
@@ -583,10 +584,17 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
                return;
            }
            final SpellCheckerInfo sci = spellCheckerMap.get(sciId);
            final int uid = Binder.getCallingUid();
            if (!canCallerAccessSpellChecker(sci, uid, userId)) {
                if (DBG) {
                    Slog.d(TAG, "Spell checker " + sci.getId()
                            + " is not visible to the caller " + uid);
                }
                return;
            }
            HashMap<String, SpellCheckerBindGroup> spellCheckerBindGroups =
                    tsd.mSpellCheckerBindGroups;
            SpellCheckerBindGroup bindGroup = spellCheckerBindGroups.get(sciId);
            final int uid = Binder.getCallingUid();
            if (bindGroup == null) {
                final long ident = Binder.clearCallingIdentity();
                try {
@@ -649,20 +657,28 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
    public SpellCheckerInfo[] getEnabledSpellCheckers(@UserIdInt int userId) {
        verifyUser(userId);

        final ArrayList<SpellCheckerInfo> spellCheckerList;
        synchronized (mLock) {
            final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
            if (tsd == null) return null;

            ArrayList<SpellCheckerInfo> spellCheckerList = tsd.mSpellCheckerList;
            if (DBG) {
                Slog.d(TAG, "getEnabledSpellCheckers: " + spellCheckerList.size());
                for (int i = 0; i < spellCheckerList.size(); ++i) {
                    Slog.d(TAG,
                            "EnabledSpellCheckers: " + spellCheckerList.get(i).getPackageName());
            spellCheckerList = new ArrayList<>(tsd.mSpellCheckerList);
        }
        int size = spellCheckerList.size();
        final int callingUid = Binder.getCallingUid();
        for (int i = size - 1; i >= 0; i--) {
            if (canCallerAccessSpellChecker(spellCheckerList.get(i), callingUid, userId)) {
                continue;
            }
            if (DBG) {
                Slog.d(TAG, "Spell checker " + spellCheckerList.get(i).getPackageName()
                        + " is not visible to the caller " + callingUid);
            }
            return spellCheckerList.toArray(new SpellCheckerInfo[spellCheckerList.size()]);
            spellCheckerList.remove(i);
        }

        return spellCheckerList.isEmpty() ? null
                : spellCheckerList.toArray(new SpellCheckerInfo[spellCheckerList.size()]);
    }

    @Override
@@ -701,6 +717,26 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
        }
    }

    /**
     * Filter the access to spell checkers by rules of the package visibility. Return {@code true}
     * if the given spell checker is the currently selected one or visible to the caller.
     *
     * @param sci The spell checker to check.
     * @param callingUid The caller that is going to access the spell checker.
     * @param userId The user id where the spell checker resides.
     * @return {@code true} if caller is able to access the spell checker.
     */
    private boolean canCallerAccessSpellChecker(@NonNull SpellCheckerInfo sci, int callingUid,
            @UserIdInt int userId) {
        final SpellCheckerInfo currentSci = getCurrentSpellCheckerForUser(userId);
        if (currentSci != null && currentSci.getId().equals(sci.getId())) {
            return true;
        }
        final PackageManagerInternal pmInternal =
                LocalServices.getService(PackageManagerInternal.class);
        return !pmInternal.filterAppAccess(sci.getPackageName(), callingUid, userId);
    }

    private void setCurrentSpellCheckerLocked(@Nullable SpellCheckerInfo sci, TextServicesData tsd) {
        final String sciId = (sci != null) ? sci.getId() : "";
        if (DBG) {