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
This commit is contained in:
Rhed Jao
2022-08-19 15:56:02 +08:00
parent 5b0d25ef33
commit 9ddd4d4fc7
2 changed files with 50 additions and 10 deletions

View File

@@ -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

View File

@@ -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());
}
}
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) {