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

This commit is contained in:
Evan Chen
2021-05-21 00:29:50 +00:00
committed by Android (Google) Code Review
5 changed files with 148 additions and 8 deletions

View File

@@ -123,6 +123,15 @@ public final class AssociationRequest implements Parcelable {
*/ */
private long mCreationTime; 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() { private void onConstructed() {
mCreationTime = System.currentTimeMillis(); mCreationTime = System.currentTimeMillis();
} }
@@ -137,6 +146,11 @@ public final class AssociationRequest implements Parcelable {
mDeviceProfilePrivilegesDescription = desc; mDeviceProfilePrivilegesDescription = desc;
} }
/** @hide */
public void setSkipPrompt(boolean value) {
mSkipPrompt = true;
}
/** @hide */ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isSingleDevice() { public boolean isSingleDevice() {
@@ -207,13 +221,14 @@ public final class AssociationRequest implements Parcelable {
markUsed(); markUsed();
return new AssociationRequest( return new AssociationRequest(
mSingleDevice, emptyIfNull(mDeviceFilters), mSingleDevice, emptyIfNull(mDeviceFilters),
mDeviceProfile, null, null, -1L); mDeviceProfile, null, null, -1L, false);
} }
} }
// Code below generated by codegen v1.0.22. // Code below generated by codegen v1.0.22.
// //
// DO NOT MODIFY! // DO NOT MODIFY!
@@ -250,6 +265,10 @@ public final class AssociationRequest implements Parcelable {
* Populated by the system. * Populated by the system.
* @param creationTime * @param creationTime
* The time at which his request was created * 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 * @hide
*/ */
@DataClass.Generated.Member @DataClass.Generated.Member
@@ -259,7 +278,8 @@ public final class AssociationRequest implements Parcelable {
@Nullable @DeviceProfile String deviceProfile, @Nullable @DeviceProfile String deviceProfile,
@Nullable String callingPackage, @Nullable String callingPackage,
@Nullable String deviceProfilePrivilegesDescription, @Nullable String deviceProfilePrivilegesDescription,
long creationTime) { long creationTime,
boolean skipPrompt) {
this.mSingleDevice = singleDevice; this.mSingleDevice = singleDevice;
this.mDeviceFilters = deviceFilters; this.mDeviceFilters = deviceFilters;
com.android.internal.util.AnnotationValidations.validate( com.android.internal.util.AnnotationValidations.validate(
@@ -270,6 +290,7 @@ public final class AssociationRequest implements Parcelable {
this.mCallingPackage = callingPackage; this.mCallingPackage = callingPackage;
this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription; this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
this.mCreationTime = creationTime; this.mCreationTime = creationTime;
this.mSkipPrompt = skipPrompt;
onConstructed(); onConstructed();
} }
@@ -318,6 +339,18 @@ public final class AssociationRequest implements Parcelable {
return mCreationTime; 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 @Override
@DataClass.Generated.Member @DataClass.Generated.Member
public String toString() { public String toString() {
@@ -330,7 +363,8 @@ public final class AssociationRequest implements Parcelable {
"deviceProfile = " + mDeviceProfile + ", " + "deviceProfile = " + mDeviceProfile + ", " +
"callingPackage = " + mCallingPackage + ", " + "callingPackage = " + mCallingPackage + ", " +
"deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription + ", " + "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(mDeviceProfile, that.mDeviceProfile)
&& Objects.equals(mCallingPackage, that.mCallingPackage) && Objects.equals(mCallingPackage, that.mCallingPackage)
&& Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription) && Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription)
&& mCreationTime == that.mCreationTime; && mCreationTime == that.mCreationTime
&& mSkipPrompt == that.mSkipPrompt;
} }
@Override @Override
@@ -368,6 +403,7 @@ public final class AssociationRequest implements Parcelable {
_hash = 31 * _hash + Objects.hashCode(mCallingPackage); _hash = 31 * _hash + Objects.hashCode(mCallingPackage);
_hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription); _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription);
_hash = 31 * _hash + Long.hashCode(mCreationTime); _hash = 31 * _hash + Long.hashCode(mCreationTime);
_hash = 31 * _hash + Boolean.hashCode(mSkipPrompt);
return _hash; return _hash;
} }
@@ -379,6 +415,7 @@ public final class AssociationRequest implements Parcelable {
byte flg = 0; byte flg = 0;
if (mSingleDevice) flg |= 0x1; if (mSingleDevice) flg |= 0x1;
if (mSkipPrompt) flg |= 0x40;
if (mDeviceProfile != null) flg |= 0x4; if (mDeviceProfile != null) flg |= 0x4;
if (mCallingPackage != null) flg |= 0x8; if (mCallingPackage != null) flg |= 0x8;
if (mDeviceProfilePrivilegesDescription != null) flg |= 0x10; if (mDeviceProfilePrivilegesDescription != null) flg |= 0x10;
@@ -403,6 +440,7 @@ public final class AssociationRequest implements Parcelable {
byte flg = in.readByte(); byte flg = in.readByte();
boolean singleDevice = (flg & 0x1) != 0; boolean singleDevice = (flg & 0x1) != 0;
boolean skipPrompt = (flg & 0x40) != 0;
List<DeviceFilter<?>> deviceFilters = new ArrayList<>(); List<DeviceFilter<?>> deviceFilters = new ArrayList<>();
in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader()); in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader());
String deviceProfile = (flg & 0x4) == 0 ? null : in.readString(); String deviceProfile = (flg & 0x4) == 0 ? null : in.readString();
@@ -420,6 +458,7 @@ public final class AssociationRequest implements Parcelable {
this.mCallingPackage = callingPackage; this.mCallingPackage = callingPackage;
this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription; this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
this.mCreationTime = creationTime; this.mCreationTime = creationTime;
this.mSkipPrompt = skipPrompt;
onConstructed(); onConstructed();
} }
@@ -439,10 +478,10 @@ public final class AssociationRequest implements Parcelable {
}; };
@DataClass.Generated( @DataClass.Generated(
time = 1614976943652L, time = 1615252862756L,
codegenVersion = "1.0.22", codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java", 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 @Deprecated
private void __metadata() {} private void __metadata() {}

View File

@@ -3774,6 +3774,23 @@
--> -->
<string name="config_companionDeviceManagerPackage" translatable="false"></string> <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. <!-- The package name for the default wellbeing app.
This package must be trusted, as it has the permissions to control other applications This package must be trusted, as it has the permissions to control other applications
on the device. on the device.

View File

@@ -692,6 +692,8 @@
<java-symbol type="string" name="cfTemplateRegisteredTime" /> <java-symbol type="string" name="cfTemplateRegisteredTime" />
<java-symbol type="string" name="chooseActivity" /> <java-symbol type="string" name="chooseActivity" />
<java-symbol type="string" name="checked" /> <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_default_dns_server" />
<java-symbol type="string" name="config_ethernet_iface_regex" /> <java-symbol type="string" name="config_ethernet_iface_regex" />
<java-symbol type="string" name="not_checked" /> <java-symbol type="string" name="not_checked" />

View File

@@ -100,6 +100,9 @@ public class CompanionDeviceActivity extends Activity {
mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice)); mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice));
getService().mSelectedDevice = selectedDevice; getService().mSelectedDevice = selectedDevice;
onSelectionUpdate(); onSelectionUpdate();
if (getRequest().isSkipPrompt()) {
onDeviceConfirmed(selectedDevice);
}
} else { } else {
setContentView(R.layout.device_chooser); setContentView(R.layout.device_chooser);
mPairButton = findViewById(R.id.button_pair); mPairButton = findViewById(R.id.button_pair);

View File

@@ -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.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
import static android.content.Context.BIND_IMPORTANT; import static android.content.Context.BIND_IMPORTANT;
import static android.content.pm.PackageManager.MATCH_ALL; 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.any;
import static com.android.internal.util.CollectionUtils.emptyIfNull; 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.PackageManager;
import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.net.NetworkPolicyManager; import android.net.NetworkPolicyManager;
import android.os.Binder; import android.os.Binder;
@@ -101,6 +103,7 @@ import android.util.ArraySet;
import android.util.AtomicFile; import android.util.AtomicFile;
import android.util.ExceptionUtils; import android.util.ExceptionUtils;
import android.util.Log; import android.util.Log;
import android.util.PackageUtils;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.Xml; import android.util.Xml;
@@ -134,8 +137,10 @@ import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; 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_FILE_NAME = "companion_device_preferences.xml";
private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done"; 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_ASSOCIATIONS = "associations";
private static final String XML_TAG_ASSOCIATION = "association"; private static final String XML_TAG_ASSOCIATION = "association";
private static final String XML_ATTR_PACKAGE = "package"; private static final String XML_ATTR_PACKAGE = "package";
@@ -418,6 +426,11 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
mRequest = request; mRequest = request;
mCallingPackage = callingPackage; mCallingPackage = callingPackage;
request.setCallingPackage(callingPackage); request.setCallingPackage(callingPackage);
if (mayAssociateWithoutPrompt(callingPackage, userId)) {
Slog.i(LOG_TAG, "setSkipPrompt(true)");
request.setSkipPrompt(true);
}
callback.asBinder().linkToDeath(CompanionDeviceManagerService.this /* recipient */, 0); callback.asBinder().linkToDeath(CompanionDeviceManagerService.this /* recipient */, 0);
AndroidFuture<String> fetchProfileDescription = AndroidFuture<String> fetchProfileDescription =
@@ -503,7 +516,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private boolean callerCanManageCompanionDevices() { private boolean callerCanManageCompanionDevices() {
return getContext().checkCallingOrSelfPermission( return getContext().checkCallingOrSelfPermission(
android.Manifest.permission.MANAGE_COMPANION_DEVICES) android.Manifest.permission.MANAGE_COMPANION_DEVICES)
== PackageManager.PERMISSION_GRANTED; == PERMISSION_GRANTED;
} }
private void checkCallerIsSystemOr(String pkg) throws RemoteException { private void checkCallerIsSystemOr(String pkg) throws RemoteException {
@@ -583,7 +596,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
boolean bypassMacPermission = getContext().getPackageManager().checkPermission( boolean bypassMacPermission = getContext().getPackageManager().checkPermission(
android.Manifest.permission.COMPANION_APPROVE_WIFI_CONNECTIONS, packageName) android.Manifest.permission.COMPANION_APPROVE_WIFI_CONNECTIONS, packageName)
== PackageManager.PERMISSION_GRANTED; == PERMISSION_GRANTED;
if (bypassMacPermission) { if (bypassMacPermission) {
return true; 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) { private static <T> boolean containsEither(T[] array, T a, T b) {
return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b); return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
} }