Merge "[Media ML] Add volume-related system APIs in AudioManager" am: 742b2816e5
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/12360923 Change-Id: I69ba062efffa194b7c896f9f9b4c4764212a6a79
This commit is contained in:
@@ -35,6 +35,9 @@ package android.graphics {
|
||||
package android.media {
|
||||
|
||||
public class AudioManager {
|
||||
method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
|
||||
method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
|
||||
method public void setStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
|
||||
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
|
||||
}
|
||||
|
||||
|
||||
@@ -6649,6 +6649,132 @@ public class AudioManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the volume of the most relevant stream, or the given fallback
|
||||
* stream.
|
||||
* <p>
|
||||
* This method should only be used by applications that replace the
|
||||
* platform-wide management of audio settings or the main telephony
|
||||
* application.
|
||||
* <p>
|
||||
* This method has no effect if the device implements a fixed volume policy
|
||||
* as indicated by {@link #isVolumeFixed()}.
|
||||
* <p>This API checks if the caller has the necessary permissions based on the provided
|
||||
* component name, uid, and pid values.
|
||||
* See {@link #adjustSuggestedStreamVolume(int, int, int)}.
|
||||
*
|
||||
* @param suggestedStreamType The stream type that will be used if there
|
||||
* isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is
|
||||
* valid here.
|
||||
* @param direction The direction to adjust the volume. One of
|
||||
* {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
|
||||
* {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
|
||||
* {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
|
||||
* @param flags One or more flags.
|
||||
* @param packageName the package name of client application
|
||||
* @param uid the uid of client application
|
||||
* @param pid the pid of client application
|
||||
* @param targetSdkVersion the target sdk version of client application
|
||||
* @see #adjustVolume(int, int)
|
||||
* @see #adjustStreamVolume(int, int, int)
|
||||
* @see #setStreamVolume(int, int, int)
|
||||
* @see #isVolumeFixed()
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
||||
public void adjustSuggestedStreamVolumeForUid(int suggestedStreamType, int direction, int flags,
|
||||
@NonNull String packageName, int uid, int pid, int targetSdkVersion) {
|
||||
try {
|
||||
getService().adjustSuggestedStreamVolumeForUid(suggestedStreamType, direction, flags,
|
||||
packageName, uid, pid, UserHandle.getUserHandleForUid(uid), targetSdkVersion);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the volume of a particular stream by one step in a direction.
|
||||
* <p>
|
||||
* This method should only be used by applications that replace the platform-wide
|
||||
* management of audio settings or the main telephony application.
|
||||
* <p>This method has no effect if the device implements a fixed volume policy
|
||||
* as indicated by {@link #isVolumeFixed()}.
|
||||
* <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
|
||||
* unless the app has been granted Do Not Disturb Access.
|
||||
* See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
|
||||
* <p>This API checks if the caller has the necessary permissions based on the provided
|
||||
* component name, uid, and pid values.
|
||||
* See {@link #adjustStreamVolume(int, int, int)}.
|
||||
*
|
||||
* @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL},
|
||||
* {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC},
|
||||
* {@link #STREAM_ALARM} or {@link #STREAM_ACCESSIBILITY}.
|
||||
* @param direction The direction to adjust the volume. One of
|
||||
* {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
|
||||
* {@link #ADJUST_SAME}.
|
||||
* @param flags One or more flags.
|
||||
* @param packageName the package name of client application
|
||||
* @param uid the uid of client application
|
||||
* @param pid the pid of client application
|
||||
* @param targetSdkVersion the target sdk version of client application
|
||||
* @see #adjustVolume(int, int)
|
||||
* @see #setStreamVolume(int, int, int)
|
||||
* @throws SecurityException if the adjustment triggers a Do Not Disturb change
|
||||
* and the caller is not granted notification policy access.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
||||
public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
|
||||
@NonNull String packageName, int uid, int pid, int targetSdkVersion) {
|
||||
try {
|
||||
getService().adjustStreamVolumeForUid(streamType, direction, flags, packageName, uid,
|
||||
pid, UserHandle.getUserHandleForUid(uid), targetSdkVersion);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the volume index for a particular stream.
|
||||
* <p>This method has no effect if the device implements a fixed volume policy
|
||||
* as indicated by {@link #isVolumeFixed()}.
|
||||
* <p>From N onward, volume adjustments that would toggle Do Not Disturb are not allowed unless
|
||||
* the app has been granted Do Not Disturb Access.
|
||||
* See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
|
||||
* <p>This API checks if the caller has the necessary permissions based on the provided
|
||||
* component name, uid, and pid values.
|
||||
* See {@link #setStreamVolume(int, int, int)}.
|
||||
*
|
||||
* @param streamType The stream whose volume index should be set.
|
||||
* @param index The volume index to set. See
|
||||
* {@link #getStreamMaxVolume(int)} for the largest valid value.
|
||||
* @param flags One or more flags.
|
||||
* @param packageName the package name of client application
|
||||
* @param uid the uid of client application
|
||||
* @param pid the pid of client application
|
||||
* @param targetSdkVersion the target sdk version of client application
|
||||
* @see #getStreamMaxVolume(int)
|
||||
* @see #getStreamVolume(int)
|
||||
* @see #isVolumeFixed()
|
||||
* @throws SecurityException if the volume change triggers a Do Not Disturb change
|
||||
* and the caller is not granted notification policy access.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
||||
public void setStreamVolumeForUid(int streamType, int index, int flags,
|
||||
@NonNull String packageName, int uid, int pid, int targetSdkVersion) {
|
||||
try {
|
||||
getService().setStreamVolumeForUid(streamType, index, flags, packageName, uid, pid,
|
||||
UserHandle.getUserHandleForUid(uid), targetSdkVersion);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @hide
|
||||
* TODO: make this a @SystemApi */
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
|
||||
|
||||
@@ -28,15 +28,6 @@ import com.android.server.LocalServices;
|
||||
*/
|
||||
public abstract class AudioManagerInternal {
|
||||
|
||||
public abstract void adjustSuggestedStreamVolumeForUid(int streamType, int direction,
|
||||
int flags, String callingPackage, int uid, int pid);
|
||||
|
||||
public abstract void adjustStreamVolumeForUid(int streamType, int direction, int flags,
|
||||
String callingPackage, int uid, int pid);
|
||||
|
||||
public abstract void setStreamVolumeForUid(int streamType, int direction, int flags,
|
||||
String callingPackage, int uid, int pid);
|
||||
|
||||
public abstract void setRingerModeDelegate(RingerModeDelegate delegate);
|
||||
|
||||
public abstract int getRingerModeInternal();
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.media;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.ComponentName;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioDeviceAttributes;
|
||||
import android.media.AudioFocusInfo;
|
||||
@@ -41,6 +42,7 @@ import android.media.audiopolicy.AudioVolumeGroup;
|
||||
import android.media.audiopolicy.IAudioPolicyCallback;
|
||||
import android.media.projection.IMediaProjection;
|
||||
import android.net.Uri;
|
||||
import android.os.UserHandle;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
/**
|
||||
@@ -320,4 +322,16 @@ interface IAudioService {
|
||||
|
||||
oneway void unregisterCapturePresetDevicesRoleDispatcher(
|
||||
ICapturePresetDevicesRoleDispatcher dispatcher);
|
||||
|
||||
oneway void adjustStreamVolumeForUid(int streamType, int direction, int flags,
|
||||
in String packageName, int uid, int pid, in UserHandle userHandle,
|
||||
int targetSdkVersion);
|
||||
|
||||
oneway void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
|
||||
in String packageName, int uid, int pid, in UserHandle userHandle,
|
||||
int targetSdkVersion);
|
||||
|
||||
oneway void setStreamVolumeForUid(int streamType, int direction, int flags,
|
||||
in String packageName, int uid, int pid, in UserHandle userHandle,
|
||||
int targetSdkVersion);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ package android.graphics {
|
||||
package android.media {
|
||||
|
||||
public class AudioManager {
|
||||
method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
|
||||
method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
|
||||
method public void setStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
|
||||
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
|
||||
}
|
||||
|
||||
|
||||
@@ -4186,6 +4186,62 @@ public class AudioService extends IAudioService.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/** @see AudioManager#adjustSuggestedStreamVolumeForUid(int, int, int, String, int, int, int) */
|
||||
@Override
|
||||
public void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
|
||||
@NonNull String packageName, int uid, int pid, UserHandle userHandle,
|
||||
int targetSdkVersion) {
|
||||
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
|
||||
throw new SecurityException("Should only be called from system process");
|
||||
}
|
||||
|
||||
final boolean hasModifyAudioSettings =
|
||||
mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
// direction and stream type swap here because the public
|
||||
// adjustSuggested has a different order than the other methods.
|
||||
adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName, uid,
|
||||
hasModifyAudioSettings, VOL_ADJUST_NORMAL);
|
||||
}
|
||||
|
||||
/** @see AudioManager#adjustStreamVolumeForUid(int, int, int, String, int, int, int) */
|
||||
@Override
|
||||
public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
|
||||
@NonNull String packageName, int uid, int pid, UserHandle userHandle,
|
||||
int targetSdkVersion) {
|
||||
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
|
||||
throw new SecurityException("Should only be called from system process");
|
||||
}
|
||||
|
||||
if (direction != AudioManager.ADJUST_SAME) {
|
||||
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_VOL_UID, streamType,
|
||||
direction/*val1*/, flags/*val2*/,
|
||||
new StringBuilder(packageName).append(" uid:").append(uid)
|
||||
.toString()));
|
||||
}
|
||||
final boolean hasModifyAudioSettings =
|
||||
mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid,
|
||||
hasModifyAudioSettings, VOL_ADJUST_NORMAL);
|
||||
}
|
||||
|
||||
/** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
|
||||
@Override
|
||||
public void setStreamVolumeForUid(int streamType, int index, int flags,
|
||||
@NonNull String packageName, int uid, int pid, UserHandle userHandle,
|
||||
int targetSdkVersion) {
|
||||
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
|
||||
throw new SecurityException("Should only be called from system process");
|
||||
}
|
||||
|
||||
final boolean hasModifyAudioSettings =
|
||||
mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
setStreamVolume(streamType, index, flags, packageName, packageName, uid,
|
||||
hasModifyAudioSettings);
|
||||
}
|
||||
|
||||
//==========================================================================================
|
||||
// Sound Effects
|
||||
//==========================================================================================
|
||||
@@ -8070,43 +8126,6 @@ public class AudioService extends IAudioService.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
|
||||
String callingPackage, int uid, int pid) {
|
||||
final boolean hasModifyAudioSettings =
|
||||
mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
// direction and stream type swap here because the public
|
||||
// adjustSuggested has a different order than the other methods.
|
||||
adjustSuggestedStreamVolume(direction, streamType, flags, callingPackage,
|
||||
callingPackage, uid, hasModifyAudioSettings, VOL_ADJUST_NORMAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
|
||||
String callingPackage, int uid, int pid) {
|
||||
if (direction != AudioManager.ADJUST_SAME) {
|
||||
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_VOL_UID, streamType,
|
||||
direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
|
||||
.append(" uid:").append(uid).toString()));
|
||||
}
|
||||
final boolean hasModifyAudioSettings =
|
||||
mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
adjustStreamVolume(streamType, direction, flags, callingPackage,
|
||||
callingPackage, uid, hasModifyAudioSettings, VOL_ADJUST_NORMAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStreamVolumeForUid(int streamType, int direction, int flags,
|
||||
String callingPackage, int uid, int pid) {
|
||||
final boolean hasModifyAudioSettings =
|
||||
mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
setStreamVolume(streamType, direction, flags, callingPackage, callingPackage, uid,
|
||||
hasModifyAudioSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRingerModeInternal() {
|
||||
return AudioService.this.getRingerModeInternal();
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.content.Intent;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioManagerInternal;
|
||||
import android.media.AudioSystem;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.Rating;
|
||||
@@ -53,8 +52,6 @@ import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -144,7 +141,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
|
||||
// Volume handling fields
|
||||
private AudioAttributes mAudioAttrs;
|
||||
private AudioManager mAudioManager;
|
||||
private AudioManagerInternal mAudioManagerInternal;
|
||||
private int mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL;
|
||||
private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
|
||||
private int mMaxVolume = 0;
|
||||
@@ -179,7 +175,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
|
||||
mContext = mService.getContext();
|
||||
mHandler = new MessageHandler(handlerLooper);
|
||||
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
|
||||
mAudioAttrs = DEFAULT_ATTRIBUTES;
|
||||
mPolicies = policies;
|
||||
|
||||
@@ -328,8 +323,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
mAudioManagerInternal.setStreamVolumeForUid(stream, volumeValue, flags,
|
||||
opPackageName, uid, pid);
|
||||
mAudioManager.setStreamVolumeForUid(stream, volumeValue, flags,
|
||||
opPackageName, uid, pid,
|
||||
mContext.getApplicationInfo().targetSdkVersion);
|
||||
} catch (IllegalArgumentException | SecurityException e) {
|
||||
Log.e(TAG, "Cannot set volume: stream=" + stream + ", value=" + volumeValue
|
||||
+ ", flags=" + flags, e);
|
||||
@@ -518,16 +514,19 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
|
||||
try {
|
||||
if (useSuggested) {
|
||||
if (AudioSystem.isStreamActive(stream, 0)) {
|
||||
mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream,
|
||||
direction, flags, opPackageName, uid, pid);
|
||||
mAudioManager.adjustSuggestedStreamVolumeForUid(stream,
|
||||
direction, flags, opPackageName, uid, pid,
|
||||
mContext.getApplicationInfo().targetSdkVersion);
|
||||
} else {
|
||||
mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
|
||||
mAudioManager.adjustSuggestedStreamVolumeForUid(
|
||||
AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
|
||||
flags | previousFlagPlaySound, opPackageName, uid, pid);
|
||||
flags | previousFlagPlaySound, opPackageName, uid, pid,
|
||||
mContext.getApplicationInfo().targetSdkVersion);
|
||||
}
|
||||
} else {
|
||||
mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
|
||||
opPackageName, uid, pid);
|
||||
mAudioManager.adjustStreamVolumeForUid(stream, direction, flags,
|
||||
opPackageName, uid, pid,
|
||||
mContext.getApplicationInfo().targetSdkVersion);
|
||||
}
|
||||
} catch (IllegalArgumentException | SecurityException e) {
|
||||
Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
|
||||
|
||||
@@ -42,7 +42,6 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioManagerInternal;
|
||||
import android.media.AudioPlaybackConfiguration;
|
||||
import android.media.AudioSystem;
|
||||
import android.media.IRemoteVolumeController;
|
||||
@@ -85,7 +84,6 @@ import android.view.ViewConfiguration;
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.SystemService.TargetUser;
|
||||
import com.android.server.Watchdog;
|
||||
@@ -136,7 +134,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
new ArrayList<>();
|
||||
|
||||
private KeyguardManager mKeyguardManager;
|
||||
private AudioManagerInternal mAudioManagerInternal;
|
||||
private AudioManager mAudioManager;
|
||||
private ContentResolver mContentResolver;
|
||||
private boolean mHasFeatureLeanback;
|
||||
|
||||
@@ -162,6 +160,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
PowerManager pm = mContext.getSystemService(PowerManager.class);
|
||||
mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
|
||||
mNotificationManager = mContext.getSystemService(NotificationManager.class);
|
||||
mAudioManager = mContext.getSystemService(AudioManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,7 +168,6 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
|
||||
Watchdog.getInstance().addMonitor(this);
|
||||
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
|
||||
mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
|
||||
mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(mContext);
|
||||
mAudioPlayerStateMonitor.registerListener(
|
||||
(config, isRemoved) -> {
|
||||
@@ -2057,8 +2055,9 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
callingPid = pid;
|
||||
}
|
||||
try {
|
||||
mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream,
|
||||
direction, flags, callingOpPackageName, callingUid, callingPid);
|
||||
mAudioManager.adjustSuggestedStreamVolumeForUid(suggestedStream,
|
||||
direction, flags, callingOpPackageName, callingUid, callingPid,
|
||||
getContext().getApplicationInfo().targetSdkVersion);
|
||||
} catch (SecurityException | IllegalArgumentException e) {
|
||||
Log.e(TAG, "Cannot adjust volume: direction=" + direction
|
||||
+ ", suggestedStream=" + suggestedStream + ", flags=" + flags
|
||||
|
||||
Reference in New Issue
Block a user