Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cbcb4a66 authored by Evan Chen's avatar Evan Chen Committed by Automerger Merge Worker
Browse files

Merge "Allow skipping CDM dialog for same-oem" into sc-dev am: ad6ef1c7

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13809078

Change-Id: Id455e4f1a9a01a948ec253b7c0d8d4fd5c127a29
parents d71cc2d1 ad6ef1c7
Loading
Loading
Loading
Loading
+45 −6
Original line number Diff line number Diff line
@@ -123,6 +123,15 @@ public final class AssociationRequest implements Parcelable {
     */
    private long mCreationTime;

    /**
     * Whether the user-prompt may be skipped once the device is found.
     *
     * Populated by the system.
     *
     * @hide
     */
    private boolean mSkipPrompt = false;

    private void onConstructed() {
        mCreationTime = System.currentTimeMillis();
    }
@@ -137,6 +146,11 @@ public final class AssociationRequest implements Parcelable {
        mDeviceProfilePrivilegesDescription = desc;
    }

    /** @hide */
    public void setSkipPrompt(boolean value) {
        mSkipPrompt = true;
    }

    /** @hide */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public boolean isSingleDevice() {
@@ -207,13 +221,14 @@ public final class AssociationRequest implements Parcelable {
            markUsed();
            return new AssociationRequest(
                    mSingleDevice, emptyIfNull(mDeviceFilters),
                    mDeviceProfile, null, null, -1L);
                    mDeviceProfile, null, null, -1L, false);
        }
    }





    // Code below generated by codegen v1.0.22.
    //
    // DO NOT MODIFY!
