diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c9378b254649c8cb01dc7f28a41e33d94dc37a3f..5575ca1a46bee3c88af2757865851f4567af68f5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -9,6 +9,8 @@
+
+
0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
this.sendBroadcast(new Intent(GRANT_MICROPHONE_PERMISSION_SUCCESS_ACTION));
}
+ } else if (requestCode == DEV_TYPE_LOCATION) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ this.sendBroadcast(new Intent(GRANT_LOCATION_PERMISSION_SUCCESS_ACTION));
+ }
}
}
diff --git a/cloudphone/src/main/cpp/cas_common/CasMsg.h b/cloudphone/src/main/cpp/cas_common/CasMsg.h
index fcea1f5be0777b34083a66d444822351f2e52fe8..f591ff846bf06edc253bc95cfe1d0f24a3088df5 100644
--- a/cloudphone/src/main/cpp/cas_common/CasMsg.h
+++ b/cloudphone/src/main/cpp/cas_common/CasMsg.h
@@ -38,6 +38,7 @@ enum CasMsgType : uint8_t {
VirtualCamera = 21,
VirtualMicrophone = 22,
VirtualSensor = 23,
+ VirtualLocation = 24,
End,
};
@@ -61,6 +62,7 @@ enum CasMsgType : uint8_t {
#define CAS_MSG_CHECKSUM_VIRTUAL_CAMERA GET_CAS_CHECKSUM(CasMsgType::VirtualCamera)
#define CAS_MSG_CHECKSUM_VIRTUAL_MICROPHONE GET_CAS_CHECKSUM(CasMsgType::VirtualMicrophone)
#define CAS_MSG_CHECKSUM_VIRTUAL_SENSOR GET_CAS_CHECKSUM(CasMsgType::VirtualSensor)
+#define CAS_MSG_CHECKSUM_VIRTUAL_LOCATION GET_CAS_CHECKSUM(CasMsgType::VirtualLocation)
// 客户端通用消息头
typedef struct streamMsgHead {
diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp
index 92bbf8a97847c424df782db2945b48f1be80f778..f857d95782c46f59b02e8bc852850148b49c6504 100644
--- a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp
+++ b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp
@@ -84,6 +84,9 @@ int CasStreamBuildSender::SendDataToServer(CasMsgType type, const void *buf, siz
case (VirtualSensor):
msgHead.checksum = CAS_MSG_CHECKSUM_VIRTUAL_SENSOR;
break;
+ case (VirtualLocation):
+ msgHead.checksum = CAS_MSG_CHECKSUM_VIRTUAL_LOCATION;
+ break;
default: {
return -1;
}
diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp
index 60c1257cef0d7331ff5601900387e9f2673c5d21..09746b90d9944d7f2f12a1a03801807f5d773186 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 VirtualSensor >= type && type >= VirtualCamera ? m_serviceHandles[VirtualDevice] : m_serviceHandles[type];
+ return VirtualLocation >= 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/jniwrapper/JNIWrapper.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java
index 0ea60bdabcb5872f44cddc716d3088eb9337bc9c..99ccd8afdefaf9baecd15787451efe6bdab6b53b 100644
--- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java
@@ -56,6 +56,7 @@ public class JNIWrapper {
public static final byte CAMERA_DATA = 21;
public static final byte MICROPHONE_DATA = 22;
public static final byte SENSOR_DATA = 23;
+ public static final byte LOCATION_DATA = 24;
static {
System.loadLibrary("cloudapp");
diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceManager.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceManager.java
index 48d8d5e4e3d0013b6358d8ec194b6ee743e6c4b2..5c046833a4e80d92bb68041556fc4ceb7185623d 100644
--- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceManager.java
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/VirtualDeviceManager.java
@@ -21,6 +21,7 @@ public class VirtualDeviceManager {
public static final short DEV_TYPE_CAMERA = 1;
public static final short DEV_TYPE_MICROPHONE = 2;
public static final short DEV_TYPE_SENSOR = 0;
+ public static final short DEV_TYPE_LOCATION = 4;
public void processMsg(MsgHeader header, byte[] body) {
}
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 f4bfb0b06c73a906a148eae7dd7ab46a11b3bad1..1e5c85543585cb5b695cc78a098a6cdec26c2a2b 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
@@ -17,8 +17,10 @@ package com.huawei.cloudphone.virtualdevice.common;
import static com.huawei.cloudphone.virtualdevice.camera.VirtualCameraManager.DEV_TYPE_CAMERA;
import static com.huawei.cloudphone.virtualdevice.camera.VirtualCameraManager.GRANT_CAMERA_PERMISSION_SUCCESS_ACTION;
+import static com.huawei.cloudphone.virtualdevice.common.VirtualDeviceManager.DEV_TYPE_LOCATION;
import static com.huawei.cloudphone.virtualdevice.common.VirtualDeviceManager.DEV_TYPE_MICROPHONE;
import static com.huawei.cloudphone.virtualdevice.common.VirtualDeviceManager.DEV_TYPE_SENSOR;
+import static com.huawei.cloudphone.virtualdevice.location.VirtualLocationManager.GRANT_LOCATION_PERMISSION_SUCCESS_ACTION;
import static com.huawei.cloudphone.virtualdevice.microphone.VirtualMicrophoneManager.GRANT_MICROPHONE_PERMISSION_SUCCESS_ACTION;
import android.content.BroadcastReceiver;
@@ -29,6 +31,8 @@ import android.hardware.SensorManager;
import android.util.Log;
import com.huawei.cloudphone.virtualdevice.camera.VirtualCameraManager;
+import com.huawei.cloudphone.virtualdevice.location.VirtualLocation;
+import com.huawei.cloudphone.virtualdevice.location.VirtualLocationManager;
import com.huawei.cloudphone.virtualdevice.microphone.VirtualMicrophoneManager;
import com.huawei.cloudphone.virtualdevice.sensor.VirtualSensorManager;
@@ -52,6 +56,8 @@ public class VirtualDeviceProtocol {
((VirtualCameraManager)virtualDeviceManagers.get(DEV_TYPE_CAMERA)).initCamera();
} else if (GRANT_MICROPHONE_PERMISSION_SUCCESS_ACTION.equals(intent.getAction())) {
((VirtualMicrophoneManager)virtualDeviceManagers.get(DEV_TYPE_MICROPHONE)).initMicrophone();
+ } else if (GRANT_LOCATION_PERMISSION_SUCCESS_ACTION.equals(intent.getAction())) {
+ ((VirtualLocationManager)virtualDeviceManagers.get(DEV_TYPE_LOCATION)).initLocation();
}
}
};
@@ -68,10 +74,12 @@ public class VirtualDeviceProtocol {
virtualDeviceManagers.put(DEV_TYPE_MICROPHONE, new VirtualMicrophoneManager(this, mContext));
virtualDeviceManagers.put(DEV_TYPE_SENSOR, new VirtualSensorManager(this,
(SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE)));
+ virtualDeviceManagers.put(DEV_TYPE_LOCATION, new VirtualLocationManager(this, mContext));
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(GRANT_CAMERA_PERMISSION_SUCCESS_ACTION);
intentFilter.addAction(GRANT_MICROPHONE_PERMISSION_SUCCESS_ACTION);
+ intentFilter.addAction(GRANT_LOCATION_PERMISSION_SUCCESS_ACTION);
mContext.registerReceiver(mPermissionResultReceiver, intentFilter);
}
@@ -84,7 +92,6 @@ public class VirtualDeviceProtocol {
virtualDeviceManager.processMsg(header, body);
}
-
public void startProcess() {
mIsTaskRun = true;
mPktProcessThread = new PacketParseThread();
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
new file mode 100644
index 0000000000000000000000000000000000000000..a5a09f33b3e05bbf2abe9de36e4c97705b2cf1d6
--- /dev/null
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/location/VirtualLocation.java
@@ -0,0 +1,230 @@
+/*
+ * 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.location;
+
+import static android.os.Build.VERSION.SDK_INT;
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
+import static com.huawei.cloudphone.virtualdevice.common.VirtualDeviceManager.DEV_TYPE_LOCATION;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Looper;
+import android.provider.Settings;
+import android.telephony.CellIdentityCdma;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityWcdma;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoCdma;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoWcdma;
+import android.telephony.TelephonyManager;
+
+import androidx.annotation.RequiresApi;
+import androidx.core.app.ActivityCompat;
+
+import com.huawei.cloudphone.common.CASLog;
+import com.huawei.cloudphone.virtualdevice.common.IVirtualDeviceDataListener;
+
+import java.util.ArrayList;
+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 Context mContext;
+ private Location mLocation;
+ private LocationManager mLocationManager;
+ private LocationListener mLocationListener;
+ private String mLocationProvider;
+ private IVirtualDeviceDataListener mListener = null;
+
+ public VirtualLocation(Context context) {
+ mContext = context;
+ }
+
+ public void registerLocationDataListener(IVirtualDeviceDataListener listener) {
+ mListener = listener;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ @SuppressLint("MissingPermission")
+ public void requestLocationUpdates() {
+ 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");
+ ActivityCompat.requestPermissions((Activity) mContext,
+ new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION},
+ DEV_TYPE_LOCATION);
+ } else {
+ 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;
+ }
+
+ 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);
+ }
+ }
+
+ @Override
+ public void onStatusChanged(String s, int i, Bundle bundle) {
+ }
+
+ @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);
+ }
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ public void closeLocationUpdates() {
+ if (mLocationManager != null) {
+ mLocationManager.removeUpdates(mLocationListener);
+ }
+ if (mLocationListener != null) {
+ mLocationListener = null;
+ }
+ }
+
+ private String getLocationInfo(Location location) {
+ if (location == null) {
+ return null;
+ }
+ String telephonyCellInfo = getTelephonyCellInfo();
+ String locationInfo = "longitude=" + location.getLongitude() +
+ ":latitude=" + location.getLatitude() +
+ ":altitude=" + location.getAltitude() +
+ ":speed=" + location.getSpeed() +
+ ":bearing=" + location.getBearing() +
+ ":accuracy=" + location.getAccuracy();
+ if (telephonyCellInfo != null) {
+ locationInfo = locationInfo + ":" + telephonyCellInfo;
+ }
+ return locationInfo;
+ }
+
+ @SuppressLint("MissingPermission")
+ private String getTelephonyCellInfo() {
+ TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ String operator = telephonyManager.getNetworkOperator();
+ int mcc = -1;
+ if (operator != null && operator.length() == 3) {
+ mcc = Integer.parseInt(operator.substring(0, 3));
+ }
+
+ List cellInfoDataList = new ArrayList<>();
+ List cellInfoList = telephonyManager.getAllCellInfo();
+ for (CellInfo cellInfo : cellInfoList) {
+ CellInfoData cellInfoData = new CellInfoData();
+ cellInfoData.mcc = mcc;
+ if (cellInfo instanceof CellInfoCdma) {
+ CellInfoCdma cellInfoCdma = (CellInfoCdma) cellInfo;
+ CellIdentityCdma cellIdentityCdma = cellInfoCdma.getCellIdentity();
+ cellInfoData.mnc = cellIdentityCdma.getSystemId();
+ cellInfoData.lac = cellIdentityCdma.getNetworkId();
+ cellInfoData.cid = cellIdentityCdma.getBasestationId();
+ cellInfoData.cellType = "CDMA";
+ } else if (cellInfo instanceof CellInfoGsm) {
+ CellInfoGsm cellInfoGsm = (CellInfoGsm) cellInfo;
+ CellIdentityGsm cellIdentityGsm = cellInfoGsm.getCellIdentity();
+ cellInfoData.mnc = cellIdentityGsm.getMnc();
+ cellInfoData.lac = cellIdentityGsm.getLac();
+ cellInfoData.cid = cellIdentityGsm.getCid();
+ cellInfoData.cellType = "GSM";
+ } else if (cellInfo instanceof CellInfoLte) {
+ CellInfoLte cellInfoLte = (CellInfoLte) cellInfo;
+ CellIdentityLte cellIdentityLte = cellInfoLte.getCellIdentity();
+ cellInfoData.mnc = cellIdentityLte.getMnc();
+ cellInfoData.lac = cellIdentityLte.getTac();
+ cellInfoData.cid = cellIdentityLte.getCi();
+ cellInfoData.cellType = "LTE";
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) cellInfo;
+ CellIdentityWcdma cellIdentityWcdma = null;
+ if (SDK_INT >= JELLY_BEAN_MR2) {
+ cellIdentityWcdma = cellInfoWcdma.getCellIdentity();
+ cellInfoData.mnc = cellIdentityWcdma.getMnc();
+ cellInfoData.lac = cellIdentityWcdma.getLac();
+ cellInfoData.cid = cellIdentityWcdma.getCid();
+ }
+ cellInfoData.cellType = "WCDMA";
+ } else {
+ return null;
+ }
+ cellInfoDataList.add(cellInfoData);
+ }
+ if (cellInfoDataList.size() > 0) {
+ return cellInfoDataList.get(0).getCellInfo();
+ }
+ return null;
+ }
+
+ private class CellInfoData {
+ private String cellType = "";
+ private int mcc = -1;
+ private int mnc = -1;
+ private int lac = -1;
+ private int cid = -1;
+
+ private String getCellInfo() {
+ return String.format("cell_type=%s:mcc=%d:mnc=%d:lac=%d:cid=%d", cellType, mcc, mnc, lac, cid);
+ }
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000000000000000000000000000000000000..c062b1fa1615a7c2015af530ec9753c43f74b198
--- /dev/null
+++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/location/VirtualLocationManager.java
@@ -0,0 +1,71 @@
+package com.huawei.cloudphone.virtualdevice.location;
+
+import static com.huawei.cloudphone.jniwrapper.JNIWrapper.LOCATION_DATA;
+import static com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol.MSG_HEADER_LEN;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+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;
+
+public class VirtualLocationManager extends VirtualDeviceManager {
+ private static final String TAG = "VirtualLocationManager";
+ 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 String GRANT_LOCATION_PERMISSION_SUCCESS_ACTION = "android.intent.action.GRANT_LOCATION_PERMISSION_SUCCESS";
+
+ private VirtualLocation mVirtualLocation;
+ private VirtualDeviceProtocol mVirtualDeviceProtocol;
+
+ public VirtualLocationManager(VirtualDeviceProtocol virtualDeviceProtocol, Context context) {
+ mVirtualDeviceProtocol = virtualDeviceProtocol;
+ mVirtualLocation = new VirtualLocation(context);
+ mVirtualLocation.registerLocationDataListener(new LocationDataListener());
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ public void initLocation() {
+ mVirtualLocation.requestLocationUpdates();
+ }
+
+ public void stop() {
+ mVirtualLocation.closeLocationUpdates();
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ public void processMsg(VirtualDeviceProtocol.MsgHeader header, byte[] body) {
+ switch (header.mOptType) {
+ case OPT_LOCATION_OPEN_REQ:
+ Log.i(TAG, "processMsg: open location");
+ mVirtualLocation.requestLocationUpdates();
+ break;
+ case OPT_LOCATION_CLOSE_REQ:
+ Log.i(TAG, "processMsg: close location");
+ mVirtualLocation.closeLocationUpdates();
+ break;
+ default:
+ Log.e(TAG, "processMsg: error opt type");
+ }
+ }
+
+ class LocationDataListener 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;
+ 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);
+ }
+ }
+}