The DeviceVirtualization Engine (DV Engine for short) is a multi-device virtualization platform provided by Huawei to facilitate the virtualization of external devices and components. Through this platform, peripheral devices or device components are converted into virtual components of mobile phones, and their capabilities are incorporated and utilized by mobile phones as general capabilities. In addition, capabilities of multiple virtual devices can be used synchronously.
By integrating the DV Engine, device vendors can use external devices and components in an optimally convenient manner, including but not limited to cameras, speakers, monitors, microphones, wearables, and other peripheral devices. The devices and components can be controlled and switched flexibly, fully utilizing all available resource advantages, to create mutually reinforcing device collaborations that benefit users immensely.

What You Will Create

In this Codelab, you will create a demo project, integrate the HUAWEI DeviceVirtualization Engine to complete building the overall service process, and start and stop device discovery listening and virtualization event processing on the TV screen.

What You Will Learn

Hardware Requirements

Software Requirements

Required Knowledge

HUAWEI DeviceVirtualization Engine integration requires the following preparations:

For details, see DeviceVirtualization Engine Development Preparations.

1. Integrate DeviceVirtualization Engine.

Add the following code to the build.gradle file in the app directory of the project and add the downloaded .aar package to the project:


dependencies { implementation files('libs/devicevirtualization-device-android-') }

Click the sync button, as shown in the following figure.

After the synchronization, the following screen is displayed.

2. Configure the system permission for the app in the AndroidManifest.xml file.


<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.huawei.dmsdptest" android:sharedUserId="android.uid.system"> ......

3. Initialize the service.

Use the static method initInstance of VirtualizationAdapter to initialize the service. During initialization, specify the device name and type. The initialization result is obtained through the onInitSuccess and onInitFail callback methods.


