android virtualdisplay创建流程及原理
android displaymanager提供了createvirtualdisplay接口,用于创建虚拟屏。虚拟屏可用于录屏(网上很多资料说这个功能),分屏幕(比如一块很长的屏幕,通过虚拟屏分出不同的区域)等等。
创建virtualdisplay
displaymanager中的函数原型如下。后两个hide的api,只有平台的应用才可以使用。
// frameworks/base/core/java/android/hardware/display/displaymanager.java
public virtualdisplay createvirtualdisplay(@nonnull string name,
int width, int height, int densitydpi, @nullable surface surface, int flags) {
}
public virtualdisplay createvirtualdisplay(@nonnull string name,
int width, int height, int densitydpi, @nullable surface surface, int flags,
@nullable virtualdisplay.callback callback, @nullable handler handler) {
}
/** @hide */
public virtualdisplay createvirtualdisplay(@nullable mediaprojection projection,
@nonnull string name, int width, int height, int densitydpi, @nullable surface surface,
int flags, @nullable virtualdisplay.callback callback, @nullable handler handler,
@nullable string uniqueid) {
}
/** @hide */
public virtualdisplay createvirtualdisplay(@nullable mediaprojection projection,
@nonnull virtualdisplayconfig virtualdisplayconfig,
@nullable virtualdisplay.callback callback, @nullable handler handler) {
}补充一点,mediaprojection中也提供了 createvirtualdisplay这个接口,实际上也是通过调用displaymanager实现的功能。
// frameworks/base/media/java/android/media/projection/mediaprojection.java
public virtualdisplay createvirtualdisplay(@nonnull virtualdisplayconfig virtualdisplayconfig,
@nullable virtualdisplay.callback callback, @nullable handler handler) {
displaymanager dm = mcontext.getsystemservice(displaymanager.class);
// 调用displaymanager的接口
return dm.createvirtualdisplay(this, virtualdisplayconfig, callback, handler);
}创建virtualdisplay时,需要传入surface。**virtualdisplay上要绘制的内容,实际是通过传入的surface显示出来的。**比如在主屏(根据物理屏,分配逻辑display)上创建了一个surfaceview,通过把这个surfaceview传给virtualdisplay。那么virtualdisplay的 内容,实际上是在主屏的surfaceview上显示的。下面是一段android原生的例子。
// frameworks/base/packages/systemui/tests/src/com/android/systemui/navigationbar/navigationbarbuttontest.java
private display createvirtualdisplay() {
final string displayname = "navvirtualdisplay";
final displayinfo displayinfo = new displayinfo();
mcontext.getdisplay().getdisplayinfo(displayinfo);
final displaymanager displaymanager = mcontext.getsystemservice(displaymanager.class);
// 创建imagereader,通过它得到一张surface
mreader = imagereader.newinstance(displayinfo.logicalwidth,
displayinfo.logicalheight, pixelformat.rgba_8888, 2);
assertnotnull("imagereader must not be null", mreader);
// 创建虚拟屏,传入surface。
mvirtualdisplay = displaymanager.createvirtualdisplay(displayname, displayinfo.logicalwidth,
displayinfo.logicalheight, displayinfo.logicaldensitydpi, mreader.getsurface(),
0 /*flags*/);
assertnotnull("virtual display must not be null", mvirtualdisplay);
waitfordisplayready(mvirtualdisplay.getdisplay().getdisplayid());
return mvirtualdisplay.getdisplay();
}上面的例子中创建虚拟屏,返回display(实际上是virtualdislay)对象。有了display对象,我们就可以将view绑定到这个虚拟的display上了(绑定网上方法比较多可自行搜索)。关于surface的创建,有很多种方法,比如通过surfacecontron+buffer这种方式也可以。
vituraldisplay创建时,需要提供flag。其值定义如下,可通过 “或”将flag组合。
public static final int virtual_display_flag_public = 1 << 0;
public static final int virtual_display_flag_presentation = 1 << 1;
public static final int virtual_display_flag_secure = 1 << 2;
public static final int virtual_display_flag_own_content_only = 1 << 3;
public static final int virtual_display_flag_auto_mirror = 1 << 4;
public static final int virtual_display_flag_can_show_with_insecure_keyguard = 1 << 5;
public static final int virtual_display_flag_supports_touch = 1 << 6;
public static final int virtual_display_flag_rotates_with_content = 1 << 7;
public static final int virtual_display_flag_destroy_content_on_removal = 1 << 8;
public static final int virtual_display_flag_should_show_system_decorations = 1 << 9;
public static final int virtual_display_flag_trusted = 1 << 10;
public static final int virtual_display_flag_own_display_group = 1 << 11;displaymanager公开的接口中,有virtualdisplay.callback ,提供了其状态的回调。
public static abstract class callback {
/**
* called when the virtual display video projection has been
* paused by the system or when the surface has been detached
* by the application by calling setsurface(null).
* the surface will not receive any more buffers while paused.
*/
public void onpaused() { }
/**
* called when the virtual display video projection has been
* resumed after having been paused.
*/
public void onresumed() { }
/**
* called when the virtual display video projection has been
* stopped by the system. it will no longer receive frames
* and it will never be resumed. it is still the responsibility
* of the application to release() the virtual display.
*/
public void onstopped() { }
}virtualdisplay原理
关于virtualdisplay的实现原理,主要从androidframework角度进行分析。

