Merge "Allow skipping CDM dialog for same-oem" into sc-dev
This commit is contained in:
@@ -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() {}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user