Compare commits

..

5 Commits

Author SHA1 Message Date
Romain Vimont
828795a180 Adapt FakeContext for Android 14
This fixes audio for Android 14 developer preview 2.

Fixes #3784 <https://github.com/Genymobile/scrcpy/issues/3784>

Suggested-by: Namelesswonder <Namelesswonder@users.noreply.github.com>
2023-03-19 10:55:46 +01:00
Romain Vimont
5a743d73df Adapt clipboard wrappers to Android 14
A new parameter deviceId has been added.

Fixes #3784 <https://github.com/Genymobile/scrcpy/issues/3784>
2023-03-19 10:55:46 +01:00
Romain Vimont
3626d90004 Use separate audio capture code for Android 11
The code to start audio capture is more complicated for Android 11
(launch a fake popup, wait, make several attempts, close the shell
package).

Use a distinct code path specific to Android 11.
2023-03-17 21:30:04 +01:00
Romain Vimont
02f4ff7534 Make 3 attempts to start AudioRecord
On Android 11, a fake popup must be briefly opened to make the system
think that the shell app is in the foreground so that audio may be
recorded.

Making the shell app foreground may take some time depending on the
device, so make 3 attempts, waiting 100ms before each.

Fixes #3796 <https://github.com/Genymobile/scrcpy/issues/3796>
2023-03-17 21:30:04 +01:00
Romain Vimont
a3871130cc List available encoders on failure
When the creation of an encoder fails, log an explicit error message
with the list of available encoders.
2023-03-17 21:29:45 +01:00
3 changed files with 82 additions and 31 deletions

View File

@@ -274,7 +274,7 @@ public final class AudioEncoder implements AsyncProcessor {
Ln.e("Audio encoder '" + encoderName + "' for " + codec.getName() + " not found\n" + LogUtils.buildAudioEncoderListMessage()); Ln.e("Audio encoder '" + encoderName + "' for " + codec.getName() + " not found\n" + LogUtils.buildAudioEncoderListMessage());
throw new ConfigurationException("Unknown encoder: " + encoderName); throw new ConfigurationException("Unknown encoder: " + encoderName);
} catch (IOException e) { } catch (IOException e) {
Ln.e("Could not create video encoder '" + encoderName + "' for " + codec.getName() + "\n" + LogUtils.buildAudioEncoderListMessage()); Ln.e("Could not create audio encoder '" + encoderName + "' for " + codec.getName() + "\n" + LogUtils.buildAudioEncoderListMessage());
throw e; throw e;
} }
} }

View File

@@ -38,4 +38,10 @@ public final class FakeContext extends ContextWrapper {
builder.setPackageName(PACKAGE_NAME); builder.setPackageName(PACKAGE_NAME);
return builder.build(); return builder.build();
} }
// @Override to be added on SDK upgrade for Android 14
@SuppressWarnings("unused")
public int getDeviceId() {
return 0;
}
} }

View File

