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;
/**
* 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() {}

View File

@@ -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.

View File

@@ -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" />

View File

@@ -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);

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.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);
}