From 9ddd4d4fc720a5a2000026765a0954d25cd1874e Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Fri, 19 Aug 2022 15:56:02 +0800 Subject: [PATCH] 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 --- .../view/textservice/TextServicesManager.java | 4 ++ .../TextServicesManagerService.java | 56 +++++++++++++++---- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java index e31eabb083e51..0b63c179d0420 100644 --- a/core/java/android/view/textservice/TextServicesManager.java +++ b/core/java/android/view/textservice/TextServicesManager.java @@ -303,6 +303,10 @@ public final class TextServicesManager { /** * Retrieve the list of currently enabled spell checkers. * + *

Note: The results are filtered by the rules of + * package visibility, except for + * the currently active spell checker. + * * @return The list of currently enabled spell checkers. */ @UserHandleAware diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java index cd2b8943ce117..8431f1cc0b4cb 100644 --- a/services/core/java/com/android/server/textservices/TextServicesManagerService.java +++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java @@ -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 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 spellCheckerList; synchronized (mLock) { final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return null; - ArrayList 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()); - } - } - return spellCheckerList.toArray(new SpellCheckerInfo[spellCheckerList.size()]); + 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); + } + 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) {