diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5575ca1a46bee3c88af2757865851f4567af68f5..a0492d33e72d3645d73a859e7e1f2fe36af56ca9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -8,9 +8,11 @@
+
+
requestBody = new HashMap();
diff --git a/cloudphone/src/main/cpp/cas_common/CasMsg.h b/cloudphone/src/main/cpp/cas_common/CasMsg.h
index f591ff846bf06edc253bc95cfe1d0f24a3088df5..3f58e511f89cb1f206e90049814e0122e44c1f7e 100644
--- a/cloudphone/src/main/cpp/cas_common/CasMsg.h
+++ b/cloudphone/src/main/cpp/cas_common/CasMsg.h
@@ -39,6 +39,7 @@ enum CasMsgType : uint8_t {
VirtualMicrophone = 22,
VirtualSensor = 23,
VirtualLocation = 24,
+ VirtualVibrator = 25,
End,
};
diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp
index 09746b90d9944d7f2f12a1a03801807f5d773186..6439dfc24279450567628bea9cbdb95aece221ea 100644
--- a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp
+++ b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp
@@ -75,7 +75,7 @@ void CasStreamRecvParser::SetServiceHandle(unsigned char type, CasPktHandle *ser
CasPktHandle *CasStreamRecvParser::GetServiceHandle(unsigned char type)
{
- return VirtualLocation >= type && type >= VirtualCamera ? m_serviceHandles[VirtualDevice] : m_serviceHandles[type];
+ return VirtualVibrator >= type && type >= VirtualCamera ? m_serviceHandles[VirtualDevice] : m_serviceHandles[type];
}
CasStreamParseThread::CasStreamParseThread(CasSocket *socket, CasStreamRecvParser *streamRecvParser)
diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudPhoneParas.java b/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudPhoneParas.java
index 1abef3663c31777417328c7886a2987f65d9312c..729c0d81908e7b1dacfd90b7f5eed0aaa4ad57af 100644
--- a/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudPhoneParas.java
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudPhoneParas.java
@@ -31,4 +31,5 @@ public class CloudPhoneParas {
public static final short DEV_TYPE_MICROPHONE = 2;
public static final short DEV_TYPE_SENSOR = 3;
public static final short DEV_TYPE_LOCATION = 4;
+ public static final short DEV_TYPE_VIBRATOR = 5;
}
diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceProtocol.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceProtocol.java
index 16906a5ac97aca5931b60a17aba8351617847163..b8e347aa74a8d3120d9ac7aa7feb95769f213e6f 100644
--- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceProtocol.java
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceProtocol.java
@@ -19,6 +19,7 @@ import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_CAMERA;
import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_LOCATION;
import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_MICROPHONE;
import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_SENSOR;
+import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_VIBRATOR;
import android.content.Context;
import android.hardware.SensorManager;
@@ -29,6 +30,7 @@ import com.huawei.cloudphone.virtualdevice.camera.VirtualCameraManager;
import com.huawei.cloudphone.virtualdevice.location.VirtualLocationManager;
import com.huawei.cloudphone.virtualdevice.microphone.VirtualMicrophoneManager;
import com.huawei.cloudphone.virtualdevice.sensor.VirtualSensorManager;
+import com.huawei.cloudphone.virtualdevice.vibrator.VirtualVibratorManager;
import java.util.HashMap;
import java.util.Map;
@@ -56,6 +58,7 @@ public class VirtualDeviceProtocol {
virtualDeviceManagers.put(DEV_TYPE_SENSOR, new VirtualSensorManager(this,
(SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE)));
virtualDeviceManagers.put(DEV_TYPE_LOCATION, new VirtualLocationManager(this, mContext));
+ virtualDeviceManagers.put(DEV_TYPE_VIBRATOR, new VirtualVibratorManager(this, mContext));
// 设置权限监听
if (listener != null) {
@@ -85,6 +88,7 @@ public class VirtualDeviceProtocol {
public void processMsg(MsgHeader header, byte[] body) {
short devType = header.mDeviceType;
+ Log.e(TAG, "processMsg: device type :" + header.mDeviceType);
if (devType == 0) devType = DEV_TYPE_SENSOR;
VirtualDeviceManager virtualDeviceManager = virtualDeviceManagers.get(devType);
if (virtualDeviceManager == null) {
diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/location/VirtualLocation.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/location/VirtualLocation.java
index baef69718cb5b9cc64882fb2872276875041c760..f8fe2ae05ef8275c0f92980a47c467ca7ccbb2fa 100644
--- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/location/VirtualLocation.java
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/location/VirtualLocation.java
@@ -25,6 +25,9 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.location.GnssClock;
+import android.location.GnssMeasurement;
+import android.location.GnssMeasurementsEvent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -51,32 +54,61 @@ import com.huawei.cloudphone.api.CloudPhonePermissionRequestListener;
import com.huawei.cloudphone.common.CASLog;
import com.huawei.cloudphone.virtualdevice.common.IVirtualDeviceDataListener;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.text.DecimalFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
public class VirtualLocation {
private static final String TAG = "VirtualLocation";
private final long MIN_TIME = 1000;
private final float MIN_DISTANCE = 1;
+ private final int TYPE_LOCATION = 0;
+ private final int TYPE_GNSS_MEASUREMENT = 1;
+
+ private static final int HAS_SNR = (1<<0);
+ private static final int HAS_CARRIER_FREQUENCY = (1<<9);
+ private static final int HAS_CARRIER_CYCLES = (1<<10);
+ private static final int HAS_CARRIER_PHASE = (1<<11);
+ private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12);
+ private static final int HAS_AUTOMATIC_GAIN_CONTROL = (1<<13);
+
+ private static final int HAS_LEAP_SECOND = (1<<0);
+ private static final int HAS_TIME_UNCERTAINTY = (1<<1);
+ private static final int HAS_FULL_BIAS = (1<<2);
+ private static final int HAS_BIAS = (1<<3);
+ private static final int HAS_BIAS_UNCERTAINTY = (1<<4);
+ private static final int HAS_DRIFT = (1<<5);
+ private static final int HAS_DRIFT_UNCERTAINTY = (1<<6);
private Context mContext;
private Location mLocation;
private LocationManager mLocationManager;
private LocationListener mLocationListener;
+ private GnssMeasurementsEvent.Callback mGnssMeasurementsEventCallback;
private String mLocationProvider;
- private IVirtualDeviceDataListener mListener = null;
+ private IVirtualDeviceDataListener mLocationDataListener = null;
+ private IVirtualDeviceDataListener mGnssMeasurementsDataListener = null;
public VirtualLocation(Context context) {
mContext = context;
}
public void registerLocationDataListener(IVirtualDeviceDataListener listener) {
- mListener = listener;
+ mLocationDataListener = listener;
+ }
+
+ public void registerGnssMeasurementsDataListener(IVirtualDeviceDataListener listener) {
+ mGnssMeasurementsDataListener = listener;
}
@RequiresApi(api = Build.VERSION_CODES.P)
@SuppressLint("MissingPermission")
- public void requestLocationUpdates(CloudPhonePermissionRequestListener listener) {
+ public void requestUpdates(CloudPhonePermissionRequestListener listener, int type) {
if ((ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
&& (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
CASLog.i(TAG, "request coarse and fine location permission");
@@ -92,52 +124,84 @@ public class VirtualLocation {
if (mLocationManager == null) {
mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
}
- boolean locationEnable = mLocationManager.isLocationEnabled();
- if (!locationEnable) {
- CASLog.i(TAG, "location enable is false");
- Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
- mContext.startActivity(intent);
- return;
+ if (type == TYPE_LOCATION) {
+ requestLocationUpdates();
+ } else if (type == TYPE_GNSS_MEASUREMENT) {
+ requestGnssMeasurementUpdates();
}
+ }
+ }
- List providers = mLocationManager.getProviders(true);
- if (providers.contains(LocationManager.GPS_PROVIDER)) {
- mLocationProvider = LocationManager.GPS_PROVIDER;
- } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
- mLocationProvider = LocationManager.NETWORK_PROVIDER;
- } else {
- return;
- }
- CASLog.i(TAG, "location provider is " + mLocationProvider);
- mLocationListener = new LocationListener() {
- @Override
- public void onLocationChanged(Location location) {
- if (location != null) {
- mLocation = location;
- String locationInfo = getLocationInfo(location);
- mListener.onRecvData(locationInfo);
- }
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ @SuppressLint("MissingPermission")
+ private void requestLocationUpdates() {
+ if (mLocationManager == null) {
+ return;
+ }
+ boolean locationEnable = mLocationManager.isLocationEnabled();
+ if (!locationEnable) {
+ CASLog.i(TAG, "location enable is false");
+ Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
+ mContext.startActivity(intent);
+ return;
+ }
+ List providers = mLocationManager.getProviders(true);
+ if (providers.contains(LocationManager.GPS_PROVIDER)) {
+ mLocationProvider = LocationManager.GPS_PROVIDER;
+ } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
+ mLocationProvider = LocationManager.NETWORK_PROVIDER;
+ } else {
+ return;
+ }
+ CASLog.i(TAG, "location provider is " + mLocationProvider);
+ mLocationListener = new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ if (location != null) {
+ mLocation = location;
+ String locationInfo = getLocationInfo(location);
+ mLocationDataListener.onRecvData(locationInfo);
}
+ }
- @Override
- public void onStatusChanged(String s, int i, Bundle bundle) {
- }
+ @Override
+ public void onStatusChanged(String s, int i, Bundle bundle) {
+ }
- @Override
- public void onProviderEnabled(String s) {
- }
+ @Override
+ public void onProviderEnabled(String s) {
+ }
- @Override
- public void onProviderDisabled(String s) {
- }
- };
- mLocationManager.requestLocationUpdates(mLocationProvider, MIN_TIME, MIN_DISTANCE, mLocationListener, Looper.getMainLooper());
- mLocation = mLocationManager.getLastKnownLocation(mLocationProvider);
- String locationInfo = getLocationInfo(mLocation);
- if (locationInfo != null) {
- mListener.onRecvData(locationInfo);
+ @Override
+ public void onProviderDisabled(String s) {
}
+ };
+ mLocationManager.requestLocationUpdates(mLocationProvider, MIN_TIME, MIN_DISTANCE, mLocationListener, Looper.getMainLooper());
+ mLocation = mLocationManager.getLastKnownLocation(mLocationProvider);
+ String locationInfo = getLocationInfo(mLocation);
+ if (locationInfo != null) {
+ mLocationDataListener.onRecvData(locationInfo);
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void requestGnssMeasurementUpdates() {
+ if (mLocationManager == null) {
+ return;
}
+ mGnssMeasurementsEventCallback = new GnssMeasurementsEvent.Callback() {
+ @Override
+ public void onGnssMeasurementsReceived(GnssMeasurementsEvent eventArgs) {
+ super.onGnssMeasurementsReceived(eventArgs);
+ try {
+ handleReceivedGnssMeasurements(eventArgs);
+ } catch (JSONException e) {
+ CASLog.e(TAG, "Failed to handle received gnss measurements");
+ }
+ }
+ };
+ mLocationManager.registerGnssMeasurementsCallback(mGnssMeasurementsEventCallback);
}
@SuppressLint("MissingPermission")
@@ -150,6 +214,16 @@ public class VirtualLocation {
}
}
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ public void closeGnssMeasurementUpdates() {
+ if (mLocationManager != null) {
+ mLocationManager.unregisterGnssMeasurementsCallback(mGnssMeasurementsEventCallback);
+ }
+ if (mGnssMeasurementsEventCallback != null) {
+ mGnssMeasurementsEventCallback = null;
+ }
+ }
+
private String getLocationInfo(Location location) {
if (location == null) {
return null;
@@ -223,6 +297,151 @@ public class VirtualLocation {
return null;
}
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void handleReceivedGnssMeasurements(GnssMeasurementsEvent event) throws JSONException {
+ JSONObject gnssMesurementsJson = new JSONObject();
+
+ JSONObject gnssClockJsonObject = gnssClockToJsonObject(event.getClock());
+ gnssMesurementsJson.put("clock", gnssClockJsonObject);
+
+ JSONArray gnssMesurementsJsonArray = new JSONArray();
+ JSONObject jsonObject;
+ List measurements = (List) event.getMeasurements();
+ // 云手机接口文档最大64个,取64个卫星,当大于64时如何选择
+ int measurementCount = measurements.size() <= 64 ? measurements.size() : 64;
+ for (int i = 0; i < measurementCount; i++) {
+ jsonObject = gnssMeasurementToJsonObject(measurements.get(i));
+ gnssMesurementsJsonArray.put(jsonObject);
+ }
+
+ gnssMesurementsJson.put("measurements", gnssMesurementsJsonArray);
+ mGnssMeasurementsDataListener.onRecvData(gnssMesurementsJson.toString());
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private JSONObject gnssClockToJsonObject(GnssClock gnssClock) throws JSONException {
+ DecimalFormat numberFormat = new DecimalFormat("#.#");
+ JSONObject jsonObject = new JSONObject();
+ int flags = 0;
+
+ if (gnssClock.hasLeapSecond()) {
+ flags = flags | HAS_LEAP_SECOND;
+ jsonObject.put("leapSecond", gnssClock.getLeapSecond());
+ }
+
+ jsonObject.put("timeNs", gnssClock.getTimeNanos());
+
+ if (gnssClock.hasTimeUncertaintyNanos()) {
+ flags = flags | HAS_TIME_UNCERTAINTY;
+// jsonObject.put("timeUncertaintyNs", numberFormat.format(gnssClock.getTimeUncertaintyNanos()));
+ jsonObject.put("timeUncertaintyNs", gnssClock.getTimeUncertaintyNanos());
+ }
+
+ if (gnssClock.hasFullBiasNanos()) {
+ flags = flags | HAS_FULL_BIAS;
+ jsonObject.put("fullBiasNs", gnssClock.getFullBiasNanos());
+ }
+
+ if (gnssClock.hasBiasNanos()) {
+ flags = flags | HAS_BIAS;
+// jsonObject.put("biasNs", numberFormat.format(gnssClock.getBiasNanos()));
+ jsonObject.put("biasNs", gnssClock.getBiasNanos());
+ }
+
+ if (gnssClock.hasBiasUncertaintyNanos()) {
+ flags = flags | HAS_BIAS_UNCERTAINTY;
+// jsonObject.put("biasUncertaintyNs", numberFormat.format(gnssClock.getBiasUncertaintyNanos()));
+ jsonObject.put("biasUncertaintyNs", gnssClock.getBiasUncertaintyNanos());
+ }
+
+ if (gnssClock.hasDriftNanosPerSecond()) {
+ flags = flags | HAS_DRIFT;
+// jsonObject.put("driftNsps", numberFormat.format(gnssClock.getDriftNanosPerSecond()));
+ jsonObject.put("driftNsps", gnssClock.getDriftNanosPerSecond());
+ }
+
+ if (gnssClock.hasDriftUncertaintyNanosPerSecond()) {
+ flags = flags | HAS_DRIFT_UNCERTAINTY;
+// jsonObject.put("driftUncertaintyNsps", numberFormat.format(gnssClock.getDriftUncertaintyNanosPerSecond()));
+ jsonObject.put("driftUncertaintyNsps", gnssClock.getDriftUncertaintyNanosPerSecond());
+ }
+
+ jsonObject.put("hwClockDiscontinuityCount", gnssClock.getHardwareClockDiscontinuityCount());
+ jsonObject.put("flags", flags);
+ return jsonObject;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private JSONObject gnssMeasurementToJsonObject(GnssMeasurement measurement) throws JSONException {
+ DecimalFormat numberFormat = new DecimalFormat("#.#");
+ JSONObject jsonObject = new JSONObject();
+ int flags = 0;
+
+ jsonObject.put("svid", measurement.getSvid());
+ jsonObject.put("constellation", measurement.getConstellationType());
+// jsonObject.put("timeOffsetNs", numberFormat.format(measurement.getTimeOffsetNanos()));
+ jsonObject.put("timeOffsetNs", measurement.getTimeOffsetNanos());
+ jsonObject.put("state", measurement.getState());
+ jsonObject.put("receivedSvTimeInNs", measurement.getReceivedSvTimeNanos());
+ jsonObject.put("receivedSvTimeUncertaintyInNs", measurement.getReceivedSvTimeUncertaintyNanos());
+// jsonObject.put("cN0Dbhz", numberFormat.format(measurement.getCn0DbHz()));
+// jsonObject.put("pseudorangeRateMps", numberFormat.format(measurement.getPseudorangeRateMetersPerSecond()));
+// jsonObject.put("pseudorangeRateUncertaintyMps", numberFormat.format(measurement.getPseudorangeRateUncertaintyMetersPerSecond()));
+
+ jsonObject.put("cN0Dbhz", measurement.getCn0DbHz());
+ jsonObject.put("pseudorangeRateMps", measurement.getPseudorangeRateMetersPerSecond());
+ jsonObject.put("pseudorangeRateUncertaintyMps", measurement.getPseudorangeRateUncertaintyMetersPerSecond());
+
+ jsonObject.put("accumulatedDeltaRangeState", measurement.getAccumulatedDeltaRangeState());
+// jsonObject.put("accumulatedDeltaRangeM", numberFormat.format(measurement.getAccumulatedDeltaRangeMeters()));
+// jsonObject.put("accumulatedDeltaRangeUncertaintyM", numberFormat.format(measurement.getAccumulatedDeltaRangeUncertaintyMeters()));
+
+ jsonObject.put("accumulatedDeltaRangeM", measurement.getAccumulatedDeltaRangeMeters());
+ jsonObject.put("accumulatedDeltaRangeUncertaintyM", measurement.getAccumulatedDeltaRangeUncertaintyMeters());
+
+ if (measurement.hasCarrierFrequencyHz()) {
+ flags = flags | HAS_CARRIER_FREQUENCY;
+// jsonObject.put("carrierFrequencyHz", numberFormat.format(measurement.getCarrierFrequencyHz()));
+ jsonObject.put("carrierFrequencyHz", measurement.getCarrierFrequencyHz());
+ }
+
+ if (measurement.hasCarrierCycles()) {
+ flags = flags | HAS_CARRIER_CYCLES;
+ jsonObject.put("carrierCycles", measurement.getCarrierCycles());
+ }
+
+ if (measurement.hasCarrierPhase()) {
+ flags = flags | HAS_CARRIER_PHASE;
+// jsonObject.put("carrierPhase", numberFormat.format(measurement.getCarrierPhase()));
+ jsonObject.put("carrierPhase", measurement.getCarrierPhase());
+ }
+
+ if (measurement.hasCarrierPhaseUncertainty()) {
+ flags = flags | HAS_CARRIER_PHASE_UNCERTAINTY;
+// jsonObject.put("carrierPhaseUncertainty", numberFormat.format(measurement.getCarrierPhaseUncertainty()));
+ jsonObject.put("carrierPhaseUncertainty", measurement.getCarrierPhaseUncertainty());
+ }
+
+ jsonObject.put("multipathIndicator", measurement.getMultipathIndicator());
+
+ if (measurement.hasSnrInDb()) {
+ flags = flags | HAS_SNR;
+// jsonObject.put("snrDb", numberFormat.format(measurement.getSnrInDb()));
+ jsonObject.put("snrDb", measurement.getSnrInDb());
+ }
+
+ if (SDK_INT >= Build.VERSION_CODES.O) {
+ if (measurement.hasAutomaticGainControlLevelDb()) {
+ flags = flags | HAS_AUTOMATIC_GAIN_CONTROL;
+// jsonObject.put("agcLevelDb", numberFormat.format(measurement.getAutomaticGainControlLevelDb()));
+ jsonObject.put("agcLevelDb", measurement.getAutomaticGainControlLevelDb());
+ }
+ }
+
+ jsonObject.put("flags", flags);
+ return jsonObject;
+ }
+
private class CellInfoData {
private String cellType = "";
private int mcc = -1;
diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/location/VirtualLocationManager.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/location/VirtualLocationManager.java
index 582bedf924bd167f053fdd955735367c2bb1f83b..fc778f6122e34163146cf08a54f3e98d1d29fcf4 100644
--- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/location/VirtualLocationManager.java
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/location/VirtualLocationManager.java
@@ -11,6 +11,7 @@ import android.util.Log;
import androidx.annotation.RequiresApi;
import com.huawei.cloudphone.api.CloudPhonePermissionRequestListener;
+import com.huawei.cloudphone.common.CASLog;
import com.huawei.cloudphone.virtualdevice.common.IVirtualDeviceDataListener;
import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceManager;
import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol;
@@ -20,15 +21,24 @@ public class VirtualLocationManager extends VirtualDeviceManager {
public static final short OPT_LOCATION_OPEN_REQ = 0x0;
public static final short OPT_LOCATION_CLOSE_REQ = 0x1;
public static final short OPT_LOCATION_DATA = 0x2;
+ public static final short OPT_GNSS_MEASUREMENT_OPEN_REQ = 0x3;
+ public static final short OPT_GNSS_MEASUREMENT_CLOSE_REQ = 0x4;
+ public static final short OPT_GNSS_MEASUREMENT_DATA = 0x5;
private VirtualLocation mVirtualLocation;
private VirtualDeviceProtocol mVirtualDeviceProtocol;
private CloudPhonePermissionRequestListener mPermissionListener;
+ private boolean mIsLocationOpen = false;
+ private boolean mIsGnssMeasurementOpen = false;
+ private static final int TYPE_LOCATION = 0;
+ private static final int TYPE_GNSS_MEASUREMENT = 1;
+
public VirtualLocationManager(VirtualDeviceProtocol virtualDeviceProtocol, Context context) {
mVirtualDeviceProtocol = virtualDeviceProtocol;
mVirtualLocation = new VirtualLocation(context);
mVirtualLocation.registerLocationDataListener(new LocationDataListener());
+ mVirtualLocation.registerGnssMeasurementsDataListener(new GnssMeasurementsDataListener());
}
@Override
@@ -38,11 +48,18 @@ public class VirtualLocationManager extends VirtualDeviceManager {
@RequiresApi(api = Build.VERSION_CODES.P)
public void init() {
- mVirtualLocation.requestLocationUpdates(mPermissionListener);
+ if (mIsLocationOpen) {
+ mVirtualLocation.requestUpdates(mPermissionListener, TYPE_LOCATION);
+ }
+ if (mIsGnssMeasurementOpen) {
+ mVirtualLocation.requestUpdates(mPermissionListener, TYPE_GNSS_MEASUREMENT);
+ }
}
+ @RequiresApi(api = Build.VERSION_CODES.N)
public void stop() {
mVirtualLocation.closeLocationUpdates();
+ mVirtualLocation.closeGnssMeasurementUpdates();
}
@RequiresApi(api = Build.VERSION_CODES.P)
@@ -50,12 +67,24 @@ public class VirtualLocationManager extends VirtualDeviceManager {
switch (header.mOptType) {
case OPT_LOCATION_OPEN_REQ:
Log.i(TAG, "processMsg: open location");
- mVirtualLocation.requestLocationUpdates(mPermissionListener);
+ mIsLocationOpen = true;
+ mVirtualLocation.requestUpdates(mPermissionListener, TYPE_LOCATION);
break;
case OPT_LOCATION_CLOSE_REQ:
Log.i(TAG, "processMsg: close location");
+ mIsLocationOpen = false;
mVirtualLocation.closeLocationUpdates();
break;
+ case OPT_GNSS_MEASUREMENT_OPEN_REQ:
+ Log.i(TAG, "processMsg: open gnss measurement");
+ mIsGnssMeasurementOpen = true;
+ mVirtualLocation.requestUpdates(mPermissionListener, TYPE_GNSS_MEASUREMENT);
+ break;
+ case OPT_GNSS_MEASUREMENT_CLOSE_REQ:
+ Log.i(TAG, "processMsg: close gnss measurement");
+ mIsGnssMeasurementOpen = false;
+ mVirtualLocation.closeGnssMeasurementUpdates();
+ break;
default:
Log.e(TAG, "processMsg: error opt type");
}
@@ -68,10 +97,27 @@ public class VirtualLocationManager extends VirtualDeviceManager {
int type = 0;
int bodyLen = body.getBytes().length;
int rspMsgLen = bodyLen + MSG_HEADER_LEN;
+ CASLog.i(TAG, "location bodyLen=" + bodyLen + " body=" + body);
VirtualDeviceProtocol.MsgHeader header = new VirtualDeviceProtocol.MsgHeader(OPT_LOCATION_DATA, DEV_TYPE_LOCATION, (short) type, rspMsgLen);
byte[] rspBody = new byte[bodyLen];
System.arraycopy(body.getBytes(), 0, rspBody, 0, bodyLen);
mVirtualDeviceProtocol.sendMsg(header, rspBody, LOCATION_DATA);
}
}
+
+ class GnssMeasurementsDataListener implements IVirtualDeviceDataListener {
+
+ @Override
+ public void onRecvData(Object... args) {
+ String body = (String) args[0];
+ int type = 0;
+ int bodyLen = body.getBytes().length;
+ int rspMsgLen = bodyLen + MSG_HEADER_LEN;
+ CASLog.i(TAG, "gnss bodyLen=" + bodyLen + " body=" + body);
+ VirtualDeviceProtocol.MsgHeader header = new VirtualDeviceProtocol.MsgHeader(OPT_GNSS_MEASUREMENT_DATA, DEV_TYPE_LOCATION, (short) type, rspMsgLen);
+ byte[] rspBody = new byte[bodyLen];
+ System.arraycopy(body.getBytes(), 0, rspBody, 0, bodyLen);
+ mVirtualDeviceProtocol.sendMsg(header, rspBody, LOCATION_DATA);
+ }
+ }
}
diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/sensor/VirtualSensor.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/sensor/VirtualSensor.java
index 0580a0e03c1e37d8dadc0bc9a198939ce566b12d..e6d3aeea774408f83df75dc3677230a4b2050683 100644
--- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/sensor/VirtualSensor.java
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/sensor/VirtualSensor.java
@@ -23,65 +23,86 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
+import com.huawei.cloudphone.common.CASLog;
import com.huawei.cloudphone.virtualdevice.common.IVirtualDeviceDataListener;
+import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
public class VirtualSensor implements SensorEventListener {
private static final String TAG = "VirtualSensor";
private SensorManager mSensorManager;
- private Sensor mSensor;
- private Sensor mAccelerationSensor;
private IVirtualDeviceDataListener mListener = null;
private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private Sensor mSensor;
+ private Sensor mAccelerationSensor;
+
private boolean mIsStart = false;
+ private Map mSensorMap = new HashMap<>();
public VirtualSensor(SensorManager sensorManager) {
mSensorManager = sensorManager;
- mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mAccelerationSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+// List sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+// CASLog.i(TAG, "sensor list size " + sensorList.size());
+// for (Sensor sensor : sensorList) {
+// mSensorMap.put(sensor.getType(), mSensorManager.getDefaultSensor(sensor.getType()));
+// }
}
void registerSensorDataListener(IVirtualDeviceDataListener listener) {
mListener = listener;
}
- public void startProcess() {
- Log.i(TAG, "startProcess");
+ public void start() {
+ Log.i(TAG, "start");
if (mIsStart) {
return;
}
mHandlerThread = new HandlerThread("sensorThread");
mHandlerThread.start();
- Handler handler = new Handler(mHandlerThread.getLooper());
- mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME, handler);
- mSensorManager.registerListener(this, mAccelerationSensor, SensorManager.SENSOR_DELAY_GAME, handler);
+ mHandler = new Handler(mHandlerThread.getLooper());
mIsStart = true;
}
- public void stopProcess() {
- Log.i(TAG, "stopProcess");
+ public void stop() {
+ Log.i(TAG, "stop");
if (!mIsStart) {
return;
}
- mSensorManager.unregisterListener(this, mSensor);
- mSensorManager.unregisterListener(this, mAccelerationSensor);
mHandlerThread.getLooper().quit();
mIsStart = false;
}
+ public void startProcess(short sensorId) {
+ Log.i(TAG, "register sensor id " + sensorId);
+ if (sensorId == 1) {
+ mSensorManager.registerListener(this, mAccelerationSensor, SensorManager.SENSOR_DELAY_GAME, mHandler);
+ return;
+ }
+ mSensor = mSensorManager.getDefaultSensor(sensorId);
+ mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME, mHandler);
+ }
+
+ public void stopProcess(short sensorId) {
+ Log.i(TAG, "unregister sensor id " + sensorId);
+ if (sensorId == 1) {
+ mSensorManager.unregisterListener(this, mAccelerationSensor);
+ }
+ mSensor = mSensorManager.getDefaultSensor(sensorId);
+ mSensorManager.unregisterListener(this, mSensor);
+ }
+
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (mListener == null) {
return;
}
- float x = sensorEvent.values[0];
- float y = sensorEvent.values[1];
- float z = sensorEvent.values[2];
- int type = sensorEvent.sensor.getType();
- if (type == Sensor.TYPE_ACCELEROMETER) {
- mListener.onRecvData(x, y, z, sensorEvent.accuracy, 0);
- } else if (type == Sensor.TYPE_GYROSCOPE) {
- mListener.onRecvData(x, y, z, sensorEvent.accuracy, 1);
- }
+ mListener.onRecvData(sensorEvent);
}
@Override
diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/sensor/VirtualSensorManager.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/sensor/VirtualSensorManager.java
index 561f2d62651dbf872c99b0fa7680017c4f1fa6b6..48b9c7d03052f5b1962692d595b01fcb8ec80b7b 100644
--- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/sensor/VirtualSensorManager.java
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/sensor/VirtualSensorManager.java
@@ -19,43 +19,71 @@ import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_SENSOR;
import static com.huawei.cloudphone.jniwrapper.JNIWrapper.SENSOR_DATA;
import static com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol.MSG_HEADER_LEN;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.util.Log;
+import com.huawei.cloudphone.common.CASLog;
import com.huawei.cloudphone.virtualdevice.common.IVirtualDeviceDataListener;
import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceManager;
import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol;
import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol.MsgHeader;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
public class VirtualSensorManager extends VirtualDeviceManager {
private static final String TAG = "VirtualSensorManager";
- public static final short DEV_ACCELERATION = 0x0;
- public static final short DEV_GYROSCOPE = 0x1;
public static final short OPT_SENSOR_ENABLE_REQ = 0x1;
public static final short OPT_SENSOR_ENABLE_RSP = 0x1001;
public static final short OPT_SENSOR_DISABLE_REQ = 0x2;
public static final short OPT_SENSOR_DISABLE_RSP = 0x1002;
+ public static final short OPT_SENSOR_STATUS_RSP = 0x1003;
+ public static final short OPT_SENSOR_INFO_RSP = 0x1004;
public static final short OPT_SENSOR_DATA = 0x10;
+ private final int SENSOR_TYPE_WAKE_GESTURE = 23;
+ private final int SENSOR_TYPE_GLANCE_GESTURE = 24;
+ private final int SENSOR_TYPE_PICK_UP_GESTURE = 25;
+
private VirtualSensor mVirtualSensor;
private VirtualDeviceProtocol mVirtualDeviceProtocol;
+ private Set mOneShotSet = new HashSet(Arrays.asList(
+ Sensor.TYPE_SIGNIFICANT_MOTION, Sensor.TYPE_STATIONARY_DETECT, Sensor.TYPE_MOTION_DETECT,
+ SENSOR_TYPE_WAKE_GESTURE, SENSOR_TYPE_GLANCE_GESTURE, SENSOR_TYPE_PICK_UP_GESTURE
+ ));
public VirtualSensorManager(VirtualDeviceProtocol virtualDeviceProtocol, SensorManager sensorManager) {
mVirtualDeviceProtocol = virtualDeviceProtocol;
mVirtualSensor = new VirtualSensor(sensorManager);
+ mVirtualSensor.registerSensorDataListener(new SensorDataListener());
}
public void processMsg(MsgHeader header, byte[] body) {
+ short sensorId = header.mDeviceId;
switch (header.mOptType) {
case OPT_SENSOR_ENABLE_RSP:
- Log.i(TAG, "processMsg: enable sensor");
- mVirtualSensor.registerSensorDataListener(new SensorDataListener());
- mVirtualSensor.startProcess();
+ Log.i(TAG, "processMsg: sensor id " + sensorId + " enable");
+ mVirtualSensor.startProcess(sensorId);
break;
case OPT_SENSOR_DISABLE_RSP:
- Log.i(TAG, "processMsg: disable sensor");
- mVirtualSensor.stopProcess();
+ Log.i(TAG, "processMsg: sensor id " + sensorId + " disable");
+ mVirtualSensor.stopProcess(sensorId);
+ break;
+ case OPT_SENSOR_STATUS_RSP:
+ int status = (body[0] << 8) | (body[1] & 0x0FF);
+ Log.i(TAG, "processMsg: sensor id " + sensorId + " status " + status);
+ MsgHeader msgHeader;
+ if (status != 0) {
+ msgHeader = new MsgHeader(OPT_SENSOR_ENABLE_REQ, DEV_TYPE_SENSOR, sensorId, MSG_HEADER_LEN);
+ mVirtualDeviceProtocol.sendMsg(msgHeader, null, SENSOR_DATA);
+ } else {
+ header = new MsgHeader(OPT_SENSOR_DISABLE_REQ, DEV_TYPE_SENSOR, sensorId, MSG_HEADER_LEN);
+ mVirtualDeviceProtocol.sendMsg(header, null, SENSOR_DATA);
+ }
break;
default:
Log.e(TAG, "processMsg: error opt type");
@@ -63,38 +91,40 @@ public class VirtualSensorManager extends VirtualDeviceManager {
}
public void start() {
- int rspMsgLen = MSG_HEADER_LEN;
- MsgHeader header = new MsgHeader(OPT_SENSOR_ENABLE_REQ, DEV_TYPE_SENSOR, DEV_GYROSCOPE, rspMsgLen);
- mVirtualDeviceProtocol.sendMsg(header, null, SENSOR_DATA);
- header = new MsgHeader(OPT_SENSOR_ENABLE_REQ, DEV_TYPE_SENSOR, DEV_ACCELERATION, rspMsgLen);
- mVirtualDeviceProtocol.sendMsg(header, null, SENSOR_DATA);
+ Log.e(TAG, "start");
+ mVirtualSensor.start();
}
public void stop() {
- mVirtualSensor.stopProcess();
- int rspMsgLen = MSG_HEADER_LEN;
- MsgHeader header = new MsgHeader(OPT_SENSOR_DISABLE_REQ, DEV_TYPE_SENSOR, DEV_GYROSCOPE, rspMsgLen);
- mVirtualDeviceProtocol.sendMsg(header, null, SENSOR_DATA);
- header = new MsgHeader(OPT_SENSOR_DISABLE_REQ, DEV_TYPE_SENSOR, DEV_ACCELERATION, rspMsgLen);
- mVirtualDeviceProtocol.sendMsg(header, null, SENSOR_DATA);
+ Log.e(TAG, "stop");
+ mVirtualSensor.stop();
}
class SensorDataListener implements IVirtualDeviceDataListener {
@Override
public void onRecvData(Object... args) {
- String xStr = Float.valueOf((float) args[0]).toString();
- String yStr = Float.valueOf((float) args[1]).toString();
- String zStr = Float.valueOf((float) args[2]).toString();
- String accurateStr = Integer.valueOf((int) args[3]).toString();
- String body = xStr + ":" + yStr + ":" + zStr + ":" + accurateStr + ":";
- int type = (int) args[4];
+ SensorEvent sensorEvent = (SensorEvent) args[0];
+ StringBuilder stringBuilder = new StringBuilder();
+ for (int i = 0; i < sensorEvent.values.length; i++) {
+ stringBuilder.append(Float.valueOf(sensorEvent.values[i]).toString() + ":");
+ }
+ stringBuilder.append(Integer.valueOf(sensorEvent.accuracy).toString() + ":");
+ String body = stringBuilder.toString();
+ int sensorId = sensorEvent.sensor.getType();
int bodyLen = body.getBytes().length;
int rspMsgLen = bodyLen + MSG_HEADER_LEN;
- MsgHeader header = new MsgHeader(OPT_SENSOR_DATA, DEV_TYPE_SENSOR, (short)type, rspMsgLen);
+ CASLog.i(TAG, "sensorid=" + sensorId + " bodyLen=" + bodyLen + " body=" + body);
+ MsgHeader header = new MsgHeader(OPT_SENSOR_DATA, DEV_TYPE_SENSOR, (short)sensorId, rspMsgLen);
byte[] rspBody = new byte[bodyLen];
System.arraycopy(body.getBytes(), 0, rspBody, 0, bodyLen);
mVirtualDeviceProtocol.sendMsg(header, rspBody, SENSOR_DATA);
+ // oneshot模式需主动发送0x2
+ if (mOneShotSet.contains(sensorId)) {
+ header = new MsgHeader(OPT_SENSOR_DISABLE_REQ, DEV_TYPE_SENSOR, (short)sensorId, MSG_HEADER_LEN);
+ mVirtualDeviceProtocol.sendMsg(header, null, SENSOR_DATA);
+ mVirtualSensor.stopProcess((short)sensorId);
+ }
}
}
-}
+}
\ No newline at end of file
diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/vibrator/VirtualVibrator.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/vibrator/VirtualVibrator.java
new file mode 100644
index 0000000000000000000000000000000000000000..e795dc7903b13ae4246201f531653aae98642797
--- /dev/null
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/vibrator/VirtualVibrator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.huawei.cloudphone.virtualdevice.vibrator;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Vibrator;
+
+import com.huawei.cloudphone.common.CASLog;
+import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol;
+
+public class VirtualVibrator {
+ private static final String TAG = "VirtualVibrator";
+
+ private Context mContext;
+ private Vibrator mVibrator;
+
+ public VirtualVibrator(Context context) {
+ mContext = context;
+ mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ }
+
+ @SuppressLint("MissingPermission")
+ public void startVibrate(VirtualDeviceProtocol.MsgHeader header, byte[] body) {
+ if (!mVibrator.hasVibrator()) {
+ return;
+ }
+ long vibrateTime = ((body[0] << 24) | (body[1] << 16) | (body[2] << 8) | (body[3]));
+ CASLog.i(TAG, "vibrate time is " + vibrateTime);
+ mVibrator.cancel();
+// mVibrator.vibrate(vibrateTime);
+ }
+
+ @SuppressLint("MissingPermission")
+ public void stopVibrate() {
+ if (!mVibrator.hasVibrator()) {
+ return;
+ }
+ mVibrator.cancel();
+ }
+}
\ No newline at end of file
diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/vibrator/VirtualVibratorManager.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/vibrator/VirtualVibratorManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..dd522a7af635499bd978d756ad921352e12b1f69
--- /dev/null
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/vibrator/VirtualVibratorManager.java
@@ -0,0 +1,36 @@
+package com.huawei.cloudphone.virtualdevice.vibrator;
+
+import android.content.Context;
+
+import com.huawei.cloudphone.common.CASLog;
+import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceManager;
+import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol;
+
+public class VirtualVibratorManager extends VirtualDeviceManager {
+ private static final String TAG = "VirtualVibratorManager";
+ public static final short OPT_VIBRATOR_START_REQ = 0x1;
+ public static final short OPT_VIBRATOR_STOP_REQ = 0x2;
+
+ private VirtualVibrator mVirtualVibrator;
+ private VirtualDeviceProtocol mVirtualDeviceProtocol;
+
+ public VirtualVibratorManager(VirtualDeviceProtocol virtualDeviceProtocol, Context context) {
+ mVirtualDeviceProtocol = virtualDeviceProtocol;
+ mVirtualVibrator = new VirtualVibrator(context);
+ }
+
+ public void processMsg(VirtualDeviceProtocol.MsgHeader header, byte[] body) {
+ switch (header.mOptType) {
+ case OPT_VIBRATOR_START_REQ:
+ CASLog.i(TAG, "processMsg: start vibrate");
+ mVirtualVibrator.startVibrate(header, body);
+ break;
+ case OPT_VIBRATOR_STOP_REQ:
+ CASLog.i(TAG, "processMsg: stop vibrate");
+ mVirtualVibrator.stopVibrate();
+ break;
+ default:
+ CASLog.e(TAG, "processMsg: error opt type");
+ }
+ }
+}