中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

在Android中要怎么實現模擬定位

發布時間:2022-02-25 14:41:18 來源:億速云 閱讀:263 作者:小新 欄目:開發技術

小編給大家分享一下在Android中要怎么實現模擬定位,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

在導航測試場景中經常需要定位模擬和路線回放,記錄下通過LocationManager.setTestProviderLocation()方法實現模擬地位,如果要測試的應用不支持TestProviderLocation模擬位置輸入,可以考慮從HAL層入手,hook系統默認的GPS實現。

一、Android模擬權限開啟配置

在Android6.0以下的版本中,需要在設置中勾選模擬定位的開關,在6.0以上就改成了選擇模擬定位的應用,對應的開啟配置方式也不一樣,相同的是在AndroidManifest.xml都需要配置以下兩個權限:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

1)Android 6.0以下開啟模擬定位開關

Settings.Secure.putInt(getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 1);

通過這種方式去開啟模擬定位需要在AndroidManifest.xml中配置以下系統權限,應用還需要經過系統簽名,對于非系統應用不能通過這種實現方式。

android:sharedUserId="android.uid.system"

<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />

涉及到這種操作可以通過adb shell命令的方式配置來繞過系統權限配置:

adb shell settings put secure mock_location 1

2)Android 6.0以上代碼配置選擇模擬定位的應用

在6.0以上的Android版本就需要設置指定包名的mock_location權限為allow。

try {
    String mockLocationPkgName = getPackageName();
    PackageManager mPackageManager = getPackageManager();
    final ApplicationInfo ai = mPackageManager.getApplicationInfo(
            mockLocationPkgName, PackageManager.MATCH_DISABLED_COMPONENTS);
    AppOpsManager mAppsOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
    mAppsOpsManager.setMode(AppOpsManager.OPSTR_MOCK_LOCATION, ai.uid,
            mockLocationPkgName, AppOpsManager.MODE_ALLOWED);
} catch (PackageManager.NameNotFoundException e) {
    /* ignore */
}

同時在AndroidManifest.xml中還需要配置如下權限,可惜這種方式也是需要經過系統簽名和源碼一起編譯,只有系統層級的應用才可以使用。

<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />

為了在設置界面模擬定位選項中顯示模擬定位的應用,需要配置ACCESS_MOCK_LOCATION權限。

<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>

以上配置也可以通過adb shell命令去實現,<package> 參數用自己應用的包名替換。

adb shell appops set <package> android:mock_location allow

關閉模擬定位權限,用如下命令:

adb shell appops set <package> android:mock_location deny

要查詢有哪些應用開啟了模擬定位權限,用如下命令:

adb shell appops query-op android:mock_location allow

執行后會輸出應用的包名列表參數。

二、Android模擬定位實現

1)模擬定位開關檢查

首先是代碼中先判斷模擬定位權限是否開啟,6.0以上的只能通過添加定位監聽是否有異常來判斷。

boolean mockPermission = false;
if (Build.VERSION.SDK_INT <= 22) {//6.0以下
    mockPermission = Settings.Secure.getInt(getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
} else {
    try {
        LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            mockPermission = false;            
            return;
        }
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
            }
            @Override
            public void onStatusChanged(String s, int i, Bundle bundle) {
            }
            @Override
            public void onProviderEnabled(String s) {
            }
            @Override
            public void onProviderDisabled(String s) {
            }
        });
        mockPermission = true;
    } catch (SecurityException e) {
        mockPermission = false;
    }
}

可以看下添加定位監聽的源碼實現,以Android 7.0的源碼實現做參考,LocationManager調用接口之后,最終是調用到LocationManagerService,在requestLocationUpdates 方法或 getLastLocation 方法中都有checkPackageName方法的調用

private void checkPackageName(String packageName) {
    if (packageName == null) {
        throw new SecurityException("invalid package name: " + packageName);
    }
    int uid = Binder.getCallingUid();
    String[] packages = mPackageManager.getPackagesForUid(uid);
    if (packages == null) {
        throw new SecurityException("invalid UID " + uid);
    }
    for (String pkg : packages) {
        if (packageName.equals(pkg)) return;
    }
    throw new SecurityException("invalid package name: " + packageName);
}

如果調用requestLocationUpdates方法的應用沒有模擬定位的權限,就會報SecurityException異常,另外requestLocationUpdates需要在主線程中調用,如果在子線程中調用,還需要傳一個looper參數,不然在實例化ListenerTransport的時候會報錯。

看下ListenerTransport的構造函數,如果在子線程中添加監聽,又沒有傳Loop,初始化mListenerHandler的時候就會報異常。