// /frameworks/base/core/java/android/hardware/display/displaymanager.java
public virtualdisplay createvirtualdisplay(@nonnull string name,
int width, int height, int densitydpi, @nullable surface surface, int flags) {
return createvirtualdisplay(name, width, height, densitydpi, surface, flags, null, null);
}
public virtualdisplay createvirtualdisplay(@nonnull string name,
int width, int height, int densitydpi, @nullable surface surface, int flags,
@nullable virtualdisplay.callback callback, @nullable handler handler) {
final virtualdisplayconfig.builder builder = new virtualdisplayconfig.builder(name, width,
height, densitydpi);
builder.setflags(flags);
if (surface != null) {
builder.setsurface(surface);
}
return createvirtualdisplay(null /* projection */, builder.build(), callback, handler);
}
// todo : remove this hidden api after remove all callers. (refer to multidisplayservice)
/** @hide */
public virtualdisplay createvirtualdisplay(@nullable mediaprojection projection,
@nonnull string name, int width, int height, int densitydpi, @nullable surface surface,
int flags, @nullable virtualdisplay.callback callback, @nullable handler handler,
@nullable string uniqueid) {
final virtualdisplayconfig.builder builder = new virtualdisplayconfig.builder(name, width,
height, densitydpi);
builder.setflags(flags);
if (uniqueid != null) {
builder.setuniqueid(uniqueid);
}
if (surface != null) {
builder.setsurface(surface);
}
return createvirtualdisplay(projection, builder.build(), callback, handler);
}
/** @hide */
public virtualdisplay createvirtualdisplay(@nullable mediaprojection projection,
@nonnull virtualdisplayconfig virtualdisplayconfig,
@nullable virtualdisplay.callback callback, @nullable handler handler) {
// 走的这里,会调用到displaymanagerglobal中。
return mglobal.createvirtualdisplay(mcontext, projection, virtualdisplayconfig, callback,
handler);
}displaymanagerglobal调用dms(displaymanagerservice)服务创建虚拟屏,得到dms返回的displayid后,通过displayid在client端创建了virtualdisplay对象。
// /frameworks/base/core/java/android/hardware/display/displaymanager.java
public virtualdisplay createvirtualdisplay(@nonnull context context, mediaprojection projection,
@nonnull virtualdisplayconfig virtualdisplayconfig, virtualdisplay.callback callback,
handler handler) {
virtualdisplaycallback callbackwrapper = new virtualdisplaycallback(callback, handler);
// 从mediaprojection过来的调用,这个地方非空。
imediaprojection projectiontoken = projection != null ? projection.getprojection() : null;
int displayid;
try {
// 告知dms创建虚拟屏,并返回displayid
displayid = mdm.createvirtualdisplay(virtualdisplayconfig, callbackwrapper,
projectiontoken, context.getpackagename());
} catch (remoteexception ex) {
throw ex.rethrowfromsystemserver();
}
if (displayid < 0) {
log.e(tag, "could not create virtual display: " + virtualdisplayconfig.getname());
return null;
}
// 通过displayid,取得display对象信息(也是调用dms得到的)
display display = getrealdisplay(displayid);
if (display == null) {
log.wtf(tag, "could not obtain display info for newly created "
+ "virtual display: " + virtualdisplayconfig.getname());
try {
// 创建失败,需要释放
mdm.releasevirtualdisplay(callbackwrapper);
} catch (remoteexception ex) {
throw ex.rethrowfromsystemserver();
}
return null;
}
// 创建virtualdisplay
return new virtualdisplay(this, display, callbackwrapper,
virtualdisplayconfig.getsurface());
}displaymanagerservice(dms)中创建displaydevice并添加到device列表中管理
// /frameworks/base/services/core/java/com/android/server/display/displaymanagerservice.java
@override // binder call
public int createvirtualdisplay(virtualdisplayconfig virtualdisplayconfig,
ivirtualdisplaycallback callback, imediaprojection projection, string packagename) {
// 检查uid与包名,是否相符。
final int callinguid = binder.getcallinguid();
if (!validatepackagename(callinguid, packagename)) {
throw new securityexception("packagename must match the calling uid");
}
if (callback == null) {
throw new illegalargumentexception("apptoken must not be null");
}
if (virtualdisplayconfig == null) {
throw new illegalargumentexception("virtualdisplayconfig must not be null");
}
// 拿到client端传过来的surface对象
final surface surface = virtualdisplayconfig.getsurface();
int flags = virtualdisplayconfig.getflags();
if (surface != null && surface.issinglebuffered()) {
throw new illegalargumentexception("surface can't be single-buffered");
}
// 下面开始针对flag,做一些逻辑判断。
if ((flags & virtual_display_flag_public) != 0) {
flags |= virtual_display_flag_auto_mirror;
// public displays can't be allowed to show content when locked.
if ((flags & virtual_display_flag_can_show_with_insecure_keyguard) != 0) {
throw new illegalargumentexception(
"public display must not be marked as show_when_locked_insecure");
}
}
if ((flags & virtual_display_flag_own_content_only) != 0) {
flags &= ~virtual_display_flag_auto_mirror;
}
if ((flags & virtual_display_flag_auto_mirror) != 0) {
flags &= ~virtual_display_flag_own_display_group;
}
if (projection != null) {
try {
if (!getprojectionservice().isvalidmediaprojection(projection)) {
throw new securityexception("invalid media projection");
}
flags = projection.applyvirtualdisplayflags(flags);
} catch (remoteexception e) {
throw new securityexception("unable to validate media projection or flags");
}
}
if (callinguid != process.system_uid &&
(flags & virtual_display_flag_auto_mirror) != 0) {
if (!canprojectvideo(projection)) {
throw new securityexception("requires capture_video_output or "
+ "capture_secure_video_output permission, or an appropriate "
+ "mediaprojection token in order to create a screen sharing virtual "
+ "display.");
}
}
if (callinguid != process.system_uid && (flags & virtual_display_flag_secure) != 0) {
if (!canprojectsecurevideo(projection)) {
throw new securityexception("requires capture_secure_video_output "
+ "or an appropriate mediaprojection token to create a "
+ "secure virtual display.");
}
}
if (callinguid != process.system_uid && (flags & virtual_display_flag_trusted) != 0) {
if (!checkcallingpermission(add_trusted_display, "createvirtualdisplay()")) {
eventlog.writeevent(0x534e4554, "162627132", callinguid,
"attempt to create a trusted display without holding permission!");
throw new securityexception("requires add_trusted_display permission to "
+ "create a trusted virtual display.");
}
}
if (callinguid != process.system_uid
&& (flags & virtual_display_flag_own_display_group) != 0) {
if (!checkcallingpermission(add_trusted_display, "createvirtualdisplay()")) {
throw new securityexception("requires add_trusted_display permission to "
+ "create a virtual display which is not in the default displaygroup.");
}
}
if ((flags & virtual_display_flag_trusted) == 0) {
flags &= ~virtual_display_flag_should_show_system_decorations;
}
// sometimes users can have sensitive information in system decoration windows. an app
// could create a virtual display with system decorations support and read the user info
// from the surface.
// we should only allow adding flag virtual_display_flag_should_show_system_decorations
// to trusted virtual displays.
final int trusteddisplaywithsysdecorflag =
(virtual_display_flag_should_show_system_decorations
| virtual_display_flag_trusted);
if ((flags & trusteddisplaywithsysdecorflag)
== virtual_display_flag_should_show_system_decorations
&& !checkcallingpermission(internal_system_window, "createvirtualdisplay()")) {
throw new securityexception("requires internal_system_window permission");
}
final long token = binder.clearcallingidentity();
try {
// 调用内部实现
return createvirtualdisplayinternal(callback, projection, callinguid, packagename,
surface, flags, virtualdisplayconfig);
} finally {
binder.restorecallingidentity(token);
}
}
// /frameworks/base/services/core/java/com/android/server/display/displaymanagerservice.java
private int createvirtualdisplayinternal(ivirtualdisplaycallback callback,
imediaprojection projection, int callinguid, string packagename, surface surface,
int flags, virtualdisplayconfig virtualdisplayconfig) {
synchronized (msyncroot) {
if (mvirtualdisplayadapter == null) {
slog.w(tag, "rejecting request to create private virtual display "
+ "because the virtual display adapter is not available.");
return -1;
}
// 为虚拟屏创建device(告知surfaceflinger创建display)
displaydevice device = mvirtualdisplayadapter.createvirtualdisplaylocked(
callback, projection, callinguid, packagename, surface, flags,
virtualdisplayconfig);
if (device == null) {
return -1;
}
// 发送添加device通知,这里比较重要
mdisplaydevicerepo.ondisplaydeviceevent(device,
displayadapter.display_device_event_added);
// 检查display是否创建成功
final logicaldisplay display = mlogicaldisplaymapper.getdisplaylocked(device);
if (display != null) {
return display.getdisplayidlocked();
}
// something weird happened and the logical display was not created.
slog.w(tag, "rejecting request to create virtual display "
+ "because the logical display was not created.");
mvirtualdisplayadapter.releasevirtualdisplaylocked(callback.asbinder());
mdisplaydevicerepo.ondisplaydeviceevent(device,
displayadapter.display_device_event_removed);
}
return -1;
}接下来dms开始调用surfaceflinger的接口,创建display。并将display放入自身的list中管理。
// /frameworks/base/services/core/java/com/android/server/display/virtualdisplayadapter.java
public displaydevice createvirtualdisplaylocked(ivirtualdisplaycallback callback,
imediaprojection projection, int owneruid, string ownerpackagename, surface surface,
int flags, virtualdisplayconfig virtualdisplayconfig) {
string name = virtualdisplayconfig.getname();
// virtual_display_flag_secure 的用途,是判断是否为安全的display,这个参数会告知surfaceflinger
boolean secure = (flags & virtual_display_flag_secure) != 0;
ibinder apptoken = callback.asbinder();
// 调用surfacefligner创建display(display的type是virtual)
ibinder displaytoken = msurfacecontroldisplayfactory.createdisplay(name, secure);
final string baseuniqueid =
unique_id_prefix + ownerpackagename + "," + owneruid + "," + name + ",";
final int uniqueindex = getnextuniqueindex(baseuniqueid);
string uniqueid = virtualdisplayconfig.getuniqueid();
if (uniqueid == null) {
uniqueid = baseuniqueid + uniqueindex;
} else {
uniqueid = unique_id_prefix + ownerpackagename + ":" + uniqueid;
}
// 通过surfacefligner返回的displaytoken,创建device对象
virtualdisplaydevice device = new virtualdisplaydevice(displaytoken, apptoken,
owneruid, ownerpackagename, surface, flags, new callback(callback, mhandler),
uniqueid, uniqueindex, virtualdisplayconfig);
// 放到虚拟屏的list中管理。
mvirtualdisplaydevices.put(apptoken, device);
try {
if (projection != null) {
projection.registercallback(new mediaprojectioncallback(apptoken));
}
apptoken.linktodeath(device, 0);
} catch (remoteexception ex) {
mvirtualdisplaydevices.remove(apptoken);
device.destroylocked(false);
return null;
}
// return the display device without actually sending the event indicating
// that it was added. the caller will handle it.
return device;
}
// /frameworks/base/services/core/java/com/android/server/display/displaydevicerepository.java
// mdisplaydevicerepo.ondisplaydeviceevent(device,
// displayadapter.display_device_event_added);
// 这段代码,会调用到下面的函数中。
private void handledisplaydeviceadded(displaydevice device) {
synchronized (msyncroot) {
displaydeviceinfo info = device.getdisplaydeviceinfolocked();
if (mdisplaydevices.contains(device)) {
slog.w(tag, "attempted to add already added display device: " + info);
return;
}
slog.i(tag, "display device added: " + info);
device.mdebuglastloggeddeviceinfo = info;
// 需要是将device(就是上面创建的虚拟屏幕device)放入到dms的管理list
mdisplaydevices.add(device);
// 通知device添加,会调用到logicaldisplaymappe的handledisplaydeviceaddedlocked中。
sendeventlocked(device, display_device_event_added);
}
}
// /frameworks/base/services/core/java/com/android/server/display/logicaldisplaymapper.java
private void handledisplaydeviceaddedlocked(displaydevice device) {
displaydeviceinfo deviceinfo = device.getdisplaydeviceinfolocked();
// internal displays need to have additional initialization.
// this initializes a default dynamic display layout for internal
// devices, which is used as a fallback in case no static layout definitions
// exist or cannot be loaded.
if (deviceinfo.type == display.type_internal) {
initializeinternaldisplaydevicelocked(device);
}
// create a logical display for the new display device
logicaldisplay display = createnewlogicaldisplaylocked(
device, layout.assigndisplayidlocked(false /*isdefault*/));
// 刷新布局和display配置
applylayoutlocked();
updatelogicaldisplayslocked();
}虚拟屏幕的创建,client端通过surface告知的displayid,创建virtualdisplay对象。通过displayid,与dms打交道。dms服务端,通过surfaceflinger创建虚拟屏,拿到surfacefligner的displaytoken,然后通过它创建virtualdisplaydevice + logicaldisplay来管理虚拟屏幕。

如何上屏
创建虚拟屏幕的时候,会传入了一张surface(比如绑定主屏的一张buffer)。虚拟屏通过这张surface拿到surface对应的buffer,将上屏内容绘制到这个buffer上,然后提交到画面流水线上(surfaceflinger)。通过surfaceflinger将这个这个buffer最终由surfaceflinger描画并显示到surface所在那张display上(根据virtualdisplay位置去显示。)

以上就是android使用virtualdisplay的创建虚拟屏流程及原理解析的详细内容,更多关于android virtualdisplay的资料请关注代码网其它相关文章!
发表评论