当前位置: 代码网 > it编程>App开发>Android > Android使用DisplayManager创建虚拟屏流程及原理解析

Android使用DisplayManager创建虚拟屏流程及原理解析

2024年05月28日 Android 我要评论
android virtualdisplay创建流程及原理android displaymanager提供了createvirtualdisplay接口,用于创建虚拟屏。虚拟屏可用于录屏(网上很多资料

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

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com