Loading services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +1 −68 Original line number Diff line number Diff line Loading @@ -48,7 +48,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.net.MacAddress; import android.os.Binder; import android.os.Bundle; Loading @@ -56,16 +55,9 @@ import android.os.Handler; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; import android.util.Log; import android.util.PackageUtils; import android.util.Slog; import com.android.internal.util.ArrayUtils; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Class responsible for handling incoming {@link AssociationRequest}s. Loading Loading @@ -447,31 +439,6 @@ class AssociationRequestsProcessor { }; private boolean mayAssociateWithoutPrompt(@NonNull String packageName, @UserIdInt int userId) { // Below we check if the requesting package is allowlisted (usually by the OEM) for creating // CDM associations without user confirmation (prompt). // For this we'll check to config arrays: // - com.android.internal.R.array.config_companionDevicePackages // and // - com.android.internal.R.array.config_companionDeviceCerts. // Both arrays are expected to contain similar number of entries. // config_companionDevicePackages contains package names of the allowlisted packages. // config_companionDeviceCerts contains SHA256 digests of the signatures of the // corresponding packages. // If a package may be signed with one of several certificates, its package name would // appear multiple times in the config_companionDevicePackages, with different entries // (one for each of the valid signing certificates) at the corresponding positions in // config_companionDeviceCerts. final String[] allowlistedPackages = mContext.getResources() .getStringArray(com.android.internal.R.array.config_companionDevicePackages); if (!ArrayUtils.contains(allowlistedPackages, packageName)) { if (DEBUG) { Log.d(TAG, packageName + " is not allowlisted for creating associations " + "without user confirmation (prompt)"); Log.v(TAG, "Allowlisted packages=" + Arrays.toString(allowlistedPackages)); } return false; } // Throttle frequent associations final long now = System.currentTimeMillis(); final List<AssociationInfo> associationForPackage = Loading @@ -491,40 +458,6 @@ class AssociationRequestsProcessor { } } final String[] allowlistedPackagesSignatureDigests = mContext.getResources() .getStringArray(com.android.internal.R.array.config_companionDeviceCerts); final Set<String> allowlistedSignatureDigestsForRequestingPackage = new HashSet<>(); for (int i = 0; i < allowlistedPackages.length; i++) { if (allowlistedPackages[i].equals(packageName)) { final String digest = allowlistedPackagesSignatureDigests[i].replaceAll(":", ""); allowlistedSignatureDigestsForRequestingPackage.add(digest); } } final Signature[] requestingPackageSignatures = mPackageManager.getPackage(packageName) .getSigningDetails().getSignatures(); final String[] requestingPackageSignatureDigests = PackageUtils.computeSignaturesSha256Digests(requestingPackageSignatures); boolean requestingPackageSignatureAllowlisted = false; for (String signatureDigest : requestingPackageSignatureDigests) { if (allowlistedSignatureDigestsForRequestingPackage.contains(signatureDigest)) { requestingPackageSignatureAllowlisted = true; break; } } if (!requestingPackageSignatureAllowlisted) { Slog.w(TAG, "Certificate mismatch for allowlisted package " + packageName); if (DEBUG) { Log.d(TAG, " > allowlisted signatures for " + packageName + ": [" + String.join(", ", allowlistedSignatureDigestsForRequestingPackage) + "]"); Log.d(TAG, " > actual signatures for " + packageName + ": " + Arrays.toString(requestingPackageSignatureDigests)); } } return requestingPackageSignatureAllowlisted; return PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName); } } services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +2 −1 Original line number Diff line number Diff line Loading @@ -244,7 +244,8 @@ public class CompanionDeviceManagerService extends SystemService { mCompanionAppController = new CompanionApplicationController( context, mAssociationStore, mDevicePresenceMonitor); mTransportManager = new CompanionTransportManager(context, mAssociationStore); mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore, mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mPackageManagerInternal, mAssociationStore, mSystemDataTransferRequestStore, mTransportManager); // TODO(b/279663946): move context sync to a dedicated system service mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager); Loading services/companion/java/com/android/server/companion/PackageUtils.java +75 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP; import static android.content.pm.PackageManager.GET_CONFIGURATIONS; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static com.android.server.companion.CompanionDeviceManagerService.DEBUG; import static com.android.server.companion.CompanionDeviceManagerService.TAG; import android.Manifest; Loading @@ -35,20 +36,28 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.PackageManager.ResolveInfoFlags; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.os.Binder; import android.util.Log; import android.util.Slog; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Utility methods for working with {@link PackageInfo}-s. */ final class PackageUtils { public final class PackageUtils { private static final Intent COMPANION_SERVICE_INTENT = new Intent(CompanionDeviceService.SERVICE_INTERFACE); private static final String PROPERTY_PRIMARY_TAG = Loading Loading @@ -141,4 +150,69 @@ final class PackageUtils { return false; } } /** * Check if the package is allowlisted in the overlay config. * For this we'll check to config arrays: * - com.android.internal.R.array.config_companionDevicePackages * and * - com.android.internal.R.array.config_companionDeviceCerts. * Both arrays are expected to contain similar number of entries. * config_companionDevicePackages contains package names of the allowlisted packages. * config_companionDeviceCerts contains SHA256 digests of the signatures of the * corresponding packages. * If a package is signed with one of several certificates, its package name would * appear multiple times in the config_companionDevicePackages, with different entries * (one for each of the valid signing certificates) at the corresponding positions in * config_companionDeviceCerts. */ public static boolean isPackageAllowlisted(Context context, PackageManagerInternal packageManagerInternal, @NonNull String packageName) { final String[] allowlistedPackages = context.getResources() .getStringArray(com.android.internal.R.array.config_companionDevicePackages); if (!ArrayUtils.contains(allowlistedPackages, packageName)) { if (DEBUG) { Log.d(TAG, packageName + " is not allowlisted."); } return false; } final String[] allowlistedPackagesSignatureDigests = context.getResources() .getStringArray(com.android.internal.R.array.config_companionDeviceCerts); final Set<String> allowlistedSignatureDigestsForRequestingPackage = new HashSet<>(); for (int i = 0; i < allowlistedPackages.length; i++) { if (allowlistedPackages[i].equals(packageName)) { final String digest = allowlistedPackagesSignatureDigests[i].replaceAll(":", ""); allowlistedSignatureDigestsForRequestingPackage.add(digest); } } final Signature[] requestingPackageSignatures = packageManagerInternal.getPackage( packageName) .getSigningDetails().getSignatures(); final String[] requestingPackageSignatureDigests = android.util.PackageUtils.computeSignaturesSha256Digests( requestingPackageSignatures); boolean requestingPackageSignatureAllowlisted = false; for (String signatureDigest : requestingPackageSignatureDigests) { if (allowlistedSignatureDigestsForRequestingPackage.contains(signatureDigest)) { requestingPackageSignatureAllowlisted = true; break; } } if (!requestingPackageSignatureAllowlisted) { Slog.w(TAG, "Certificate mismatch for allowlisted package " + packageName); if (DEBUG) { Log.d(TAG, " > allowlisted signatures for " + packageName + ": [" + String.join(", ", allowlistedSignatureDigestsForRequestingPackage) + "]"); Log.d(TAG, " > actual signatures for " + packageName + ": " + Arrays.toString(requestingPackageSignatureDigests)); } } return requestingPackageSignatureAllowlisted; } } services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java +32 −16 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import android.companion.datatransfer.SystemDataTransferRequest; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.Bundle; import android.os.Handler; Loading @@ -50,6 +51,7 @@ import android.util.Slog; import com.android.server.companion.AssociationStore; import com.android.server.companion.CompanionDeviceManagerService; import com.android.server.companion.PackageUtils; import com.android.server.companion.PermissionsUtils; import com.android.server.companion.transport.CompanionTransportManager; Loading Loading @@ -80,6 +82,7 @@ public class SystemDataTransferProcessor { ".CompanionDeviceDataTransferActivity"); private final Context mContext; private final PackageManagerInternal mPackageManager; private final AssociationStore mAssociationStore; private final SystemDataTransferRequestStore mSystemDataTransferRequestStore; private final CompanionTransportManager mTransportManager; Loading @@ -87,10 +90,12 @@ public class SystemDataTransferProcessor { private final ExecutorService mExecutor; public SystemDataTransferProcessor(CompanionDeviceManagerService service, PackageManagerInternal packageManager, AssociationStore associationStore, SystemDataTransferRequestStore systemDataTransferRequestStore, CompanionTransportManager transportManager) { mContext = service.getContext(); mPackageManager = packageManager; mAssociationStore = associationStore; mSystemDataTransferRequestStore = systemDataTransferRequestStore; mTransportManager = transportManager; Loading Loading @@ -131,6 +136,11 @@ public class SystemDataTransferProcessor { */ public PendingIntent buildPermissionTransferUserConsentIntent(String packageName, @UserIdInt int userId, int associationId) { if (PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName)) { Slog.i(LOG_TAG, "User consent Intent should be skipped. Returning null."); return null; } final AssociationInfo association = resolveAssociation(packageName, userId, associationId); Slog.i(LOG_TAG, "Creating permission sync intent for userId [" + userId Loading Loading @@ -174,12 +184,16 @@ public class SystemDataTransferProcessor { final AssociationInfo association = resolveAssociation(packageName, userId, associationId); // Check if the request has been consented by the user. if (PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName)) { Slog.i(LOG_TAG, "Skip user consent check due to the same OEM package."); } else { List<SystemDataTransferRequest> storedRequests = mSystemDataTransferRequestStore.readRequestsByAssociationId(userId, associationId); boolean hasConsented = false; for (SystemDataTransferRequest storedRequest : storedRequests) { if (storedRequest instanceof PermissionSyncRequest && storedRequest.isUserConsented()) { if (storedRequest instanceof PermissionSyncRequest && storedRequest.isUserConsented()) { hasConsented = true; break; } Loading @@ -189,9 +203,11 @@ public class SystemDataTransferProcessor { Slog.e(LOG_TAG, message); try { callback.onError(message); } catch (RemoteException ignored) { } } catch (RemoteException ignored) { } return; } } // Start permission sync final long callingIdentityToken = Binder.clearCallingIdentity(); Loading services/tests/servicestests/src/com/android/server/companion/utils/OWNERS 0 → 100644 +1 −0 Original line number Diff line number Diff line include /services/companion/java/com/android/server/companion/OWNERS No newline at end of file Loading
services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +1 −68 Original line number Diff line number Diff line Loading @@ -48,7 +48,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.net.MacAddress; import android.os.Binder; import android.os.Bundle; Loading @@ -56,16 +55,9 @@ import android.os.Handler; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; import android.util.Log; import android.util.PackageUtils; import android.util.Slog; import com.android.internal.util.ArrayUtils; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Class responsible for handling incoming {@link AssociationRequest}s. Loading Loading @@ -447,31 +439,6 @@ class AssociationRequestsProcessor { }; private boolean mayAssociateWithoutPrompt(@NonNull String packageName, @UserIdInt int userId) { // Below we check if the requesting package is allowlisted (usually by the OEM) for creating // CDM associations without user confirmation (prompt). // For this we'll check to config arrays: // - com.android.internal.R.array.config_companionDevicePackages // and // - com.android.internal.R.array.config_companionDeviceCerts. // Both arrays are expected to contain similar number of entries. // config_companionDevicePackages contains package names of the allowlisted packages. // config_companionDeviceCerts contains SHA256 digests of the signatures of the // corresponding packages. // If a package may be signed with one of several certificates, its package name would // appear multiple times in the config_companionDevicePackages, with different entries // (one for each of the valid signing certificates) at the corresponding positions in // config_companionDeviceCerts. final String[] allowlistedPackages = mContext.getResources() .getStringArray(com.android.internal.R.array.config_companionDevicePackages); if (!ArrayUtils.contains(allowlistedPackages, packageName)) { if (DEBUG) { Log.d(TAG, packageName + " is not allowlisted for creating associations " + "without user confirmation (prompt)"); Log.v(TAG, "Allowlisted packages=" + Arrays.toString(allowlistedPackages)); } return false; } // Throttle frequent associations final long now = System.currentTimeMillis(); final List<AssociationInfo> associationForPackage = Loading @@ -491,40 +458,6 @@ class AssociationRequestsProcessor { } } final String[] allowlistedPackagesSignatureDigests = mContext.getResources() .getStringArray(com.android.internal.R.array.config_companionDeviceCerts); final Set<String> allowlistedSignatureDigestsForRequestingPackage = new HashSet<>(); for (int i = 0; i < allowlistedPackages.length; i++) { if (allowlistedPackages[i].equals(packageName)) { final String digest = allowlistedPackagesSignatureDigests[i].replaceAll(":", ""); allowlistedSignatureDigestsForRequestingPackage.add(digest); } } final Signature[] requestingPackageSignatures = mPackageManager.getPackage(packageName) .getSigningDetails().getSignatures(); final String[] requestingPackageSignatureDigests = PackageUtils.computeSignaturesSha256Digests(requestingPackageSignatures); boolean requestingPackageSignatureAllowlisted = false; for (String signatureDigest : requestingPackageSignatureDigests) { if (allowlistedSignatureDigestsForRequestingPackage.contains(signatureDigest)) { requestingPackageSignatureAllowlisted = true; break; } } if (!requestingPackageSignatureAllowlisted) { Slog.w(TAG, "Certificate mismatch for allowlisted package " + packageName); if (DEBUG) { Log.d(TAG, " > allowlisted signatures for " + packageName + ": [" + String.join(", ", allowlistedSignatureDigestsForRequestingPackage) + "]"); Log.d(TAG, " > actual signatures for " + packageName + ": " + Arrays.toString(requestingPackageSignatureDigests)); } } return requestingPackageSignatureAllowlisted; return PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName); } }
services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +2 −1 Original line number Diff line number Diff line Loading @@ -244,7 +244,8 @@ public class CompanionDeviceManagerService extends SystemService { mCompanionAppController = new CompanionApplicationController( context, mAssociationStore, mDevicePresenceMonitor); mTransportManager = new CompanionTransportManager(context, mAssociationStore); mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore, mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mPackageManagerInternal, mAssociationStore, mSystemDataTransferRequestStore, mTransportManager); // TODO(b/279663946): move context sync to a dedicated system service mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager); Loading
services/companion/java/com/android/server/companion/PackageUtils.java +75 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP; import static android.content.pm.PackageManager.GET_CONFIGURATIONS; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static com.android.server.companion.CompanionDeviceManagerService.DEBUG; import static com.android.server.companion.CompanionDeviceManagerService.TAG; import android.Manifest; Loading @@ -35,20 +36,28 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.PackageManager.ResolveInfoFlags; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.os.Binder; import android.util.Log; import android.util.Slog; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Utility methods for working with {@link PackageInfo}-s. */ final class PackageUtils { public final class PackageUtils { private static final Intent COMPANION_SERVICE_INTENT = new Intent(CompanionDeviceService.SERVICE_INTERFACE); private static final String PROPERTY_PRIMARY_TAG = Loading Loading @@ -141,4 +150,69 @@ final class PackageUtils { return false; } } /** * Check if the package is allowlisted in the overlay config. * For this we'll check to config arrays: * - com.android.internal.R.array.config_companionDevicePackages * and * - com.android.internal.R.array.config_companionDeviceCerts. * Both arrays are expected to contain similar number of entries. * config_companionDevicePackages contains package names of the allowlisted packages. * config_companionDeviceCerts contains SHA256 digests of the signatures of the * corresponding packages. * If a package is signed with one of several certificates, its package name would * appear multiple times in the config_companionDevicePackages, with different entries * (one for each of the valid signing certificates) at the corresponding positions in * config_companionDeviceCerts. */ public static boolean isPackageAllowlisted(Context context, PackageManagerInternal packageManagerInternal, @NonNull String packageName) { final String[] allowlistedPackages = context.getResources() .getStringArray(com.android.internal.R.array.config_companionDevicePackages); if (!ArrayUtils.contains(allowlistedPackages, packageName)) { if (DEBUG) { Log.d(TAG, packageName + " is not allowlisted."); } return false; } final String[] allowlistedPackagesSignatureDigests = context.getResources() .getStringArray(com.android.internal.R.array.config_companionDeviceCerts); final Set<String> allowlistedSignatureDigestsForRequestingPackage = new HashSet<>(); for (int i = 0; i < allowlistedPackages.length; i++) { if (allowlistedPackages[i].equals(packageName)) { final String digest = allowlistedPackagesSignatureDigests[i].replaceAll(":", ""); allowlistedSignatureDigestsForRequestingPackage.add(digest); } } final Signature[] requestingPackageSignatures = packageManagerInternal.getPackage( packageName) .getSigningDetails().getSignatures(); final String[] requestingPackageSignatureDigests = android.util.PackageUtils.computeSignaturesSha256Digests( requestingPackageSignatures); boolean requestingPackageSignatureAllowlisted = false; for (String signatureDigest : requestingPackageSignatureDigests) { if (allowlistedSignatureDigestsForRequestingPackage.contains(signatureDigest)) { requestingPackageSignatureAllowlisted = true; break; } } if (!requestingPackageSignatureAllowlisted) { Slog.w(TAG, "Certificate mismatch for allowlisted package " + packageName); if (DEBUG) { Log.d(TAG, " > allowlisted signatures for " + packageName + ": [" + String.join(", ", allowlistedSignatureDigestsForRequestingPackage) + "]"); Log.d(TAG, " > actual signatures for " + packageName + ": " + Arrays.toString(requestingPackageSignatureDigests)); } } return requestingPackageSignatureAllowlisted; } }
services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java +32 −16 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import android.companion.datatransfer.SystemDataTransferRequest; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.Bundle; import android.os.Handler; Loading @@ -50,6 +51,7 @@ import android.util.Slog; import com.android.server.companion.AssociationStore; import com.android.server.companion.CompanionDeviceManagerService; import com.android.server.companion.PackageUtils; import com.android.server.companion.PermissionsUtils; import com.android.server.companion.transport.CompanionTransportManager; Loading Loading @@ -80,6 +82,7 @@ public class SystemDataTransferProcessor { ".CompanionDeviceDataTransferActivity"); private final Context mContext; private final PackageManagerInternal mPackageManager; private final AssociationStore mAssociationStore; private final SystemDataTransferRequestStore mSystemDataTransferRequestStore; private final CompanionTransportManager mTransportManager; Loading @@ -87,10 +90,12 @@ public class SystemDataTransferProcessor { private final ExecutorService mExecutor; public SystemDataTransferProcessor(CompanionDeviceManagerService service, PackageManagerInternal packageManager, AssociationStore associationStore, SystemDataTransferRequestStore systemDataTransferRequestStore, CompanionTransportManager transportManager) { mContext = service.getContext(); mPackageManager = packageManager; mAssociationStore = associationStore; mSystemDataTransferRequestStore = systemDataTransferRequestStore; mTransportManager = transportManager; Loading Loading @@ -131,6 +136,11 @@ public class SystemDataTransferProcessor { */ public PendingIntent buildPermissionTransferUserConsentIntent(String packageName, @UserIdInt int userId, int associationId) { if (PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName)) { Slog.i(LOG_TAG, "User consent Intent should be skipped. Returning null."); return null; } final AssociationInfo association = resolveAssociation(packageName, userId, associationId); Slog.i(LOG_TAG, "Creating permission sync intent for userId [" + userId Loading Loading @@ -174,12 +184,16 @@ public class SystemDataTransferProcessor { final AssociationInfo association = resolveAssociation(packageName, userId, associationId); // Check if the request has been consented by the user. if (PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName)) { Slog.i(LOG_TAG, "Skip user consent check due to the same OEM package."); } else { List<SystemDataTransferRequest> storedRequests = mSystemDataTransferRequestStore.readRequestsByAssociationId(userId, associationId); boolean hasConsented = false; for (SystemDataTransferRequest storedRequest : storedRequests) { if (storedRequest instanceof PermissionSyncRequest && storedRequest.isUserConsented()) { if (storedRequest instanceof PermissionSyncRequest && storedRequest.isUserConsented()) { hasConsented = true; break; } Loading @@ -189,9 +203,11 @@ public class SystemDataTransferProcessor { Slog.e(LOG_TAG, message); try { callback.onError(message); } catch (RemoteException ignored) { } } catch (RemoteException ignored) { } return; } } // Start permission sync final long callingIdentityToken = Binder.clearCallingIdentity(); Loading
services/tests/servicestests/src/com/android/server/companion/utils/OWNERS 0 → 100644 +1 −0 Original line number Diff line number Diff line include /services/companion/java/com/android/server/companion/OWNERS No newline at end of file