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的资料请关注代码网其它相关文章!
发表评论