In navigation test scenarios often need location simulation and route playback, recorded through the LocationManager.setTestProviderLocation() method to achieve simulated status, if the application to be tested does not support TestProviderLocation simulation location input, you can consider starting with the HAL layer, the hook the system’s default GPS implementation.

## 1, Android simulation permission open configuration

In the version of Android 6.0 or below, you need to check the switch of simulated positioning in the settings, in 6.0 or above it is changed to select the application of simulated positioning, the corresponding opening configuration is different, the same is in AndroidManifest.xml both need to configure the following two permissions.

 1 2   

### 1）Android 6.0 or below turn on the analog positioning switch

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

To enable mock positioning in this way, you need to configure the following system permissions in AndroidManifest.xml, and the application also needs to be signed by the system, which can’t be achieved by this way for non-system applications.

 1  ndroid:sharedUserId="android.uid.system" 
 1   

This operation can be configured to bypass system privilege configuration by means of the adb shell command:

 1  adb shell settings put secure mock_location 1 

### 2) Android 6.0 or above code configuration to choose the application of analog positioning

In Android version above 6.0, you need to set the mock_location permission of the specified package name to allow.

  1 2 3 4 5 6 7 8 9 10 11  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 */ } 

Also in AndroidManifest.xml you need to configure the following permissions, unfortunately this way also needs to be compiled with the system signature and source code, only the system level application can be used.

 1   

In order to display the simulated positioning application in the simulated positioning option of the settings screen, you need to configure the ACCESS_MOCK_LOCATION permission.

 1   

The above configuration can also be implemented via the adb shell command, with the <package> parameter replaced by the package name of your own application.

 1  adb shell appops set android:mock_location allow 

Turn off simulated positioning privileges with the following command.

 1  adb shell appops set android:mock_location deny 

To query which applications have simulated location permissions turned on, use the following command.

 1  adb shell appops query-op android:mock_location allow 

The package name list parameter of the application will be output after execution.

## 2, Android simulation positioning implementation

### 1) Analog positioning switch check

The first is the code first determine whether the simulation positioning permission is on, 6.0 or above can only be determined by adding a positioning listener if there is an exception.

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29  boolean mockPermission = false; if (Build.VERSION.SDK_INT <= 22) {//below 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; } } 

You can see the source code implementation of adding location listener, and take the source code implementation of Android 7.0 as reference.

After LocationManager calls the interface, the final call is to LocationManagerService

In the requestLocationUpdates method or getLastLocation method, the checkPackageName method is called

  1 2 3 4 5 6 7 8 9 10 11 12 13 14  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); } 

If the application calling the requestLocationUpdates method does not have the permission to simulate location, it will throw a SecurityException exception. In addition, requestLocationUpdates needs to be called in the main thread, if it is called in a sub-thread, you also need to pass a looper parameter, otherwise it will report an error when instantiating ListenerTransport.

Look at the constructor of ListenerTransport, if you add a listener in a sub-thread and don’t pass a Loop, an exception is thrown when initializing mListenerHandler.

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  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); } }; } } 

Then add the corresponding Provider, set the switch state to true, and configure the state to AVAILABLE.

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19  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 call

The call code example is as follows. The parameters of latitude, longitude, vehicle speed, positioning accuracy, bearing and altitude are set according to the actual requirements.

  1 2 3 4 5 6 7 8 9 10 11 12  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); 

Let’s see why we need to set elapsedRealtimeNanos and time parameters. Below SDK version 17, Location (Android 4.1.1) did not have setElapsedRealtimeNanos method, starting from SDK version 17, Location (Android 4.2) added this method, when calling setTestProviderLocation to set the location information, the Android SDK version 17 or above will do the verification of the location parameter completeness, the version below 17 will do the complement automatically, and the version from 17 will throw the exception directly.

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19  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(); } } 

Then look at the makeComplete and isComplete logic processing in the Location class. isComplete has provider, Accuracy, mTime and mElapsedRealtimeNanos judgments inside.

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  @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; } 

The above is a single point of the simulation of location implementation, if you want to achieve route playback simulation, just request the route location point array data in the background, after every 1 second refresh call setTestProviderLocation interface can be.

After the simulated location operation, you need to remove the simulated location object to avoid the location information or use the parameters of the simulated location interface, if not removed the next time you use it and call the Provider with the same name will also throw an exception.

 1  locationManager.removeTestProvider(LocationManager.GPS_PROVIDER); 

Reference https://www.chenwenguan.com/android-mock-location/