summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Eugene Susla <eugenesusla@google.com> 2020-12-17 14:26:56 -0800
committer Evan Chen <evanxinchen@google.com> 2021-05-20 22:22:54 +0000
commit8f935b73c317e1485778cf90cc207ed866a3f97f (patch)
tree85077e5aaac20bfa016563e376366ace29f2de42
parent42c7ce491e398d348d1817a639701f94d59e8248 (diff)
Allow skipping CDM dialog for same-oem
Used SHA256 from ADK to make the determination Test: Manual Bug: 165951651 Change-Id: I5ed42d1e67d5abaaaf24989b54dd8e95f04621e9 Merged-In: I5ed42d1e67d5abaaaf24989b54dd8e95f04621e9
-rw-r--r--core/java/android/companion/AssociationRequest.java51
-rw-r--r--core/res/res/values/config.xml17
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java3
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java83
5 files changed, 148 insertions, 8 deletions
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 7956be343bec..bb8fa9eb4007 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -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();
}
@@ -138,6 +147,11 @@ public final class AssociationRequest implements Parcelable {
}
/** @hide */
+ public void setSkipPrompt(boolean value) {
+ mSkipPrompt = true;
+ }
+
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isSingleDevice() {
return mSingleDevice;
@@ -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() {}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d3ea52eb8888..62d08d0164cb 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3727,6 +3727,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.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b9f1e203552a..28ca4df35c8a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -687,6 +687,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" />
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 91a6749ac42d..c1a0a9a92cc2 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -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);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b1eae9edc3d4..ab85b5e5cdf6 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -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);
}