diff options
-rw-r--r-- | api/system-current.txt | 8 | ||||
-rw-r--r-- | api/test-current.txt | 8 | ||||
-rw-r--r-- | core/java/android/companion/CompanionDeviceManager.java | 37 | ||||
-rw-r--r-- | core/java/android/companion/ICompanionDeviceManager.aidl | 2 | ||||
-rw-r--r-- | core/java/com/android/internal/util/CollectionUtils.java | 40 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 2 | ||||
-rw-r--r-- | services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java | 10 |
7 files changed, 106 insertions, 1 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index e6a3e9bf3e8e..e525a71567c4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1370,6 +1370,14 @@ package android.bluetooth.le { } +package android.companion { + + public final class CompanionDeviceManager { + method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociated(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle); + } + +} + package android.content { public class ContentProviderClient implements java.lang.AutoCloseable { diff --git a/api/test-current.txt b/api/test-current.txt index a060dfcb8311..5247bd1fa16a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -640,6 +640,14 @@ package android.bluetooth { } +package android.companion { + + public final class CompanionDeviceManager { + method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociated(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle); + } + +} + package android.content { public final class AutofillOptions implements android.os.Parcelable { diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 9cb73f931773..28cc1f8fc107 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -21,7 +21,10 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.app.Activity; import android.app.Application; import android.app.PendingIntent; @@ -29,9 +32,11 @@ import android.content.ComponentName; import android.content.Context; import android.content.IntentSender; import android.content.pm.PackageManager; +import android.net.MacAddress; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.util.Log; @@ -252,6 +257,38 @@ public final class CompanionDeviceManager { } } + /** + * Check if a given package was {@link #associate associated} with a device with given + * mac address by given user. + * + * @param packageName the package to check for + * @param macAddress the mac address or BSSID of the device to check for + * @param user the user to check for + * @return whether a corresponding association record exists + * + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) + public boolean isDeviceAssociated( + @NonNull String packageName, + @NonNull MacAddress macAddress, + @NonNull UserHandle user) { + if (!checkFeaturePresent()) { + return false; + } + checkNotNull(packageName, "package name cannot be null"); + checkNotNull(macAddress, "mac address cannot be null"); + checkNotNull(user, "user cannot be null"); + try { + return mService.isDeviceAssociated( + packageName, macAddress.toString(), user.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private boolean checkFeaturePresent() { boolean featurePresent = mService != null; if (!featurePresent && DEBUG) { diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl index 561342efb3ba..2e1ff0be8577 100644 --- a/core/java/android/companion/ICompanionDeviceManager.aidl +++ b/core/java/android/companion/ICompanionDeviceManager.aidl @@ -39,4 +39,6 @@ interface ICompanionDeviceManager { boolean hasNotificationAccess(in ComponentName component); PendingIntent requestNotificationAccess(in ComponentName component); + + boolean isDeviceAssociated(in String packageName, in String macAddress, int userId); } diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java index f9cf23b29a69..4165f202998c 100644 --- a/core/java/com/android/internal/util/CollectionUtils.java +++ b/core/java/com/android/internal/util/CollectionUtils.java @@ -231,6 +231,15 @@ public class CollectionUtils { } /** + * Returns whether there exists at least one element in the set for which + * condition {@code predicate} is true + */ + public static <T> boolean any(@Nullable Set<T> items, + java.util.function.Predicate<T> predicate) { + return find(items, predicate) != null; + } + + /** * Returns the first element from the list for which * condition {@code predicate} is true, or null if there is no such element */ @@ -245,6 +254,37 @@ public class CollectionUtils { } /** + * Returns the first element from the set for which + * condition {@code predicate} is true, or null if there is no such element + */ + public static @Nullable <T> T find(@Nullable Set<T> cur, + java.util.function.Predicate<T> predicate) { + if (cur == null || predicate == null) return null; + int size = cur.size(); + if (size == 0) return null; + try { + if (cur instanceof ArraySet) { + ArraySet<T> arraySet = (ArraySet<T>) cur; + for (int i = 0; i < size; i++) { + T item = arraySet.valueAt(i); + if (predicate.test(item)) { + return item; + } + } + } else { + for (T t : cur) { + if (predicate.test(t)) { + return t; + } + } + } + } catch (Exception e) { + throw ExceptionUtils.propagate(e); + } + return null; + } + + /** * Similar to {@link List#add}, but with support for list values of {@code null} and * {@link Collections#emptyList} */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 11a506229654..8fa610240d5a 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3547,7 +3547,7 @@ <!-- Allows an application to manage the companion devices. @hide --> <permission android:name="android.permission.MANAGE_COMPANION_DEVICES" - android:protectionLevel="signature" /> + android:protectionLevel="signature|wifi" /> <!-- @SystemApi Allows an application to use SurfaceFlinger's low level features. <p>Not for use by third-party applications. diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 067becbf0c52..8f1e1568ead6 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -335,6 +335,16 @@ public class CompanionDeviceManagerService extends SystemService implements Bind return new ComponentNameSet(setting).contains(component); } + @Override + public boolean isDeviceAssociated(String packageName, String macAddress, int userId) { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.MANAGE_COMPANION_DEVICES, "isDeviceAssociated"); + + return CollectionUtils.any( + readAllAssociations(userId, packageName), + a -> Objects.equals(a.deviceAddress, macAddress)); + } + private void checkCanCallNotificationApi(String callingPackage) throws RemoteException { checkCallerIsSystemOr(callingPackage); int userId = getCallingUserId(); |