diff options
| author | 2023-07-27 17:06:36 +0000 | |
|---|---|---|
| committer | 2023-07-27 17:06:36 +0000 | |
| commit | 33be01e8962bc937c5952838d80580a5c94e68b2 (patch) | |
| tree | 2c6d93ae84d7e2de5dfa4663eca458936de8f76e | |
| parent | b60c36e01dcff971128dfbdf83fce60d54555c2c (diff) | |
| parent | cf3e29bacb6f3235110484d1e11988bf67828e84 (diff) | |
Merge "[CDM perm sync] Skip user consent for the same OEM devices" into udc-qpr-dev
6 files changed, 250 insertions, 86 deletions
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java index 77275e056db6..4ace391ed696 100644 --- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java @@ -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; @@ -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. @@ -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 = @@ -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); } } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 611541f671cf..9d6283b85bd4 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -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); diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java index 3ab4aa89406b..db40fc495cab 100644 --- a/services/companion/java/com/android/server/companion/PackageUtils.java +++ b/services/companion/java/com/android/server/companion/PackageUtils.java @@ -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; @@ -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 = @@ -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; + } } diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java index dd7d38f34f28..800a3d9f6852 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java +++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java @@ -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; @@ -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; @@ -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; @@ -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; @@ -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 @@ -174,23 +184,29 @@ public class SystemDataTransferProcessor { final AssociationInfo association = resolveAssociation(packageName, userId, associationId); // Check if the request has been consented by the user. - List<SystemDataTransferRequest> storedRequests = - mSystemDataTransferRequestStore.readRequestsByAssociationId(userId, - associationId); - boolean hasConsented = false; - for (SystemDataTransferRequest storedRequest : storedRequests) { - if (storedRequest instanceof PermissionSyncRequest && storedRequest.isUserConsented()) { - hasConsented = true; - break; + 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()) { + hasConsented = true; + break; + } + } + if (!hasConsented) { + String message = "User " + userId + " hasn't consented permission sync."; + Slog.e(LOG_TAG, message); + try { + callback.onError(message); + } catch (RemoteException ignored) { + } + return; } - } - if (!hasConsented) { - String message = "User " + userId + " hasn't consented permission sync."; - Slog.e(LOG_TAG, message); - try { - callback.onError(message); - } catch (RemoteException ignored) { } - return; } // Start permission sync diff --git a/services/tests/servicestests/src/com/android/server/companion/utils/OWNERS b/services/tests/servicestests/src/com/android/server/companion/utils/OWNERS new file mode 100644 index 000000000000..008a53f6941f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/companion/utils/OWNERS @@ -0,0 +1 @@ +include /services/companion/java/com/android/server/companion/OWNERS
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java b/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java new file mode 100644 index 000000000000..56bb2ebeef33 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.companion.utils; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.content.ContextWrapper; +import android.content.pm.PackageManagerInternal; +import android.content.pm.Signature; +import android.content.pm.SigningDetails; +import android.content.res.Resources; +import android.platform.test.annotations.Presubmit; +import android.testing.AndroidTestingRunner; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.companion.PackageUtils; +import com.android.server.pm.pkg.AndroidPackage; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@RunWith(AndroidTestingRunner.class) +public class PackageUtilsTest { + + private static final String[] ALLOWED_PACKAGE_NAMES = new String[]{ + "allowed_app", + }; + + private static final Signature[] ALLOWED_PACKAGE_SIGNATURES = new Signature[]{ + new Signature("001122"), + }; + + private static final String[] DISALLOWED_PACKAGE_NAMES = new String[]{ + "disallowed_app", + }; + + private static final Signature[] DISALLOWED_PACKAGE_SIGNATURES = new Signature[]{ + new Signature("778899"), + }; + + @Test + public void isAllowlisted_true() { + Context context = spy( + new ContextWrapper( + InstrumentationRegistry.getInstrumentation().getTargetContext())); + final Resources res = spy(context.getResources()); + doReturn(ALLOWED_PACKAGE_NAMES).when(res).getStringArray( + com.android.internal.R.array.config_companionDevicePackages); + doReturn(android.util.PackageUtils.computeSignaturesSha256Digests( + ALLOWED_PACKAGE_SIGNATURES)).when(res).getStringArray( + com.android.internal.R.array.config_companionDeviceCerts); + doReturn(res).when(context).getResources(); + + PackageManagerInternal pm = mock(PackageManagerInternal.class); + AndroidPackage ap = mock(AndroidPackage.class); + SigningDetails sd = new SigningDetails( + ALLOWED_PACKAGE_SIGNATURES, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, + null, + null); + doReturn(ap).when(pm).getPackage(ALLOWED_PACKAGE_NAMES[0]); + doReturn(sd).when(ap).getSigningDetails(); + + assertTrue(PackageUtils.isPackageAllowlisted(context, pm, ALLOWED_PACKAGE_NAMES[0])); + } + + @Test + public void isAllowlisted_package_disallowed() { + Context context = spy( + new ContextWrapper( + InstrumentationRegistry.getInstrumentation().getTargetContext())); + final Resources res = spy(context.getResources()); + doReturn(ALLOWED_PACKAGE_NAMES).when(res).getStringArray( + com.android.internal.R.array.config_companionDevicePackages); + doReturn(android.util.PackageUtils.computeSignaturesSha256Digests( + ALLOWED_PACKAGE_SIGNATURES)).when(res).getStringArray( + com.android.internal.R.array.config_companionDeviceCerts); + doReturn(res).when(context).getResources(); + + PackageManagerInternal pm = mock(PackageManagerInternal.class); + AndroidPackage ap = mock(AndroidPackage.class); + SigningDetails sd = new SigningDetails( + ALLOWED_PACKAGE_SIGNATURES, // Giving the package a wrong signature + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, + null, + null); + doReturn(ap).when(pm).getPackage(DISALLOWED_PACKAGE_NAMES[0]); + doReturn(sd).when(ap).getSigningDetails(); + + assertFalse(PackageUtils.isPackageAllowlisted(context, pm, DISALLOWED_PACKAGE_NAMES[0])); + } + + @Test + public void isAllowlisted_signature_mismatch() { + Context context = spy( + new ContextWrapper( + InstrumentationRegistry.getInstrumentation().getTargetContext())); + final Resources res = spy(context.getResources()); + doReturn(ALLOWED_PACKAGE_NAMES).when(res).getStringArray( + com.android.internal.R.array.config_companionDevicePackages); + doReturn(android.util.PackageUtils.computeSignaturesSha256Digests( + ALLOWED_PACKAGE_SIGNATURES)).when(res).getStringArray( + com.android.internal.R.array.config_companionDeviceCerts); + doReturn(res).when(context).getResources(); + + PackageManagerInternal pm = mock(PackageManagerInternal.class); + AndroidPackage ap = mock(AndroidPackage.class); + SigningDetails sd = new SigningDetails( + DISALLOWED_PACKAGE_SIGNATURES, // Giving the package a wrong signature + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, + null, + null); + doReturn(ap).when(pm).getPackage(ALLOWED_PACKAGE_NAMES[0]); + doReturn(sd).when(ap).getSigningDetails(); + + assertFalse(PackageUtils.isPackageAllowlisted(context, pm, ALLOWED_PACKAGE_NAMES[0])); + } +} |