Enforce package visibility filtering on AccountManager APIs
Apps targeting Android U and above calling methods to update or query account visibility need to declare package visibility needs for the target package in the manifest. APIs listed below apply this rule. - #getAccountVisibility - #getAccountsAndVisibilityForPackage - #setAccountVisibility - #addAccountExplicitlyWithVisibility Bug: 154726397 Bug: 186229635 Test: atest CtsAccountManagerTestCases Test: atest CtsAccountsHostTestCases Test: atest CtsAppEnumerationTestCases Change-Id: Ia40a3fb6025557f35e55fd26947dd1d89226f7f7
This commit is contained in:
@@ -1029,7 +1029,9 @@ public class AccountManager {
|
||||
* @param password The password to associate with the account, null for none
|
||||
* @param extras String values to use for the account's userdata, null for none
|
||||
* @param visibility Map from packageName to visibility values which will be set before account
|
||||
* is added. See {@link #getAccountVisibility} for possible values.
|
||||
* is added. See {@link #getAccountVisibility} for possible values. Declaring
|
||||
* <a href="/training/basics/intents/package-visibility">package visibility</a> needs for
|
||||
* package names in the map is needed, if the caller is targeting API level 34 and above.
|
||||
*
|
||||
* @return True if the account was successfully added, false if the account already exists, the
|
||||
* account is null, or another error occurs.
|
||||
@@ -1113,7 +1115,9 @@ public class AccountManager {
|
||||
* the specified account.
|
||||
*
|
||||
* @param account {@link Account} to update visibility
|
||||
* @param packageName Package name of the application to modify account visibility
|
||||
* @param packageName Package name of the application to modify account visibility. Declaring
|
||||
* <a href="/training/basics/intents/package-visibility">package visibility</a> needs
|
||||
* for it is needed, if the caller is targeting API level 34 and above.
|
||||
* @param visibility New visibility value
|
||||
*
|
||||
* @return True, if visibility value was successfully updated.
|
||||
@@ -1145,7 +1149,9 @@ public class AccountManager {
|
||||
* @param account {@link Account} to get visibility
|
||||
* @param packageName Package name of the application to get account visibility
|
||||
*
|
||||
* @return int Visibility of given account.
|
||||
* @return int Visibility of given account. For the caller targeting API level 34 and above,
|
||||
* {@link #VISIBILITY_NOT_VISIBLE} is returned if the given package is filtered by the rules of
|
||||
* <a href="/training/basics/intents/package-visibility">package visibility</a>.
|
||||
*/
|
||||
public @AccountVisibility int getAccountVisibility(Account account, String packageName) {
|
||||
if (account == null)
|
||||
|
||||
@@ -45,6 +45,9 @@ import android.app.PendingIntent;
|
||||
import android.app.admin.DevicePolicyEventLogger;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.DevicePolicyManagerInternal;
|
||||
import android.app.compat.CompatChanges;
|
||||
import android.compat.annotation.ChangeId;
|
||||
import android.compat.annotation.EnabledAfter;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ClipData;
|
||||
import android.content.ComponentName;
|
||||
@@ -70,6 +73,7 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteFullException;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
@@ -196,6 +200,14 @@ public class AccountManagerService
|
||||
private static final int SIGNATURE_CHECK_MATCH = 1;
|
||||
private static final int SIGNATURE_CHECK_UID_MATCH = 2;
|
||||
|
||||
/**
|
||||
* Apps targeting Android U and above need to declare the package visibility needs in the
|
||||
* manifest to access the AccountManager APIs.
|
||||
*/
|
||||
@ChangeId
|
||||
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
|
||||
private static final long ENFORCE_PACKAGE_VISIBILITY_FILTERING = 154726397;
|
||||
|
||||
static {
|
||||
ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
|
||||
ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
|
||||
@@ -527,7 +539,7 @@ public class AccountManagerService
|
||||
*/
|
||||
private Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
|
||||
List<String> accountTypes, Integer callingUid, UserAccounts accounts) {
|
||||
if (!packageExistsForUser(packageName, accounts.userId)) {
|
||||
if (!canCallerAccessPackage(packageName, callingUid, accounts.userId)) {
|
||||
Log.w(TAG, "getAccountsAndVisibilityForPackage#Package not found " + packageName);
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
@@ -629,6 +641,9 @@ public class AccountManagerService
|
||||
return AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE;
|
||||
}
|
||||
}
|
||||
if (!canCallerAccessPackage(packageName, callingUid, accounts.userId)) {
|
||||
return AccountManager.VISIBILITY_NOT_VISIBLE;
|
||||
}
|
||||
return resolveAccountVisibility(account, packageName, accounts);
|
||||
} finally {
|
||||
restoreCallingIdentity(identityToken);
|
||||
@@ -779,7 +794,7 @@ public class AccountManagerService
|
||||
try {
|
||||
UserAccounts accounts = getUserAccounts(userId);
|
||||
return setAccountVisibility(account, packageName, newVisibility, true /* notify */,
|
||||
accounts);
|
||||
accounts, callingUid);
|
||||
} finally {
|
||||
restoreCallingIdentity(identityToken);
|
||||
}
|
||||
@@ -798,11 +813,12 @@ public class AccountManagerService
|
||||
* @param newVisibility New visibility calue
|
||||
* @param notify if the flag is set applications will get notification about visibility change
|
||||
* @param accounts UserAccount that currently hosts the account and application
|
||||
* @param callingUid The caller's uid.
|
||||
*
|
||||
* @return True if account visibility was changed.
|
||||
*/
|
||||
private boolean setAccountVisibility(Account account, String packageName, int newVisibility,
|
||||
boolean notify, UserAccounts accounts) {
|
||||
boolean notify, UserAccounts accounts, int callingUid) {
|
||||
synchronized (accounts.dbLock) {
|
||||
synchronized (accounts.cacheLock) {
|
||||
Map<String, Integer> packagesToVisibility;
|
||||
@@ -813,8 +829,8 @@ public class AccountManagerService
|
||||
getRequestingPackages(account, accounts);
|
||||
accountRemovedReceivers = getAccountRemovedReceivers(account, accounts);
|
||||
} else {
|
||||
if (!packageExistsForUser(packageName, accounts.userId)) {
|
||||
return false; // package is not installed.
|
||||
if (!canCallerAccessPackage(packageName, callingUid, accounts.userId)) {
|
||||
return false; // package is not installed or not visible.
|
||||
}
|
||||
packagesToVisibility = new HashMap<>();
|
||||
packagesToVisibility.put(packageName,
|
||||
@@ -826,8 +842,8 @@ public class AccountManagerService
|
||||
}
|
||||
} else {
|
||||
// Notifications will not be send - only used during add account.
|
||||
if (!isSpecialPackageKey(packageName) &&
|
||||
!packageExistsForUser(packageName, accounts.userId)) {
|
||||
if (!isSpecialPackageKey(packageName)
|
||||
&& !canCallerAccessPackage(packageName, callingUid, accounts.userId)) {
|
||||
// package is not installed and not meta value.
|
||||
return false;
|
||||
}
|
||||
@@ -1045,20 +1061,6 @@ public class AccountManagerService
|
||||
return (receivers != null && receivers.size() > 0);
|
||||
}
|
||||
|
||||
private boolean packageExistsForUser(String packageName, int userId) {
|
||||
try {
|
||||
final long identityToken = clearCallingIdentity();
|
||||
try {
|
||||
mPackageManager.getPackageUidAsUser(packageName, userId);
|
||||
return true;
|
||||
} finally {
|
||||
restoreCallingIdentity(identityToken);
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if packageName is one of special values.
|
||||
*/
|
||||
@@ -1903,7 +1905,7 @@ public class AccountManagerService
|
||||
for (Entry<String, Integer> entry : packageToVisibility.entrySet()) {
|
||||
setAccountVisibility(account, entry.getKey() /* package */,
|
||||
entry.getValue() /* visibility */, false /* notify */,
|
||||
accounts);
|
||||
accounts, callingUid);
|
||||
}
|
||||
}
|
||||
accounts.accountsDb.setTransactionSuccessful();
|
||||
@@ -5915,6 +5917,32 @@ public class AccountManagerService
|
||||
return (dpmi != null) && (dpmi.isActiveProfileOwner(uid) || dpmi.isActiveDeviceOwner(uid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the access to the target package by rules of the package visibility if the caller
|
||||
* targeting API level U and above. Otherwise, returns true if the package is installed on
|
||||
* the device.
|
||||
*
|
||||
* @param targetPkgName The package name to check.
|
||||
* @param callingUid The caller that is going to access the package.
|
||||
* @param userId The user ID where the target package resides.
|
||||
* @return true if the caller is able to access the package.
|
||||
*/
|
||||
private boolean canCallerAccessPackage(@NonNull String targetPkgName, int callingUid,
|
||||
int userId) {
|
||||
final PackageManagerInternal pmInternal =
|
||||
LocalServices.getService(PackageManagerInternal.class);
|
||||
if (!CompatChanges.isChangeEnabled(ENFORCE_PACKAGE_VISIBILITY_FILTERING, callingUid)) {
|
||||
return pmInternal.getPackageUid(
|
||||
targetPkgName, 0 /* flags */, userId) != Process.INVALID_UID;
|
||||
}
|
||||
final boolean canAccess = !pmInternal.filterAppAccess(targetPkgName, callingUid, userId);
|
||||
if (!canAccess && Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Log.d(TAG, "Package " + targetPkgName + " is not visible to caller " + callingUid
|
||||
+ " for user " + userId);
|
||||
}
|
||||
return canAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
|
||||
throws RemoteException {
|
||||
|
||||
Reference in New Issue
Block a user