-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Get camera list from camera service directly #5669
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package android.hardware; | ||
|
||
import android.os.IBinder; | ||
|
||
public interface ICameraServiceListener { | ||
int STATUS_NOT_PRESENT = 0; | ||
int STATUS_ENUMERATING = 2; | ||
|
||
class Default implements ICameraServiceListener { | ||
public IBinder asBinder() { | ||
return null; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package com.genymobile.scrcpy.wrappers; | ||
|
||
import android.annotation.SuppressLint; | ||
import android.hardware.ICameraServiceListener; | ||
import android.os.Binder; | ||
import android.os.IBinder; | ||
import android.os.IInterface; | ||
import android.os.Parcel; | ||
import android.util.Log; | ||
|
||
import com.genymobile.scrcpy.util.Ln; | ||
|
||
import java.lang.reflect.Method; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class CameraService { | ||
private final IInterface service; | ||
|
||
private Method addListenerMethod; | ||
private Method removeListenerMethod; | ||
|
||
public CameraService(IInterface service) { | ||
this.service = service; | ||
} | ||
|
||
private Method getAddListenerMethod() throws NoSuchMethodException { | ||
if (addListenerMethod == null) { | ||
addListenerMethod = service.getClass().getDeclaredMethod("addListener", ICameraServiceListener.class); | ||
} | ||
return addListenerMethod; | ||
} | ||
|
||
public CameraStatus[] addListener(ICameraServiceListener listener) { | ||
try { | ||
Object[] rawStatuses = (Object[]) getAddListenerMethod().invoke(service, listener); | ||
if (rawStatuses == null) { | ||
return null; | ||
} | ||
CameraStatus[] cameraStatuses = new CameraStatus[rawStatuses.length]; | ||
for (int i = 0; i < rawStatuses.length; i++) { | ||
cameraStatuses[i] = new CameraStatus(rawStatuses[i]); | ||
} | ||
return cameraStatuses; | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
private Method getRemoveListenerMethod() throws NoSuchMethodException { | ||
if (removeListenerMethod == null) { | ||
removeListenerMethod = service.getClass().getDeclaredMethod("removeListener", ICameraServiceListener.class); | ||
} | ||
return removeListenerMethod; | ||
} | ||
|
||
public void removeListener(ICameraServiceListener listener) { | ||
try { | ||
getRemoveListenerMethod().invoke(service, listener); | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
// Simulate `CameraManager.getCameraIdList()` | ||
// <https://android.googlesource.com/platform/frameworks/base/+/af274a1b252752b385bceb14f1f88e361e2c91e5/core/java/android/hardware/camera2/CameraManager.java#2351> | ||
public String[] getCameraIdList() { | ||
Binder binder = new Binder() { | ||
@Override | ||
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) { | ||
return true; | ||
} | ||
}; | ||
ICameraServiceListener listener = new ICameraServiceListener.Default() { | ||
// To immune from future changes to `ICameraServiceListener`, | ||
// we use an empty `Binder` that ignores all transactions. | ||
@Override | ||
public IBinder asBinder() { | ||
return binder; | ||
} | ||
}; | ||
|
||
CameraStatus[] cameraStatuses = addListener(listener); | ||
removeListener(listener); | ||
|
||
if (cameraStatuses == null) { | ||
return null; | ||
} | ||
|
||
List<String> cameraIds = new ArrayList<>(); | ||
for (CameraStatus cameraStatus : cameraStatuses) { | ||
if (cameraStatus.status != ICameraServiceListener.STATUS_NOT_PRESENT && cameraStatus.status != ICameraServiceListener.STATUS_ENUMERATING) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Contrary to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. |
||
cameraIds.add(cameraStatus.cameraId); | ||
} | ||
} | ||
|
||
return cameraIds.toArray(new String[0]); | ||
} | ||
|
||
@SuppressLint({"SoonBlockedPrivateApi", "PrivateApi"}) | ||
public static class CameraStatus { | ||
public int status; | ||
public String cameraId; | ||
|
||
public CameraStatus(Object raw) throws NoSuchFieldException, IllegalAccessException { | ||
status = raw.getClass().getDeclaredField("status").getInt(raw); | ||
cameraId = (String) raw.getClass().getDeclaredField("cameraId").get(raw); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should do that even with the
CameraManager
then (the current implementation), correct?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. On my Xiaomi devices, there are two invalid cameras ("facing" is "external" and fails to open) that can be filtered by this capability. However, they are hidden in
CameraManager.getCameraIdList
. I don't know if any OS returns non-backward-compatible cameras fromCameraManager