@@ -250,6 +265,10 @@ public final class AssociationRequest implements Parcelable {
     *   Populated by the system.
     * @param creationTime
     *   The time at which his request was created
     * @param skipPrompt
     *   Whether the user-prompt may be skipped once the device is found.
     *
     *   Populated by the system.
     * @hide
     */
    @DataClass.Generated.Member
@@ -259,7 +278,8 @@ public final class AssociationRequest implements Parcelable {
            @Nullable @DeviceProfile String deviceProfile,
            @Nullable String callingPackage,
            @Nullable String deviceProfilePrivilegesDescription,
            long creationTime) {
            long creationTime,
            boolean skipPrompt) {
        this.mSingleDevice = singleDevice;
        this.mDeviceFilters = deviceFilters;
        com.android.internal.util.AnnotationValidations.validate(
@@ -270,6 +290,7 @@ public final class AssociationRequest implements Parcelable {
        this.mCallingPackage = callingPackage;
        this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
        this.mCreationTime = creationTime;
        this.mSkipPrompt = skipPrompt;

        onConstructed();
    }
@@ -318,6 +339,18 @@ public final class AssociationRequest implements Parcelable {
        return mCreationTime;
    }

    /**
     * Whether the user-prompt may be skipped once the device is found.
     *
     * Populated by the system.
     *
     * @hide
     */
    @DataClass.Generated.Member
    public boolean isSkipPrompt() {
        return mSkipPrompt;
    }

    @Override
    @DataClass.Generated.Member
    public String toString() {
@@ -330,7 +363,8 @@ public final class AssociationRequest implements Parcelable {
                "deviceProfile = " + mDeviceProfile + ", " +
                "callingPackage = " + mCallingPackage + ", " +
                "deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription + ", " +
                "creationTime = " + mCreationTime +
                "creationTime = " + mCreationTime + ", " +
                "skipPrompt = " + mSkipPrompt +
        " }";
    }

@@ -352,7 +386,8 @@ public final class AssociationRequest implements Parcelable {
                && Objects.equals(mDeviceProfile, that.mDeviceProfile)
                && Objects.equals(mCallingPackage, that.mCallingPackage)
                && Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription)
                && mCreationTime == that.mCreationTime;
                && mCreationTime == that.mCreationTime
                && mSkipPrompt == that.mSkipPrompt;
    }

    @Override
@@ -368,6 +403,7 @@ public final class AssociationRequest implements Parcelable {
        _hash = 31 * _hash + Objects.hashCode(mCallingPackage);
        _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription);
        _hash = 31 * _hash + Long.hashCode(mCreationTime);
        _hash = 31 * _hash + Boolean.hashCode(mSkipPrompt);
        return _hash;
    }

@@ -379,6 +415,7 @@ public final class AssociationRequest implements Parcelable {

        byte flg = 0;
        if (mSingleDevice) flg |= 0x1;
        if (mSkipPrompt) flg |= 0x40;
        if (mDeviceProfile != null) flg |= 0x4;
        if (mCallingPackage != null) flg |= 0x8;
        if (mDeviceProfilePrivilegesDescription != null) flg |= 0x10;
@@ -403,6 +440,7 @@ public final class AssociationRequest implements Parcelable {

        byte flg = in.readByte();
        boolean singleDevice = (flg & 0x1) != 0;
        boolean skipPrompt = (flg & 0x40) != 0;
        List<DeviceFilter<?>> deviceFilters = new ArrayList<>();
        in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader());
        String deviceProfile = (flg & 0x4) == 0 ? null : in.readString();
@@ -420,6 +458,7 @@ public final class AssociationRequest implements Parcelable {
        this.mCallingPackage = callingPackage;
        this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
        this.mCreationTime = creationTime;
        this.mSkipPrompt = skipPrompt;

        onConstructed();
    }
@@ -439,10 +478,10 @@ public final class AssociationRequest implements Parcelable {
    };

    @DataClass.Generated(
            time = 1614976943652L,
            time = 1615252862756L,
            codegenVersion = "1.0.22",
            sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
            inputSignatures = "private static final  java.lang.String LOG_TAG\npublic static final  java.lang.String DEVICE_PROFILE_WATCH\nprivate  boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate  long mCreationTime\nprivate  void onConstructed()\npublic  void setCallingPackage(java.lang.String)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
            inputSignatures = "private static final  java.lang.String LOG_TAG\npublic static final  java.lang.String DEVICE_PROFILE_WATCH\nprivate  boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate  long mCreationTime\nprivate  boolean mSkipPrompt\nprivate  void onConstructed()\npublic  void setCallingPackage(java.lang.String)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic  void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
    @Deprecated
    private void __metadata() {}

+17 −0
Original line number Diff line number Diff line
@@ -3774,6 +3774,23 @@
    -->
    <string name="config_companionDeviceManagerPackage" translatable="false"></string>

    <!-- A list of packages managing companion device(s) by the same manufacturers as the main
         device. It will fall back to showing a prompt if the association has been called multiple
         times in a short period.
         Note that config_companionDeviceManagerPackage and config_companionDeviceCerts are
         parallel arrays.
     -->
    <string-array name="config_companionDevicePackages" translatable="false"></string-array>

    <!-- A list of SHA256 Certificates managing companion device(s) by the same manufacturers as
         the main device. It will fall back to showing a prompt if the association has been called
         multiple times in a short period.
         Note that config_companionDeviceCerts and config_companionDeviceManagerPackage are parallel
         arrays.
         Example: "1A:2B:3C:4D"
     -->
    <string-array name="config_companionDeviceCerts" translatable="false"></string-array>

    <!-- The package name for the default wellbeing app.
         This package must be trusted, as it has the permissions to control other applications
         on the device.
+2 −0
Original line number Diff line number Diff line
@@ -692,6 +692,8 @@
  <java-symbol type="string" name="cfTemplateRegisteredTime" />
  <java-symbol type="string" name="chooseActivity" />
  <java-symbol type="string" name="checked" />
  <java-symbol type="array" name="config_companionDevicePackages" />
  <java-symbol type="array" name="config_companionDeviceCerts" />
  <java-symbol type="string" name="config_default_dns_server" />
  <java-symbol type="string" name="config_ethernet_iface_regex" />
  <java-symbol type="string" name="not_checked" />
+3 −0
Original line number Diff line number Diff line
@@ -100,6 +100,9 @@ public class CompanionDeviceActivity extends Activity {
            mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice));
            getService().mSelectedDevice = selectedDevice;
            onSelectionUpdate();
            if (getRequest().isSkipPrompt()) {
                onDeviceConfirmed(selectedDevice);
            }
        } else {
            setContentView(R.layout.device_chooser);
            mPairButton = findViewById(R.id.button_pair);
+81 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
import static android.content.Context.BIND_IMPORTANT;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;

import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.CollectionUtils.emptyIfNull;
@@ -75,6 +76,7 @@ import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.net.NetworkPolicyManager;
import android.os.Binder;
@@ -101,6 +103,7 @@ import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.PackageUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -134,8 +137,10 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -164,6 +169,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
    private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
    private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";

    private static final int ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW = 5;
    private static final long ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS = 60 * 60 * 1000; // 60 min;

    private static final String XML_TAG_ASSOCIATIONS = "associations";
    private static final String XML_TAG_ASSOCIATION = "association";
    private static final String XML_ATTR_PACKAGE = "package";
@@ -418,6 +426,11 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
            mRequest = request;
            mCallingPackage = callingPackage;
            request.setCallingPackage(callingPackage);

            if (mayAssociateWithoutPrompt(callingPackage, userId)) {
                Slog.i(LOG_TAG, "setSkipPrompt(true)");
                request.setSkipPrompt(true);
            }
            callback.asBinder().linkToDeath(CompanionDeviceManagerService.this /* recipient */, 0);

            AndroidFuture<String> fetchProfileDescription =
@@ -503,7 +516,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
        private boolean callerCanManageCompanionDevices() {
            return getContext().checkCallingOrSelfPermission(
                    android.Manifest.permission.MANAGE_COMPANION_DEVICES)
                    == PackageManager.PERMISSION_GRANTED;
                    == PERMISSION_GRANTED;
        }

        private void checkCallerIsSystemOr(String pkg) throws RemoteException {
@@ -583,7 +596,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind

            boolean bypassMacPermission = getContext().getPackageManager().checkPermission(
                    android.Manifest.permission.COMPANION_APPROVE_WIFI_CONNECTIONS, packageName)
                    == PackageManager.PERMISSION_GRANTED;
                    == PERMISSION_GRANTED;
            if (bypassMacPermission) {
                return true;
            }
@@ -844,6 +857,72 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
        }
    }

    private Set<String> getSameOemPackageCerts(
            String packageName, String[] oemPackages, String[] sameOemCerts) {
        Set<String> sameOemPackageCerts = new HashSet<>();

        // Assume OEM may enter same package name in the parallel string array with
        // multiple ADK certs corresponding to it
        for (int i = 0; i < oemPackages.length; i++) {
            if (oemPackages[i].equals(packageName)) {
                sameOemPackageCerts.add(sameOemCerts[i].replaceAll(":", ""));
            }
        }

        return sameOemPackageCerts;
    }

    boolean mayAssociateWithoutPrompt(String packageName, int userId) {
        String[] sameOemPackages = getContext()
                .getResources()
                .getStringArray(com.android.internal.R.array.config_companionDevicePackages);
        if (!ArrayUtils.contains(sameOemPackages, packageName)) {
            Slog.w(LOG_TAG, packageName
                    + " can not silently create associations due to no package found."
                    + " Packages from OEM: " + Arrays.toString(sameOemPackages)
            );
            return false;
        }

        // Throttle frequent associations
        long now = System.currentTimeMillis();
        Set<Association> recentAssociations = filter(
                getAllAssociations(userId, packageName),
                a -> now - a.getTimeApprovedMs() < ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS);

        if (recentAssociations.size() >= ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW) {
            Slog.w(LOG_TAG, "Too many associations. " + packageName
                    + " already associated " + recentAssociations.size()
                    + " devices within the last " + ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS
                    + "ms: " + recentAssociations);
            return false;
        }
        String[] sameOemCerts = getContext()
                .getResources()
                .getStringArray(com.android.internal.R.array.config_companionDeviceCerts);

        Signature[] signatures = mPackageManagerInternal
                .getPackage(packageName).getSigningDetails().signatures;
        String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures);

        Set<String> sameOemPackageCerts =
                getSameOemPackageCerts(packageName, sameOemPackages, sameOemCerts);

        for (String cert : apkCerts) {
            if (sameOemPackageCerts.contains(cert)) {
                return true;
            }
        }

        Slog.w(LOG_TAG, packageName
                + " can not silently create associations. " + packageName
                + " has SHA256 certs from APK: " + Arrays.toString(apkCerts)
                + " and from OEM: " + Arrays.toString(sameOemCerts)
        );

        return false;
    }

    private static <T> boolean containsEither(T[] array, T a, T b) {
        return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
    }