DeviceInfo deviceInfo = new DeviceInfo(); deviceInfo.setDeviceName(getDeviceName()); deviceInfo.setDeviceType(DeviceInfo.DEVICE_TYPE_TV); HwLog.d(TAG, "initMSDPService info:" + deviceInfo); int initInstance = VirtualizationAdapter.initInstance(getApplicationContext(), deviceInfo, new IInitCallback() { @Override public void onInitSuccess(VirtualizationAdapter virtualizationAdapter) { HwLog.i(TAG, "onInitSuccess"); mVirtualizationAdapter = virtualizationAdapter; AdapterUtil.getInstance().setVirtualizationAdapter(mVirtualizationAdapter); int status = mVirtualizationAdapter.setVirtualizationCallback(mVirtualizationCallback); HwLog.i(TAG, "setVirtualizationCallback status:" + status); HwLog.i(TAG, "start listen"); int result = mVirtualizationAdapter.startAdv(); HwLog.i(TAG, "start listen result:" + result); } @Override public void onInitFail(int code) { HwLog.i(TAG, "onInitFail: " + code); disconnect(); } @Override public void onBinderDied() { HwLog.e(TAG, "onBinderDied service died!"); AdapterUtil.getInstance().setConnectedDevice(null); mVirtualizationAdapter = null; if (mDisplayService.get()) { HwLog.i(TAG, "close surface"); //Send a broadcast to disable SurfaceActivity. MainActivity.this.sendBroadcast(new Intent(ACTION_CLOSE_SURFACE_ACTIVITY)); mDisplayService.set(false); } } }); HwLog.i(TAG, "initInstance result:" + initInstance);

4. Register the virtualization service callback API.

After the initialization is successful, the onInitSuccess callback API returns a VirtualizationAdapter instance object. Register the IVirtualizationCallback API for the instance object. The following five methods should be implemented:

5. MainActivity.java

mVirtualizationCallback = new IVirtualizationCallback() { @Override public void onDeviceChange(String remoteDevice, int state) { if (state == CONNECT) { HwLog.i(TAG, "device has connected"); AdapterUtil.getInstance().setConnectedDevice(remoteDevice); } else if (state == DISCONNECT) { HwLog.i(TAG, "device has disconnected"); AdapterUtil.getInstance().setConnectedDevice(null); mDisplayService.set(false); //The device is disconnected. The surface for displaying the projection image should be destroyed. MainActivity.this.sendBroadcast(new Intent(ACTION_CLOSE_SURFACE_ACTIVITY)); } else if (state == START_PROJECTION) { HwLog.i(TAG, "service all ready,start project"); mDisplayService.set(true); //Create a surface and transfer the surface to the DigitalView service through the startProjection API to display the projection image. Intent intent = new Intent(MainActivity.this, SurfaceActivity.class); MainActivity.this.startActivity(intent); } else if (state == STOP_PROJECTION) { HwLog.i(TAG, "stopProjection"); mDisplayService.set(false); //The projection capability stops, and the surface for displaying the projection image should be destroyed. MainActivity.this.sendBroadcast(new Intent(ACTION_CLOSE_SURFACE_ACTIVITY)); } else if (state == REQUEST_CONNECTION) { //Customize the pop-up window layout file. View dialogView = View.inflate(mContext, R.layout.dialog_permission, null); final AlertDialog dialog = new AlertDialog.Builder(mContext).setView(dialogView).create(); TextView titleTextView = dialogView.findViewById(R.id.title_id); TextView informationTextView = dialogView.findViewById(R.id.information_id); Button refuseButton = dialogView.findViewById(R.id.button_prohibition); Button permanentButton = dialogView.findViewById(R.id.button_permission_permanent); Button temporaryButton = dialogView.findViewById(R.id.button_permission_temporary); temporaryButton.requestFocus(); titleTextView.setText("Allow "+ remoteDevice + "to access this device? "); informationTextView.setText("The microphone and camera may be used for audio and video collaboration or other services. "); refuseButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mVirtualizationAdapter != null) { int result = mVirtualizationAdapter.disconnectDevice(); HwLog.i(TAG, "disconnect device result:" + result); dialog.dismiss(); } } }); permanentButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int result = mVirtualizationAdapter.acceptConnection(true); HwLog.i(TAG, "permanent permit result:" + result); dialog.dismiss(); } }); temporaryButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int result = mVirtualizationAdapter.acceptConnection(false); HwLog.i(TAG, "temporary permit result:" + result); dialog.dismiss(); } }); dialog.show(); } } @Override public void onPinCode(String deviceName, final String pinCode) { HwLog.i(TAG, "pinCode:" + pinCode); MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { if (!TextUtils.isEmpty(pinCode)) { //Display pinCode. mCode.setText(pinCode); } else { //If pinCode is empty, it should not be displayed. mCode.setText(""); } } }); } @Override public long getSecureFileSize(String fileName) { HwLog.i(TAG, "readSecureFile fileName:" + fileName); try (FileInputStream fileInputStream = openFileInput(fileName)) { FileChannel channel = fileInputStream.getChannel(); return channel.size(); } catch (IOException e) { HwLog.e(TAG, "getSecureFileSize failed"); } return 0; } @Override public byte[] readSecureFile(String fileName) { HwLog.i(TAG, "readSecureFile fileName:" + fileName); try (FileInputStream fileInputStream = openFileInput(fileName)) { int canRead = fileInputStream.available(); byte[] buffer = new byte[canRead]; int read = fileInputStream.read(buffer); if (read != canRead) { HwLog.e(TAG, "read file size failed"); } else { HwLog.i(TAG, "read file success"); return buffer; } } catch (IOException e) { HwLog.e(TAG, "read file failed"); } return new byte[0]; } @Override public boolean writeSecureFile(String fileName, byte[] bytes) { HwLog.i(TAG, "writeSecureFile fileName:" + fileName); try (FileOutputStream fileOutputStream = openFileOutput(fileName, MODE_PRIVATE)) { fileOutputStream.write(bytes); fileOutputStream.flush(); } catch (IOException e) { HwLog.e(TAG, "write file exception," + e); return false; } return true; } }; int status = mVirtualizationAdapter.setVirtualizationCallback(mVirtualizationCallback);

6. Start the discovery listener.

Use the startAdv method of the VirtualizationAdapter instance object to start the discovery listener. After the listener is started successfully, the mobile phone can discover the device. After this step is complete, the basic functions of the device virtualization capability can be used.


7. Disconnect the device.

The device can disconnect from the current device by using the disconnectDevice method of the VirtualizationAdapter instance object when necessary.



8. Release the connection object.

Release the connection object by using the static method releaseInstance of VirtualizationAdapter.

@Override protected void onDestroy() { HwLog.i(TAG, "onDestroy"); disconnect(); HwLog.i(TAG, "onDestroy end"); super.onDestroy(); } //All resources are released. private void disconnect() { if (mVirtualizationAdapter != null) { //Stop screen projection. if (mDisplayService.get()) { int stopStatus = mVirtualizationAdapter.stopProjection(); HwLog.d(TAG, "disconnect stopProjection stopStatus:" + stopStatus); mDisplayService.set(false); } //Stop the connection listener. int disconnectDeviceStatus = mVirtualizationAdapter.disconnectDevice(); HwLog.i(TAG, "disconnect disconnectDevice status:" + disconnectDeviceStatus); //Stop the discovery listener. int result = mVirtualizationAdapter.stopAdv(); HwLog.i(TAG, "disconnect stop listen result:" + result); } //Release the adapter object. VirtualizationAdapter.releaseInstance(); mVirtualizationAdapter = null; }


Well done. You have successfully completed this codelab, and learned:

For more information, please click the following link:
Related documents


Code copied