ListenerTransport(LocationListener listener, Looper looper) {
    mListener = listener;
    if (looper == null) {
        mListenerHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                _handleMessage(msg);
            }
        };
    } else {
        mListenerHandler = new Handler(looper) {
            @Override
            public void handleMessage(Message msg) {
                _handleMessage(msg);
            }
        };
    }
}

接著添加對應的Provider,設置開關狀態為true,配置狀態為AVAILABLE。

LocationProvider provider = locationManager.getProvider(LocationManager.GPS_PROVIDER);
if (provider != null) {
    locationManager.addTestProvider(
        provider.getName()
        , provider.requiresNetwork()
        , provider.requiresSatellite()
        , provider.requiresCell()
        , provider.hasMonetaryCost()
        , provider.supportsAltitude()
        , provider.supportsSpeed()
        , provider.supportsBearing()
        , provider.getPowerRequirement()
        , provider.getAccuracy());
} else {
    locationManager.addTestProvider(LocationManager.GPS_PROVIDER, true, true, false, false, true, true,
        true, Criteria.POWER_LOW, Criteria.ACCURACY_FINE);
}
locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);
locationManager.setTestProviderStatus(LocationManager.GPS_PROVIDER, LocationProvider.AVAILABLE, null, System.currentTimeMillis());

2)setTestProviderLocation調用

調用代碼示例如下,經緯度、車速、定位準確性、方位、海拔參數根據實際需求設置。

Location loc = new Location(LocationManager.GPS_PROVIDER);
loc.setLongitude(24.522301);
loc.setLatitude(118.115756);
loc.setSpeed(60);
loc.setAccuracy(Criteria.ACCURACY_HIGH);
loc.setBearing(0);
loc.setAltitude(0);
loc.setTime(System.currentTimeMillis());
if (Build.VERSION.SDK_INT >= 17) {
    loc.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
}
locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER,loc);

來看下為什么需要設置elapsedRealtimeNanos和time參數。在SDK版本17以下,Location(Android 4.1.1)是沒有setElapsedRealtimeNanos這個方法的,在SDK版本17開始,Location(Android 4.2)加了這個方法,在調用setTestProviderLocation設置定位信息的時候,Android SDK版本17以上會做定位參數是否完整的校驗,17以下的版本自動做補足,17開始的版本直接拋異常。

public void setTestProviderLocation(String provider, Location loc) {
    if (!loc.isComplete()) {
        IllegalArgumentException e = new IllegalArgumentException(
            "Incomplete location object, missing timestamp or accuracy? " + loc);
        if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
            // just log on old platform (for backwards compatibility)
            Log.w(TAG, e);
            loc.makeComplete();
        } else {
            // really throw it!
            throw e;
        }
    }
    try {
        mService.setTestProviderLocation(provider, loc, mContext.getOpPackageName());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

接著看下Location類中makeComplete和isComplete做的邏輯處理,isComplete里面有provider,Accuracy,mTime和mElapsedRealtimeNanos的判斷。

@SystemApi
public void makeComplete() {
    if (mProvider == null) mProvider = "?";
    if (!hasAccuracy()) {
        mFieldsMask |= HAS_ACCURACY_MASK;
        mAccuracy = 100.0f;
    }
    if (mTime == 0) mTime = System.currentTimeMillis();
    if (mElapsedRealtimeNanos == 0) mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
}
@SystemApi
public boolean isComplete() {
    if (mProvider == null) return false;
    if (!hasAccuracy()) return false;
    if (mTime == 0) return false;
    if (mElapsedRealtimeNanos == 0) return false;
    return true;
}

以上是就是單個點的模擬定位實現,如果要實現路線回放模擬,只要在后臺請求到路線定位點數組數據之后,每隔1秒刷新調用setTestProviderLocation接口就可以了。

在模擬定位操作完畢之后,需要移除模擬定位對象,避免定位信息還是使用模擬定位接口的參數,如果沒有移除下一次使用的時候又調用添加同名的Provider也會拋異常。

locationManager.removeTestProvider(LocationManager.GPS_PROVIDER);

看完了這篇文章,相信你對“在Android中要怎么實現模擬定位”有了一定的了解,如果想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

霸州市| 彭阳县| 谷城县| 库尔勒市| 承德市| 佛教| 广汉市| 浦东新区| 桓仁| 青州市| 出国| 永安市| 云龙县| 商洛市| 常宁市| 湟源县| 鸡西市| 剑河县| 澄江县| 奎屯市| 赤城县| 论坛| 凤山市| 岱山县| 宁国市| 杭锦旗| 正镶白旗| 荃湾区| 韶关市| 十堰市| 五常市| 铜山县| 叶城县| 监利县| 响水县| 宝鸡市| 社旗县| 景洪市| 门头沟区| 罗定市| 房产|