@@ -16,9 +16,9 @@ public class ClipboardManager {
private Method getPrimaryClipMethod; private Method getPrimaryClipMethod;
private Method setPrimaryClipMethod; private Method setPrimaryClipMethod;
private Method addPrimaryClipChangedListener; private Method addPrimaryClipChangedListener;
private boolean alternativeGetMethod; private int getMethodVersion;
private boolean alternativeSetMethod; private int setMethodVersion;
private boolean alternativeAddListenerMethod; private int addListenerMethodVersion;
public ClipboardManager(IInterface manager) { public ClipboardManager(IInterface manager) {
this.manager = manager; this.manager = manager;
@@ -31,9 +31,15 @@ public class ClipboardManager {
} else { } else {
try { try {
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class); getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class);
} catch (NoSuchMethodException e) { getMethodVersion = 0;
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class); } catch (NoSuchMethodException e1) {
alternativeGetMethod = true; try {
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class);
getMethodVersion = 1;
} catch (NoSuchMethodException e2) {
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class, int.class);
getMethodVersion = 2;
}
} }
} }
} }
@@ -47,41 +53,62 @@ public class ClipboardManager {
} else { } else {
try { try {
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, int.class); setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, int.class);
} catch (NoSuchMethodException e) { setMethodVersion = 0;
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, String.class, int.class); } catch (NoSuchMethodException e1) {
alternativeSetMethod = true; try {
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, String.class, int.class);
setMethodVersion = 1;
} catch (NoSuchMethodException e2) {
setPrimaryClipMethod = manager.getClass()
.getMethod("setPrimaryClip", ClipData.class, String.class, String.class, int.class, int.class);
setMethodVersion = 2;
}
} }
} }
} }
return setPrimaryClipMethod; return setPrimaryClipMethod;
} }
private static ClipData getPrimaryClip(Method method, boolean alternativeMethod, IInterface manager) private static ClipData getPrimaryClip(Method method, int methodVersion, IInterface manager)
throws InvocationTargetException, IllegalAccessException { throws InvocationTargetException, IllegalAccessException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME); return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME);
} }
if (alternativeMethod) {
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); switch (methodVersion) {
case 0:
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID);
case 1:
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
default:
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0);
} }
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID);
} }
private static void setPrimaryClip(Method method, boolean alternativeMethod, IInterface manager, ClipData clipData) private static void setPrimaryClip(Method method, int methodVersion, IInterface manager, ClipData clipData)
throws InvocationTargetException, IllegalAccessException { throws InvocationTargetException, IllegalAccessException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME); method.invoke(manager, clipData, FakeContext.PACKAGE_NAME);
} else if (alternativeMethod) { return;
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); }
} else {
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID); switch (methodVersion) {
case 0:
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID);
break;
case 1:
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
break;
default:
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0);
break;
} }
} }
public CharSequence getText() { public CharSequence getText() {
try { try {
Method method = getGetPrimaryClipMethod(); Method method = getGetPrimaryClipMethod();
ClipData clipData = getPrimaryClip(method, alternativeGetMethod, manager); ClipData clipData = getPrimaryClip(method, getMethodVersion, manager);
if (clipData == null || clipData.getItemCount() == 0) { if (clipData == null || clipData.getItemCount() == 0) {
return null; return null;
} }
@@ -96,7 +123,7 @@ public class ClipboardManager {
try { try {
Method method = getSetPrimaryClipMethod(); Method method = getSetPrimaryClipMethod();
ClipData clipData = ClipData.newPlainText(null, text); ClipData clipData = ClipData.newPlainText(null, text);
setPrimaryClip(method, alternativeSetMethod, manager, clipData); setPrimaryClip(method, setMethodVersion, manager, clipData);
return true; return true;
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
Ln.e("Could not invoke method", e); Ln.e("Could not invoke method", e);
@@ -104,14 +131,23 @@ public class ClipboardManager {
} }
} }
private static void addPrimaryClipChangedListener(Method method, boolean alternativeMethod, IInterface manager, private static void addPrimaryClipChangedListener(Method method, int methodVersion, IInterface manager,
IOnPrimaryClipChangedListener listener) throws InvocationTargetException, IllegalAccessException { IOnPrimaryClipChangedListener listener) throws InvocationTargetException, IllegalAccessException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
method.invoke(manager, listener, FakeContext.PACKAGE_NAME); method.invoke(manager, listener, FakeContext.PACKAGE_NAME);
} else if (alternativeMethod) { return;
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); }
} else {
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID); switch (methodVersion) {
case 0:
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID);
break;
case 1:
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
break;
default:
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0);
break;
} }
} }
@@ -124,10 +160,19 @@ public class ClipboardManager {
try { try {
addPrimaryClipChangedListener = manager.getClass() addPrimaryClipChangedListener = manager.getClass()
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, int.class); .getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, int.class);
} catch (NoSuchMethodException e) { addListenerMethodVersion = 0;
addPrimaryClipChangedListener = manager.getClass() } catch (NoSuchMethodException e1) {
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, String.class, int.class); try {
alternativeAddListenerMethod = true; addPrimaryClipChangedListener = manager.getClass()
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, String.class,
int.class);
addListenerMethodVersion = 1;
} catch (NoSuchMethodException e2) {
addPrimaryClipChangedListener = manager.getClass()
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, String.class,
int.class, int.class);
addListenerMethodVersion = 2;
}
} }
} }
} }
@@ -137,7 +182,7 @@ public class ClipboardManager {
public boolean addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) { public boolean addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
try { try {
Method method = getAddPrimaryClipChangedListener(); Method method = getAddPrimaryClipChangedListener();
addPrimaryClipChangedListener(method, alternativeAddListenerMethod, manager, listener); addPrimaryClipChangedListener(method, addListenerMethodVersion, manager, listener);
return true; return true;
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
Ln.e("Could not invoke method", e); Ln.e("Could not invoke method", e);