[Nearby Media Devices] Add a SystemApi to get information about nearby
media devices from external clients. Bug: 216313420 Test: CTS included in same topic Test: atest NearbyMediaDevicesManagerTest Change-Id: Ic5f26d3048a3475dbd232c913bb00bee46339b0a
This commit is contained in:
@@ -916,8 +916,10 @@ package android.app {
|
||||
public class StatusBarManager {
|
||||
method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
|
||||
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarModeOverride();
|
||||
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerNearbyMediaDevicesProvider(@NonNull android.media.NearbyMediaDevicesProvider);
|
||||
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
|
||||
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setNavBarModeOverride(int);
|
||||
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void unregisterNearbyMediaDevicesProvider(@NonNull android.media.NearbyMediaDevicesProvider);
|
||||
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferReceiverDisplay(int, @NonNull android.media.MediaRoute2Info, @Nullable android.graphics.drawable.Icon, @Nullable CharSequence);
|
||||
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferSenderDisplay(int, @NonNull android.media.MediaRoute2Info, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
|
||||
field public static final int MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER = 0; // 0x0
|
||||
@@ -6251,6 +6253,26 @@ package android.media {
|
||||
field public static final int SYNC_EVENT_SHARE_AUDIO_HISTORY = 100; // 0x64
|
||||
}
|
||||
|
||||
public final class NearbyDevice implements android.os.Parcelable {
|
||||
ctor public NearbyDevice(@NonNull String, int);
|
||||
method public int describeContents();
|
||||
method @NonNull public String getMediaRoute2Id();
|
||||
method public int getRangeZone();
|
||||
method @NonNull public static String rangeZoneToString(int);
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.media.NearbyDevice> CREATOR;
|
||||
field public static final int RANGE_CLOSE = 3; // 0x3
|
||||
field public static final int RANGE_FAR = 1; // 0x1
|
||||
field public static final int RANGE_LONG = 2; // 0x2
|
||||
field public static final int RANGE_UNKNOWN = 0; // 0x0
|
||||
field public static final int RANGE_WITHIN_REACH = 4; // 0x4
|
||||
}
|
||||
|
||||
public interface NearbyMediaDevicesProvider {
|
||||
method public void registerNearbyDevicesCallback(@NonNull java.util.function.Consumer<java.util.List<android.media.NearbyDevice>>);
|
||||
method public void unregisterNearbyDevicesCallback(@NonNull java.util.function.Consumer<java.util.List<android.media.NearbyDevice>>);
|
||||
}
|
||||
|
||||
public class PlayerProxy {
|
||||
method public void pause();
|
||||
method public void setPan(float);
|
||||
|
||||
@@ -28,7 +28,11 @@ import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.media.INearbyMediaDevicesProvider;
|
||||
import android.media.INearbyMediaDevicesUpdateCallback;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.NearbyDevice;
|
||||
import android.media.NearbyMediaDevicesProvider;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -46,6 +50,9 @@ import com.android.internal.statusbar.NotificationVisibility;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -503,6 +510,16 @@ public class StatusBarManager {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface MediaTransferReceiverState {}
|
||||
|
||||
/**
|
||||
* A map from a provider registered in
|
||||
* {@link #registerNearbyMediaDevicesProvider(NearbyMediaDevicesProvider)} to the wrapper
|
||||
* around the provider that was created internally. We need the wrapper to make the provider
|
||||
* binder-compatible, and we need to store a reference to the wrapper so that when the provider
|
||||
* is un-registered, we un-register the saved wrapper instance.
|
||||
*/
|
||||
private final Map<NearbyMediaDevicesProvider, NearbyMediaDevicesProviderWrapper>
|
||||
nearbyMediaDevicesProviderMap = new HashMap<>();
|
||||
|
||||
@UnsupportedAppUsage
|
||||
private Context mContext;
|
||||
private IStatusBarService mService;
|
||||
@@ -1033,6 +1050,67 @@ public class StatusBarManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a provider that notifies callbacks about the status of nearby devices that are able
|
||||
* to play media.
|
||||
* <p>
|
||||
* If multiple providers are registered, all the providers will be used for nearby device
|
||||
* information.
|
||||
* <p>
|
||||
* @param provider the nearby device information provider to register
|
||||
* <p>
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)
|
||||
public void registerNearbyMediaDevicesProvider(
|
||||
@NonNull NearbyMediaDevicesProvider provider
|
||||
) {
|
||||
Objects.requireNonNull(provider);
|
||||
if (nearbyMediaDevicesProviderMap.containsKey(provider)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final IStatusBarService svc = getService();
|
||||
NearbyMediaDevicesProviderWrapper providerWrapper =
|
||||
new NearbyMediaDevicesProviderWrapper(provider);
|
||||
nearbyMediaDevicesProviderMap.put(provider, providerWrapper);
|
||||
svc.registerNearbyMediaDevicesProvider(providerWrapper);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a provider that gives information about nearby devices that are able to play
|
||||
* media.
|
||||
* <p>
|
||||
* See {@link registerNearbyMediaDevicesProvider}.
|
||||
* <p>
|
||||
* @param provider the nearby device information provider to unregister
|
||||
* <p>
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)
|
||||
public void unregisterNearbyMediaDevicesProvider(
|
||||
@NonNull NearbyMediaDevicesProvider provider
|
||||
) {
|
||||
Objects.requireNonNull(provider);
|
||||
if (!nearbyMediaDevicesProviderMap.containsKey(provider)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final IStatusBarService svc = getService();
|
||||
NearbyMediaDevicesProviderWrapper providerWrapper =
|
||||
nearbyMediaDevicesProviderMap.get(provider);
|
||||
nearbyMediaDevicesProviderMap.remove(provider);
|
||||
svc.unregisterNearbyMediaDevicesProvider(providerWrapper);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static String windowStateToString(int state) {
|
||||
if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
|
||||
@@ -1340,4 +1418,48 @@ public class StatusBarManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
static final class NearbyMediaDevicesProviderWrapper extends INearbyMediaDevicesProvider.Stub {
|
||||
@NonNull
|
||||
private final NearbyMediaDevicesProvider mProvider;
|
||||
// Because we're wrapping a {@link NearbyMediaDevicesProvider} in a binder-compatible
|
||||
// interface, we also need to wrap the callbacks that the provider receives. We use
|
||||
// this map to keep track of the original callback and the wrapper callback so that
|
||||
// unregistering the callback works correctly.
|
||||
@NonNull
|
||||
private final Map<INearbyMediaDevicesUpdateCallback, Consumer<List<NearbyDevice>>>
|
||||
mRegisteredCallbacks = new HashMap<>();
|
||||
|
||||
NearbyMediaDevicesProviderWrapper(@NonNull NearbyMediaDevicesProvider provider) {
|
||||
mProvider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerNearbyDevicesCallback(
|
||||
@NonNull INearbyMediaDevicesUpdateCallback callback) {
|
||||
Consumer<List<NearbyDevice>> callbackAsConsumer = nearbyDevices -> {
|
||||
try {
|
||||
callback.onDevicesUpdated(nearbyDevices);
|
||||
} catch (RemoteException ex) {
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
};
|
||||
|
||||
mRegisteredCallbacks.put(callback, callbackAsConsumer);
|
||||
mProvider.registerNearbyDevicesCallback(callbackAsConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterNearbyDevicesCallback(
|
||||
@NonNull INearbyMediaDevicesUpdateCallback callback) {
|
||||
if (!mRegisteredCallbacks.containsKey(callback)) {
|
||||
return;
|
||||
}
|
||||
mProvider.unregisterNearbyDevicesCallback(mRegisteredCallbacks.get(callback));
|
||||
mRegisteredCallbacks.remove(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
55
core/java/android/media/NearbyMediaDevicesProvider.java
Normal file
55
core/java/android/media/NearbyMediaDevicesProvider.java
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.app.StatusBarManager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* An interface that provides information about nearby devices that are able to play media.
|
||||
* <p>
|
||||
* External clients can implement this interface and pass it to the system via
|
||||
* {@link StatusBarManager#registerNearbyMediaDevicesProvider} to inform the system of nearby media
|
||||
* devices.
|
||||
* <p>
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public interface NearbyMediaDevicesProvider {
|
||||
/**
|
||||
* Registers a callback that should be notified each time nearby media device(s) change.
|
||||
* <p>
|
||||
* When a callback is newly registered, it should be immediately notified of the current nearby
|
||||
* media devices. Afterwards, the list of devices passed to the callback should always contain
|
||||
* the full set of nearby media devices any time you get an update. If a device is no longer
|
||||
* valid (went offline, e.g.) then it should be omitted from the list in the next update.
|
||||
* <p>
|
||||
* @param callback the callback that will consume updates to the nearby media devices.
|
||||
*/
|
||||
void registerNearbyDevicesCallback(@NonNull Consumer<List<NearbyDevice>> callback);
|
||||
|
||||
/**
|
||||
* Unregisters a callback. @see #registerNearbyDevicesCallback.
|
||||
* <p>
|
||||
* @param callback the callback to unregister.
|
||||
*/
|
||||
void unregisterNearbyDevicesCallback(@NonNull Consumer<List<NearbyDevice>> callback);
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import android.hardware.biometrics.IBiometricContextListener;
|
||||
import android.hardware.biometrics.IBiometricSysuiReceiver;
|
||||
import android.hardware.biometrics.PromptInfo;
|
||||
import android.hardware.fingerprint.IUdfpsHbmListener;
|
||||
import android.media.INearbyMediaDevicesProvider;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
@@ -311,4 +312,10 @@ oneway interface IStatusBar
|
||||
in MediaRoute2Info routeInfo,
|
||||
in Icon appIcon,
|
||||
in CharSequence appName);
|
||||
|
||||
/** Registers a nearby media devices provider. */
|
||||
void registerNearbyMediaDevicesProvider(in INearbyMediaDevicesProvider provider);
|
||||
|
||||
/** Unregisters a nearby media devices provider. */
|
||||
void unregisterNearbyMediaDevicesProvider(in INearbyMediaDevicesProvider provider);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.hardware.biometrics.IBiometricContextListener;
|
||||
import android.hardware.biometrics.IBiometricSysuiReceiver;
|
||||
import android.hardware.biometrics.PromptInfo;
|
||||
import android.hardware.fingerprint.IUdfpsHbmListener;
|
||||
import android.media.INearbyMediaDevicesProvider;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@@ -211,4 +212,10 @@ interface IStatusBarService
|
||||
in MediaRoute2Info routeInfo,
|
||||
in Icon appIcon,
|
||||
in CharSequence appName);
|
||||
|
||||
/** Registers a nearby media devices provider. */
|
||||
void registerNearbyMediaDevicesProvider(in INearbyMediaDevicesProvider provider);
|
||||
|
||||
/** Unregisters a nearby media devices provider. */
|
||||
void unregisterNearbyMediaDevicesProvider(in INearbyMediaDevicesProvider provider);
|
||||
}
|
||||
|
||||
@@ -14,25 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.shared.media;
|
||||
package android.media;
|
||||
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback;
|
||||
import com.android.systemui.shared.media.NearbyDevice;
|
||||
import android.media.INearbyMediaDevicesUpdateCallback;
|
||||
import android.media.NearbyDevice;
|
||||
|
||||
/**
|
||||
* An interface that provides information about nearby devices that are able to play media.
|
||||
* A binder-compatible version of {@link android.media.NearbyMediaDevicesProvider}. See that class
|
||||
* for more information.
|
||||
*
|
||||
* External clients will implement this interface and System UI will invoke it if it's passed to
|
||||
* SystemUI via {@link INearbyMediaDevicesService.registerProvider}.
|
||||
* @hide
|
||||
*/
|
||||
interface INearbyMediaDevicesProvider {
|
||||
/**
|
||||
* Returns a list of nearby devices that are able to play media.
|
||||
*/
|
||||
List<NearbyDevice> getCurrentNearbyDevices() = 1;
|
||||
|
||||
/**
|
||||
* Registers a callback that will be notified each time the status of a nearby device changes.
|
||||
* Registers a callback that should be notified each time nearby media device(s) change.
|
||||
*/
|
||||
oneway void registerNearbyDevicesCallback(in INearbyMediaDevicesUpdateCallback callback) = 2;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.media.NearbyDevice;
|
||||
|
||||
/**
|
||||
* A callback used to receive updates about the status of nearby devices that are able to play
|
||||
* media.
|
||||
*
|
||||
* External clients may allow registration of these callbacks and external clients will be
|
||||
* responsible for notifying the callbacks appropriately.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface INearbyMediaDevicesUpdateCallback {
|
||||
/**
|
||||
* Invoked by external clients when changes in nearby media device(s) are detected.
|
||||
*
|
||||
* When a callback is newly registered, it should be immediately notified of the current nearby
|
||||
* media devices. Afterwards, the list of devices passed to the callback should always contain
|
||||
* the full set of nearby media devices any time you get an update. If a device is no longer
|
||||
* valid (went offline, e.g.) then it should be omitted from the list in the next update.
|
||||
*
|
||||
* @param nearbyDevices the list of nearby devices that have changed status due to moving closer
|
||||
* or further away.
|
||||
*/
|
||||
void onDevicesUpdated(in List<NearbyDevice> nearbyDevices);
|
||||
}
|
||||
@@ -14,6 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.shared.media;
|
||||
package android.media;
|
||||
|
||||
parcelable NearbyDevice;
|
||||
161
media/java/android/media/NearbyDevice.java
Normal file
161
media/java/android/media/NearbyDevice.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* A parcelable representing a nearby device that can be used for media transfer.
|
||||
* <p>
|
||||
* This class includes:
|
||||
* <ul>
|
||||
* <li>an ID identifying the media route.</li>
|
||||
* <li>a range zone specifying how far away this device is from the device with the media route.
|
||||
* </li>
|
||||
* </ul>
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class NearbyDevice implements Parcelable {
|
||||
/**
|
||||
* Unknown distance range.
|
||||
*/
|
||||
public static final int RANGE_UNKNOWN = 0;
|
||||
|
||||
/**
|
||||
* Distance is very far away from the peer device.
|
||||
*/
|
||||
public static final int RANGE_FAR = 1;
|
||||
|
||||
/**
|
||||
* Distance is relatively long from the peer device, typically a few meters.
|
||||
*/
|
||||
public static final int RANGE_LONG = 2;
|
||||
|
||||
/**
|
||||
* Distance is close to the peer device, typically with one or two meter.
|
||||
*/
|
||||
public static final int RANGE_CLOSE = 3;
|
||||
|
||||
/**
|
||||
* Distance is very close to the peer device, typically within one meter or less.
|
||||
*/
|
||||
public static final int RANGE_WITHIN_REACH = 4;
|
||||
|
||||
/**
|
||||
* The various range zones a device can be in, in relation to the current device.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@IntDef(prefix = { "RANGE_" }, value = {
|
||||
RANGE_UNKNOWN,
|
||||
RANGE_FAR,
|
||||
RANGE_LONG,
|
||||
RANGE_CLOSE,
|
||||
RANGE_WITHIN_REACH
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface RangeZone {}
|
||||
|
||||
/**
|
||||
* Gets a human-readable string of the range zone.
|
||||
*/
|
||||
@NonNull
|
||||
public static String rangeZoneToString(@RangeZone int rangeZone) {
|
||||
switch (rangeZone) {
|
||||
case RANGE_UNKNOWN:
|
||||
return "UNKNOWN";
|
||||
case RANGE_FAR:
|
||||
return "FAR";
|
||||
case RANGE_LONG:
|
||||
return "LONG";
|
||||
case RANGE_CLOSE:
|
||||
return "CLOSE";
|
||||
case RANGE_WITHIN_REACH:
|
||||
return "WITHIN_REACH";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull private final String mMediaRoute2Id;
|
||||
@RangeZone private final int mRangeZone;
|
||||
|
||||
public NearbyDevice(@NonNull String mediaRoute2Id, int rangeZone) {
|
||||
mMediaRoute2Id = mediaRoute2Id;
|
||||
mRangeZone = rangeZone;
|
||||
}
|
||||
|
||||
private NearbyDevice(@NonNull Parcel in) {
|
||||
mMediaRoute2Id = in.readString();
|
||||
mRangeZone = in.readInt();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<NearbyDevice> CREATOR = new Creator<NearbyDevice>() {
|
||||
@Override
|
||||
public NearbyDevice createFromParcel(@NonNull Parcel in) {
|
||||
return new NearbyDevice(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NearbyDevice[] newArray(int size) {
|
||||
return new NearbyDevice[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NearbyDevice{mediaRoute2Id=" + mMediaRoute2Id
|
||||
+ " rangeZone=" + rangeZoneToString(mRangeZone) + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeString8(mMediaRoute2Id);
|
||||
dest.writeInt(mRangeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the media route associated with the device.
|
||||
*
|
||||
* @see MediaRoute2Info#getId
|
||||
*/
|
||||
@NonNull
|
||||
public String getMediaRoute2Id() {
|
||||
return mMediaRoute2Id;
|
||||
}
|
||||
|
||||
/** Returns the range that the device is currently in. */
|
||||
@RangeZone
|
||||
public int getRangeZone() {
|
||||
return mRangeZone;
|
||||
}
|
||||
}
|
||||
@@ -886,12 +886,6 @@
|
||||
android:singleUser="true"
|
||||
android:permission="android.permission.BIND_DREAM_SERVICE" />
|
||||
|
||||
<!-- Service for external clients to notify us of nearby media devices -->
|
||||
<!-- TODO(b/216313420): Export and guard with a permission. -->
|
||||
<service
|
||||
android:name=".media.nearby.NearbyMediaDevicesService"
|
||||
/>
|
||||
|
||||
<receiver
|
||||
android:name=".tuner.TunerService$ClearReceiver"
|
||||
android:exported="false">
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.shared.media;
|
||||
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesProvider;
|
||||
|
||||
/**
|
||||
* An interface that can be invoked to notify System UI of nearby media devices.
|
||||
*
|
||||
* External clients wanting to notify System UI about the status of nearby media devices should
|
||||
* implement {@link INearbyMediaDevicesProvider} and then register it with system UI using this
|
||||
* service.
|
||||
*
|
||||
* System UI will implement this interface and external clients will invoke it.
|
||||
*/
|
||||
interface INearbyMediaDevicesService {
|
||||
/** Registers a new provider. */
|
||||
oneway void registerProvider(INearbyMediaDevicesProvider provider) = 1;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.shared.media;
|
||||
|
||||
/**
|
||||
* A callback used to notify implementors of changes in the status of nearby devices that are able
|
||||
* to play media.
|
||||
*
|
||||
* External clients may allow registration of these callbacks and external clients will be
|
||||
* responsible for notifying the callbacks appropriately. System UI is only a mediator between the
|
||||
* external client and these callbacks.
|
||||
*/
|
||||
interface INearbyMediaDevicesUpdateCallback {
|
||||
/** Unknown distance range. */
|
||||
const int RANGE_UNKNOWN = 0;
|
||||
/** Distance is very far away from the peer device. */
|
||||
const int RANGE_FAR = 1;
|
||||
/** Distance is relatively long from the peer device, typically a few meters. */
|
||||
const int RANGE_LONG = 2;
|
||||
/** Distance is close to the peer device, typically with one or two meter. */
|
||||
const int RANGE_CLOSE = 3;
|
||||
/** Distance is very close to the peer device, typically within one meter or less. */
|
||||
const int RANGE_WITHIN_REACH = 4;
|
||||
|
||||
/** Invoked by external clients when media device changes are detected. */
|
||||
oneway void nearbyDeviceUpdate(in String routeId, in int rangeZone) = 1;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.shared.media
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
/**
|
||||
* A parcelable representing a nearby device that can be used for media transfer.
|
||||
*
|
||||
* This class includes:
|
||||
* - [routeId] identifying the media route
|
||||
* - [rangeZone] specifying how far away the device with the media route is from this device.
|
||||
*/
|
||||
class NearbyDevice(
|
||||
val routeId: String?,
|
||||
@RangeZone val rangeZone: Int
|
||||
) : Parcelable {
|
||||
|
||||
private constructor(parcel: Parcel) : this(
|
||||
routeId = parcel.readString() ?: null,
|
||||
rangeZone = parcel.readInt()
|
||||
)
|
||||
|
||||
override fun describeContents() = 0
|
||||
|
||||
override fun writeToParcel(out: Parcel, flags: Int) {
|
||||
out.writeString(routeId)
|
||||
out.writeInt(rangeZone)
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<NearbyDevice?> {
|
||||
override fun createFromParcel(parcel: Parcel) = NearbyDevice(parcel)
|
||||
override fun newArray(size: Int) = arrayOfNulls<NearbyDevice?>(size)
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.shared.media
|
||||
|
||||
import androidx.annotation.IntDef
|
||||
import kotlin.annotation.AnnotationRetention
|
||||
|
||||
@IntDef(
|
||||
INearbyMediaDevicesUpdateCallback.RANGE_UNKNOWN,
|
||||
INearbyMediaDevicesUpdateCallback.RANGE_FAR,
|
||||
INearbyMediaDevicesUpdateCallback.RANGE_LONG,
|
||||
INearbyMediaDevicesUpdateCallback.RANGE_CLOSE,
|
||||
INearbyMediaDevicesUpdateCallback.RANGE_WITHIN_REACH
|
||||
)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
/** The various range zones a device can be in, in relation to the current device. */
|
||||
annotation class RangeZone
|
||||
@@ -26,6 +26,7 @@ import com.android.systemui.dagger.qualifiers.PerUser;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.keyguard.KeyguardSliceProvider;
|
||||
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
|
||||
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
|
||||
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
|
||||
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
|
||||
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
|
||||
@@ -152,6 +153,7 @@ public interface SysUIComponent {
|
||||
getMediaTttChipControllerReceiver();
|
||||
getMediaTttCommandLineHelper();
|
||||
getMediaMuteAwaitConnectionCli();
|
||||
getNearbyMediaDevicesManager();
|
||||
getUnfoldLatencyTracker().init();
|
||||
getFoldStateLoggingProvider().ifPresent(FoldStateLoggingProvider::init);
|
||||
getFoldStateLogger().ifPresent(FoldStateLogger::init);
|
||||
@@ -231,6 +233,9 @@ public interface SysUIComponent {
|
||||
/** */
|
||||
Optional<MediaMuteAwaitConnectionCli> getMediaMuteAwaitConnectionCli();
|
||||
|
||||
/** */
|
||||
Optional<NearbyMediaDevicesManager> getNearbyMediaDevicesManager();
|
||||
|
||||
/**
|
||||
* Returns {@link CoreStartable}s that should be started with the application.
|
||||
*/
|
||||
|
||||
@@ -146,6 +146,7 @@ public class Flags {
|
||||
public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
|
||||
public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true);
|
||||
public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, false);
|
||||
public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
|
||||
public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
|
||||
|
||||
// 1000 - dock
|
||||
|
||||
@@ -42,4 +42,10 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) {
|
||||
* Check whether we support displaying information about mute await connections.
|
||||
*/
|
||||
fun areMuteAwaitConnectionsEnabled() = featureFlags.isEnabled(Flags.MEDIA_MUTE_AWAIT)
|
||||
|
||||
/**
|
||||
* Check whether we enable support for nearby media devices. See
|
||||
* [android.app.StatusBarManager.registerNearbyMediaDevicesProvider] for more information.
|
||||
*/
|
||||
fun areNearbyMediaDevicesEnabled() = featureFlags.isEnabled(Flags.MEDIA_NEARBY_DEVICES)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.systemui.media.dagger;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.view.WindowManager;
|
||||
@@ -30,7 +29,7 @@ import com.android.systemui.media.MediaHost;
|
||||
import com.android.systemui.media.MediaHostStatesManager;
|
||||
import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
|
||||
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
|
||||
import com.android.systemui.media.nearby.NearbyMediaDevicesService;
|
||||
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
|
||||
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
|
||||
import com.android.systemui.media.taptotransfer.MediaTttFlags;
|
||||
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
|
||||
@@ -43,11 +42,9 @@ import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
||||
import dagger.Binds;
|
||||
import dagger.Lazy;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.ClassKey;
|
||||
import dagger.multibindings.IntoMap;
|
||||
|
||||
/** Dagger module for the media package. */
|
||||
@Module(subcomponents = {
|
||||
@@ -159,9 +156,15 @@ public interface MediaModule {
|
||||
return Optional.of(new MediaMuteAwaitConnectionCli(commandRegistry, context));
|
||||
}
|
||||
|
||||
/** Inject into NearbyMediaDevicesService. */
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(NearbyMediaDevicesService.class)
|
||||
Service bindMediaNearbyDevicesService(NearbyMediaDevicesService service);
|
||||
/** */
|
||||
@Provides
|
||||
@SysUISingleton
|
||||
static Optional<NearbyMediaDevicesManager> providesNearbyMediaDevicesManager(
|
||||
MediaFlags mediaFlags,
|
||||
Lazy<NearbyMediaDevicesManager> nearbyMediaDevicesManagerLazy) {
|
||||
if (!mediaFlags.areNearbyMediaDevicesEnabled()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(nearbyMediaDevicesManagerLazy.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.media.nearby
|
||||
|
||||
import android.media.INearbyMediaDevicesProvider
|
||||
import android.media.INearbyMediaDevicesUpdateCallback
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import android.os.IBinder
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A service that acts as a bridge between (1) external clients that have data on nearby devices
|
||||
* that are able to play media and (2) internal clients (like media Output Switcher) that need data
|
||||
* on these nearby devices.
|
||||
*
|
||||
* TODO(b/216313420): Add logging to this class.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class NearbyMediaDevicesManager @Inject constructor(
|
||||
commandQueue: CommandQueue
|
||||
) {
|
||||
private var providers: MutableList<INearbyMediaDevicesProvider> = mutableListOf()
|
||||
private var activeCallbacks: MutableList<INearbyMediaDevicesUpdateCallback> = mutableListOf()
|
||||
|
||||
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
|
||||
override fun registerNearbyMediaDevicesProvider(newProvider: INearbyMediaDevicesProvider) {
|
||||
if (providers.contains(newProvider)) {
|
||||
return
|
||||
}
|
||||
activeCallbacks.forEach {
|
||||
newProvider.registerNearbyDevicesCallback(it)
|
||||
}
|
||||
providers.add(newProvider)
|
||||
newProvider.asBinder().linkToDeath(deathRecipient, /* flags= */ 0)
|
||||
}
|
||||
|
||||
override fun unregisterNearbyMediaDevicesProvider(
|
||||
newProvider: INearbyMediaDevicesProvider
|
||||
) {
|
||||
providers.remove(newProvider)
|
||||
}
|
||||
}
|
||||
|
||||
private val deathRecipient = object : IBinder.DeathRecipient {
|
||||
override fun binderDied() {
|
||||
// Should not be used as binderDied(IBinder who) is overridden.
|
||||
}
|
||||
|
||||
override fun binderDied(who: IBinder) {
|
||||
binderDiedInternal(who)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commandQueue.addCallback(commandQueueCallbacks)
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers [callback] to be notified each time a device's range changes or when a new device
|
||||
* comes within range.
|
||||
*
|
||||
* If a new provider is added, previously-registered callbacks will be registered with the
|
||||
* new provider.
|
||||
*/
|
||||
fun registerNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) {
|
||||
providers.forEach {
|
||||
it.registerNearbyDevicesCallback(callback)
|
||||
}
|
||||
activeCallbacks.add(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-registers [callback]. See [registerNearbyDevicesCallback].
|
||||
*/
|
||||
fun unregisterNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) {
|
||||
activeCallbacks.remove(callback)
|
||||
providers.forEach {
|
||||
it.unregisterNearbyDevicesCallback(callback)
|
||||
}
|
||||
}
|
||||
|
||||
private fun binderDiedInternal(who: IBinder) {
|
||||
synchronized(providers) {
|
||||
for (i in providers.size - 1 downTo 0) {
|
||||
if (providers[i].asBinder() == who) {
|
||||
providers.removeAt(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.media.nearby
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesProvider
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesService
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback
|
||||
import com.android.systemui.shared.media.NearbyDevice
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A service that acts as a bridge between (1) external clients that have data on nearby devices
|
||||
* that are able to play media and (2) internal clients (like media Output Switcher) that need data
|
||||
* on these nearby devices.
|
||||
*
|
||||
* TODO(b/216313420): Add logging to this class.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class NearbyMediaDevicesService @Inject constructor() : Service() {
|
||||
|
||||
private var provider: INearbyMediaDevicesProvider? = null
|
||||
|
||||
private val binder: IBinder = object : INearbyMediaDevicesService.Stub() {
|
||||
override fun registerProvider(newProvider: INearbyMediaDevicesProvider) {
|
||||
provider = newProvider
|
||||
newProvider.asBinder().linkToDeath(
|
||||
{
|
||||
// We might've gotten a new provider before the old provider died, so we only
|
||||
// need to clear our provider if the most recent provider died.
|
||||
if (provider == newProvider) {
|
||||
provider = null
|
||||
}
|
||||
},
|
||||
/* flags= */ 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder = binder
|
||||
|
||||
/** Returns a list containing the current nearby devices. */
|
||||
fun getCurrentNearbyDevices(): List<NearbyDevice> {
|
||||
val currentProvider = provider ?: return emptyList()
|
||||
return currentProvider.currentNearbyDevices
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers [callback] to be notified each time a device's range changes or when a new device
|
||||
* comes within range.
|
||||
*/
|
||||
fun registerNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) {
|
||||
val currentProvider = provider ?: return
|
||||
currentProvider.registerNearbyDevicesCallback(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-registers [callback]. See [registerNearbyDevicesCallback].
|
||||
*/
|
||||
fun unregisterNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) {
|
||||
val currentProvider = provider ?: return
|
||||
currentProvider.unregisterNearbyDevicesCallback(callback)
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import android.hardware.biometrics.PromptInfo;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.fingerprint.IUdfpsHbmListener;
|
||||
import android.inputmethodservice.InputMethodService.BackDispositionMode;
|
||||
import android.media.INearbyMediaDevicesProvider;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@@ -160,6 +161,8 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
private static final int MSG_SET_BIOMETRICS_LISTENER = 63 << MSG_SHIFT;
|
||||
private static final int MSG_MEDIA_TRANSFER_SENDER_STATE = 64 << MSG_SHIFT;
|
||||
private static final int MSG_MEDIA_TRANSFER_RECEIVER_STATE = 65 << MSG_SHIFT;
|
||||
private static final int MSG_REGISTER_NEARBY_MEDIA_DEVICE_PROVIDER = 66 << MSG_SHIFT;
|
||||
private static final int MSG_UNREGISTER_NEARBY_MEDIA_DEVICE_PROVIDER = 67 << MSG_SHIFT;
|
||||
|
||||
public static final int FLAG_EXCLUDE_NONE = 0;
|
||||
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
|
||||
@@ -456,6 +459,18 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
@NonNull MediaRoute2Info routeInfo,
|
||||
@Nullable Icon appIcon,
|
||||
@Nullable CharSequence appName) {}
|
||||
|
||||
/**
|
||||
* @see IStatusBar#registerNearbyMediaDevicesProvider
|
||||
*/
|
||||
default void registerNearbyMediaDevicesProvider(
|
||||
@NonNull INearbyMediaDevicesProvider provider) {}
|
||||
|
||||
/**
|
||||
* @see IStatusBar#unregisterNearbyMediaDevicesProvider
|
||||
*/
|
||||
default void unregisterNearbyMediaDevicesProvider(
|
||||
@NonNull INearbyMediaDevicesProvider provider) {}
|
||||
}
|
||||
|
||||
public CommandQueue(Context context) {
|
||||
@@ -1221,6 +1236,18 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
mHandler.obtainMessage(MSG_MEDIA_TRANSFER_RECEIVER_STATE, args).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerNearbyMediaDevicesProvider(@NonNull INearbyMediaDevicesProvider provider) {
|
||||
mHandler.obtainMessage(MSG_REGISTER_NEARBY_MEDIA_DEVICE_PROVIDER, provider).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterNearbyMediaDevicesProvider(
|
||||
@NonNull INearbyMediaDevicesProvider provider) {
|
||||
mHandler.obtainMessage(MSG_UNREGISTER_NEARBY_MEDIA_DEVICE_PROVIDER, provider)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
private final class H extends Handler {
|
||||
private H(Looper l) {
|
||||
super(l);
|
||||
@@ -1643,6 +1670,18 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
}
|
||||
args.recycle();
|
||||
break;
|
||||
case MSG_REGISTER_NEARBY_MEDIA_DEVICE_PROVIDER:
|
||||
INearbyMediaDevicesProvider provider = (INearbyMediaDevicesProvider) msg.obj;
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
mCallbacks.get(i).registerNearbyMediaDevicesProvider(provider);
|
||||
}
|
||||
break;
|
||||
case MSG_UNREGISTER_NEARBY_MEDIA_DEVICE_PROVIDER:
|
||||
provider = (INearbyMediaDevicesProvider) msg.obj;
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
mCallbacks.get(i).unregisterNearbyMediaDevicesProvider(provider);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.android.systemui.media.nearby
|
||||
|
||||
import android.media.INearbyMediaDevicesProvider
|
||||
import android.media.INearbyMediaDevicesUpdateCallback
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import android.media.NearbyDevice
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
class NearbyMediaDevicesManagerTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var manager: NearbyMediaDevicesManager
|
||||
@Mock
|
||||
private lateinit var commandQueue: CommandQueue
|
||||
private lateinit var commandQueueCallbacks: CommandQueue.Callbacks
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
manager = NearbyMediaDevicesManager(commandQueue)
|
||||
|
||||
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
||||
Mockito.verify(commandQueue).addCallback(callbackCaptor.capture())
|
||||
commandQueueCallbacks = callbackCaptor.value!!
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerNearbyDevicesCallback_noProviderRegistered_noCrash() {
|
||||
// No assert, just needs no crash
|
||||
manager.registerNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun onDevicesUpdated(nearbyDevices: List<NearbyDevice>) {}
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerNearbyDevicesCallback_providerRegistered_providerReceivesCallback() {
|
||||
val provider = TestProvider()
|
||||
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider)
|
||||
|
||||
val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun onDevicesUpdated(nearbyDevices: List<NearbyDevice>) {}
|
||||
}
|
||||
|
||||
manager.registerNearbyDevicesCallback(callback)
|
||||
|
||||
assertThat(provider.lastRegisteredCallback).isEqualTo(callback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerNearbyDevicesCallback_multipleProviders_allProvidersReceiveCallback() {
|
||||
val provider1 = TestProvider()
|
||||
val provider2 = TestProvider()
|
||||
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider1)
|
||||
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider2)
|
||||
|
||||
val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun onDevicesUpdated(nearbyDevices: List<NearbyDevice>) {}
|
||||
}
|
||||
|
||||
manager.registerNearbyDevicesCallback(callback)
|
||||
|
||||
assertThat(provider1.lastRegisteredCallback).isEqualTo(callback)
|
||||
assertThat(provider2.lastRegisteredCallback).isEqualTo(callback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unregisterNearbyDevicesCallback_noProviderRegistered_noCrash() {
|
||||
// No assert, just needs no crash
|
||||
manager.unregisterNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun onDevicesUpdated(nearbyDevices: List<NearbyDevice>) {}
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unregisterNearbyDevicesCallback_providerRegistered_providerReceivesCallback() {
|
||||
val provider = TestProvider()
|
||||
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider)
|
||||
|
||||
val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun onDevicesUpdated(nearbyDevices: List<NearbyDevice>) {}
|
||||
}
|
||||
|
||||
manager.unregisterNearbyDevicesCallback(callback)
|
||||
|
||||
assertThat(provider.lastUnregisteredCallback).isEqualTo(callback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unregisterNearbyDevicesCallback_multipleProviders_allProvidersReceiveCallback() {
|
||||
val provider1 = TestProvider()
|
||||
val provider2 = TestProvider()
|
||||
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider1)
|
||||
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider2)
|
||||
|
||||
val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun onDevicesUpdated(nearbyDevices: List<NearbyDevice>) {}
|
||||
}
|
||||
|
||||
manager.unregisterNearbyDevicesCallback(callback)
|
||||
|
||||
assertThat(provider1.lastUnregisteredCallback).isEqualTo(callback)
|
||||
assertThat(provider2.lastUnregisteredCallback).isEqualTo(callback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun newProviderRegisteredAfterCallbacksRegistered_providerGetsPreviouslyRegisteredCallbacks() {
|
||||
// Start off with an existing provider and callback
|
||||
val provider1 = TestProvider()
|
||||
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider1)
|
||||
val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun onDevicesUpdated(nearbyDevices: List<NearbyDevice>) {}
|
||||
}
|
||||
manager.registerNearbyDevicesCallback(callback)
|
||||
|
||||
// Add a new provider
|
||||
val provider2 = TestProvider()
|
||||
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider2)
|
||||
|
||||
// Verify the new provider received the previously-registered callbacks
|
||||
assertThat(provider2.lastRegisteredCallback).isEqualTo(callback)
|
||||
}
|
||||
|
||||
private class TestProvider : INearbyMediaDevicesProvider.Stub() {
|
||||
var lastRegisteredCallback: INearbyMediaDevicesUpdateCallback? = null
|
||||
var lastUnregisteredCallback: INearbyMediaDevicesUpdateCallback? = null
|
||||
override fun registerNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback
|
||||
) {
|
||||
lastRegisteredCallback = callback
|
||||
}
|
||||
|
||||
override fun unregisterNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback
|
||||
) {
|
||||
lastUnregisteredCallback = callback
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package com.android.systemui.media.nearby
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesProvider
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesService
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback.RANGE_LONG
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback.RANGE_WITHIN_REACH
|
||||
import com.android.systemui.shared.media.NearbyDevice
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
@SmallTest
|
||||
class NearbyMediaDevicesServiceTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var service: NearbyMediaDevicesService
|
||||
private lateinit var binderInterface: INearbyMediaDevicesService
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
service = NearbyMediaDevicesService()
|
||||
binderInterface = INearbyMediaDevicesService.Stub.asInterface(service.onBind(null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCurrentNearbyDevices_noProviderRegistered_returnsEmptyList() {
|
||||
assertThat(service.getCurrentNearbyDevices()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCurrentNearbyDevices_providerRegistered_returnsProviderInfo() {
|
||||
val nearbyDevice1 = NearbyDevice("routeId1", RANGE_LONG)
|
||||
val nearbyDevice2 = NearbyDevice("routeId2", RANGE_WITHIN_REACH)
|
||||
val provider = object : INearbyMediaDevicesProvider.Stub() {
|
||||
override fun getCurrentNearbyDevices(): List<NearbyDevice> {
|
||||
return listOf(nearbyDevice1, nearbyDevice2)
|
||||
}
|
||||
|
||||
override fun registerNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {}
|
||||
override fun unregisterNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {}
|
||||
}
|
||||
binderInterface.registerProvider(provider)
|
||||
|
||||
val returnedNearbyDevices = service.getCurrentNearbyDevices()
|
||||
|
||||
assertThat(returnedNearbyDevices).isEqualTo(listOf(nearbyDevice1, nearbyDevice2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerNearbyDevicesCallback_noProviderRegistered_noCrash() {
|
||||
// No assert, just needs no crash
|
||||
service.registerNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerNearbyDevicesCallback_providerRegistered_providerReceivesCallback() {
|
||||
val provider = object : INearbyMediaDevicesProvider.Stub() {
|
||||
var registeredCallback: INearbyMediaDevicesUpdateCallback? = null
|
||||
override fun getCurrentNearbyDevices(): List<NearbyDevice> = listOf()
|
||||
|
||||
override fun registerNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {
|
||||
registeredCallback = callback
|
||||
}
|
||||
|
||||
override fun unregisterNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {}
|
||||
}
|
||||
binderInterface.registerProvider(provider)
|
||||
|
||||
val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
|
||||
}
|
||||
|
||||
service.registerNearbyDevicesCallback(callback)
|
||||
|
||||
assertThat(provider.registeredCallback).isEqualTo(callback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unregisterNearbyDevicesCallback_noProviderRegistered_noCrash() {
|
||||
// No assert, just needs no crash
|
||||
service.unregisterNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unregisterNearbyDevicesCallback_providerRegistered_providerReceivesCallback() {
|
||||
val provider = object : INearbyMediaDevicesProvider.Stub() {
|
||||
var unregisteredCallback: INearbyMediaDevicesUpdateCallback? = null
|
||||
override fun getCurrentNearbyDevices(): List<NearbyDevice> = listOf()
|
||||
|
||||
override fun registerNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {}
|
||||
|
||||
override fun unregisterNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {
|
||||
unregisteredCallback = callback
|
||||
}
|
||||
}
|
||||
binderInterface.registerProvider(provider)
|
||||
|
||||
val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
|
||||
}
|
||||
|
||||
service.unregisterNearbyDevicesCallback(callback)
|
||||
|
||||
assertThat(provider.unregisteredCallback).isEqualTo(callback)
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ import android.hardware.biometrics.PromptInfo;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayManager.DisplayListener;
|
||||
import android.hardware.fingerprint.IUdfpsHbmListener;
|
||||
import android.media.INearbyMediaDevicesProvider;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
@@ -2045,6 +2046,56 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a provider that gives information about nearby devices that are able to play media.
|
||||
* See {@link StatusBarmanager.registerNearbyMediaDevicesProvider}.
|
||||
*
|
||||
* Requires the caller to have the {@link android.Manifest.permission.MEDIA_CONTENT_CONTROL}
|
||||
* permission.
|
||||
*
|
||||
* @param provider the nearby device information provider to register
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void registerNearbyMediaDevicesProvider(
|
||||
@NonNull INearbyMediaDevicesProvider provider
|
||||
) {
|
||||
enforceMediaContentControl();
|
||||
if (mBar != null) {
|
||||
try {
|
||||
mBar.registerNearbyMediaDevicesProvider(provider);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "registerNearbyMediaDevicesProvider", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a provider that gives information about nearby devices that are able to play
|
||||
* media. See {@link StatusBarmanager.unregisterNearbyMediaDevicesProvider}.
|
||||
*
|
||||
* Requires the caller to have the {@link android.Manifest.permission.MEDIA_CONTENT_CONTROL}
|
||||
* permission.
|
||||
*
|
||||
* @param provider the nearby device information provider to unregister
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void unregisterNearbyMediaDevicesProvider(
|
||||
@NonNull INearbyMediaDevicesProvider provider
|
||||
) {
|
||||
enforceMediaContentControl();
|
||||
if (mBar != null) {
|
||||
try {
|
||||
mBar.unregisterNearbyMediaDevicesProvider(provider);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "unregisterNearbyMediaDevicesProvider", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void passThroughShellCommand(String[] args, FileDescriptor fd) {
|
||||
enforceStatusBarOrShell();
|
||||
|
||||
Reference in New Issue
Block a user