diff --git a/extern/MicroGUiTools b/extern/MicroGUiTools index f34d87b..f0e7d6b 160000 --- a/extern/MicroGUiTools +++ b/extern/MicroGUiTools @@ -1 +1 @@ -Subproject commit f34d87bcc2bb44de1a8efd29b40c311dbea67049 +Subproject commit f0e7d6b1c806effee3f5040e5e8595e128c575e4 diff --git a/extern/UnifiedNlpApi b/extern/UnifiedNlpApi index f7fcb8e..25d8b47 160000 --- a/extern/UnifiedNlpApi +++ b/extern/UnifiedNlpApi @@ -1 +1 @@ -Subproject commit f7fcb8e7cf966f58d784f450e061b88f96f73ecf +Subproject commit 25d8b478eb2497803ffb8c5c45d186adb6b64850 diff --git a/unifiednlp-base/src/main/java/org/microg/nlp/AbstractProviderService.java b/unifiednlp-base/src/main/java/org/microg/nlp/AbstractProviderService.java index 663bd14..a89441a 100644 --- a/unifiednlp-base/src/main/java/org/microg/nlp/AbstractProviderService.java +++ b/unifiednlp-base/src/main/java/org/microg/nlp/AbstractProviderService.java @@ -21,6 +21,8 @@ import android.os.IBinder; public abstract class AbstractProviderService extends IntentService { + protected String TAG; + /** * Creates an ProviderService. Invoked by your subclass's constructor. * @@ -28,6 +30,7 @@ public abstract class AbstractProviderService extends Intent */ public AbstractProviderService(String tag) { super(tag); + this.TAG = tag; } @Override diff --git a/unifiednlp-base/src/main/java/org/microg/nlp/location/AbstractLocationService.java b/unifiednlp-base/src/main/java/org/microg/nlp/location/AbstractLocationService.java index 639f2bc..7309638 100644 --- a/unifiednlp-base/src/main/java/org/microg/nlp/location/AbstractLocationService.java +++ b/unifiednlp-base/src/main/java/org/microg/nlp/location/AbstractLocationService.java @@ -16,34 +16,49 @@ package org.microg.nlp.location; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.location.Location; -import android.os.Build; import android.os.IBinder; +import android.util.Log; import org.microg.nlp.AbstractProviderService; -import org.microg.nlp.ui.SettingInjectorService; import java.lang.reflect.Method; +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; import static org.microg.nlp.api.Constants.ACTION_FORCE_LOCATION; import static org.microg.nlp.api.Constants.ACTION_RELOAD_SETTINGS; import static org.microg.nlp.api.Constants.INTENT_EXTRA_LOCATION; import static org.microg.nlp.api.Constants.PERMISSION_FORCE_LOCATION; public abstract class AbstractLocationService extends AbstractProviderService { - public static void reloadLocationService(Context context) { + public static ComponentName reloadLocationService(Context context) { Intent intent = new Intent(ACTION_RELOAD_SETTINGS); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + setIntentTarget(context, intent); + return context.startService(intent); + } + + public static ComponentName forceLocation(Context context, Location location) { + Intent intent = new Intent(ACTION_FORCE_LOCATION); + setIntentTarget(context, intent); + intent.putExtra(INTENT_EXTRA_LOCATION, location); + return context.startService(intent); + } + + private static void setIntentTarget(Context context, Intent intent) { + if (SDK_INT >= JELLY_BEAN_MR1) { intent.setClass(context, LocationServiceV2.class); } else { intent.setClass(context, LocationServiceV1.class); } - context.startService(intent); } + public static boolean WAS_BOUND = false; + /** * Creates an LocationService. Invoked by your subclass's constructor. * @@ -55,6 +70,7 @@ public AbstractLocationService(String tag) { @Override public IBinder onBind(Intent intent) { + WAS_BOUND = true; updateLauncherIcon(); return super.onBind(intent); } @@ -69,6 +85,8 @@ protected void onHandleIntent(Intent intent) { if (provider != null && intent.hasExtra(INTENT_EXTRA_LOCATION)) { provider.forceLocation( (Location) intent.getParcelableExtra(INTENT_EXTRA_LOCATION)); + } else { + Log.d(TAG, "Cannot force location, provider not ready"); } } } @@ -76,6 +94,8 @@ protected void onHandleIntent(Intent intent) { if (ACTION_RELOAD_SETTINGS.equals(intent.getAction())) { if (provider != null) { provider.reload(); + } else { + Log.d(TAG, "Cannot reload settings, provider not ready"); } } diff --git a/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProvider.java b/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProvider.java index 3e6f929..b457e77 100644 --- a/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProvider.java +++ b/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProvider.java @@ -21,6 +21,8 @@ import org.microg.nlp.Provider; interface LocationProvider extends Provider { + int FASTEST_REFRESH_INTERVAL = 2500; // in milliseconds + void onEnable(); void onDisable(); diff --git a/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProviderV1.java b/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProviderV1.java index 0b5138e..1eb41a7 100644 --- a/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProviderV1.java +++ b/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProviderV1.java @@ -137,7 +137,7 @@ public void onEnableLocationTracking(boolean enable) { @Override public void onSetMinTime(long minTime, WorkSource ws) { Log.v(TAG, "onSetMinTime: " + minTime + " by " + ws); - autoTime = minTime; + autoTime = Math.max(minTime, FASTEST_REFRESH_INTERVAL); helper.setTime(autoTime); if (autoUpdate) helper.enable(); } diff --git a/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProviderV2.java b/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProviderV2.java index 465b68a..f310520 100644 --- a/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProviderV2.java +++ b/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationProviderV2.java @@ -88,13 +88,9 @@ public long onGetStatusUpdateTime() { public void onSetRequest(ProviderRequestUnbundled requests, WorkSource source) { Log.v(TAG, "onSetRequest: " + requests + " by " + source); - long autoTime = requests.getInterval(); + long autoTime = Math.max(requests.getInterval(), FASTEST_REFRESH_INTERVAL); boolean autoUpdate = requests.getReportLocation(); - if (autoTime < 1500) { - // Limit to 1.5s - autoTime = 1500; - } Log.v(TAG, "using autoUpdate=" + autoUpdate + " autoTime=" + autoTime); if (autoUpdate) { diff --git a/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationServiceV1.java b/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationServiceV1.java index 430b966..e515705 100644 --- a/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationServiceV1.java +++ b/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationServiceV1.java @@ -17,11 +17,10 @@ package org.microg.nlp.location; public class LocationServiceV1 extends AbstractLocationService { - private static final String TAG = "NlpLocationService"; private static LocationProviderV1 THE_ONE; public LocationServiceV1() { - super(TAG); + super("NlpLocationServiceV1"); } @Override diff --git a/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationServiceV2.java b/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationServiceV2.java index 54aa1c1..af5e25b 100644 --- a/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationServiceV2.java +++ b/unifiednlp-base/src/main/java/org/microg/nlp/location/LocationServiceV2.java @@ -17,11 +17,10 @@ package org.microg.nlp.location; public class LocationServiceV2 extends AbstractLocationService { - private static final String TAG = "NlpLocationService"; private static LocationProviderV2 THE_ONE; public LocationServiceV2() { - super(TAG); + super("NlpLocationServiceV2"); } @Override diff --git a/unifiednlp-base/src/main/java/org/microg/tools/selfcheck/NlpOsCompatChecks.java b/unifiednlp-base/src/main/java/org/microg/tools/selfcheck/NlpOsCompatChecks.java new file mode 100644 index 0000000..b57d7bb --- /dev/null +++ b/unifiednlp-base/src/main/java/org/microg/tools/selfcheck/NlpOsCompatChecks.java @@ -0,0 +1,103 @@ +/* + * Copyright 2013-2016 microG Project Team + * + * 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 org.microg.tools.selfcheck; + +import android.content.Context; + +import java.util.Arrays; + +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.JELLY_BEAN; +import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; +import static android.os.Build.VERSION_CODES.KITKAT; +import static android.os.Build.VERSION_CODES.M; + +public class NlpOsCompatChecks implements SelfCheckGroup { + + @Override + public String getGroupName(Context context) { + return "Network location provider support"; + } + + @Override + public void doChecks(Context context, ResultCollector collector) { + checkSystemIsSupported(context, collector); + checkSystemIsConfigured(context, collector); + } + + private boolean checkSystemIsSupported(Context context, ResultCollector collector) { + boolean isSupported = (SDK_INT >= KITKAT && SDK_INT <= M); + collector.addResult("Android version supported:", isSupported ? Result.Positive : Result.Unknown, "Your Android version is not officially supported. This does not necessarily mean anything."); + return isSupported; + } + + private boolean checkSystemIsConfigured(Context context, ResultCollector collector) { + // 2.3+ com.android.internal.R.string.config_networkLocationProvider + // 4.1+ com.android.internal.R.string.config_networkLocationProviderPackageName + // 4.2+ com.android.internal.R.array.config_locationProviderPackageNames + // 4.3+ com.android.internal.R.array.config_locationProviderPackageNames / + // com.android.internal.R.string.config_networkLocationProviderPackageName / + // com.android.internal.R.bool.config_enableNetworkLocationOverlay + boolean systemMatchesPackage = false; + if (SDK_INT < JELLY_BEAN) { + systemMatchesPackage |= context.getPackageName().equals(getResourceString(context, "config_networkLocationProvider")); + } else { + boolean overlay = getResourceBool(context, "config_enableNetworkLocationOverlay"); + if (SDK_INT < JELLY_BEAN_MR1 || (SDK_INT > JELLY_BEAN_MR1 && !overlay)) { + systemMatchesPackage |= context.getPackageName().equals(getResourceString(context, "config_networkLocationProviderPackageName")); + } + if (SDK_INT == JELLY_BEAN_MR1 || (SDK_INT > JELLY_BEAN_MR1 && overlay)) { + systemMatchesPackage |= Arrays.asList(getResourceArray(context, "config_locationProviderPackageNames")).contains(context.getPackageName()); + } + } + collector.addResult("System supports location provider:", systemMatchesPackage ? Result.Positive : Result.Negative, "Your system does not support this UnifiedNlp package. Either install a matching package or a compatibility Xposed module."); + return systemMatchesPackage; + } + + private String[] getResourceArray(Context context, String identifier) { + try { + int resId = context.getResources().getIdentifier(identifier, "array", "android"); + if (resId == 0) + resId = context.getResources().getIdentifier(identifier, "array", "com.android.internal"); + return context.getResources().getStringArray(resId); + } catch (Exception e) { + return new String[0]; + } + } + + private boolean getResourceBool(Context context, String identifier) { + try { + int resId = context.getResources().getIdentifier(identifier, "bool", "android"); + if (resId == 0) + resId = context.getResources().getIdentifier(identifier, "bool", "com.android.internal"); + return context.getResources().getBoolean(resId); + } catch (Exception e) { + return false; + } + } + + private String getResourceString(Context context, String identifier) { + try { + int resId = context.getResources().getIdentifier(identifier, "string", "android"); + if (resId == 0) + resId = context.getResources().getIdentifier(identifier, "string", "com.android.internal"); + return context.getString(resId); + } catch (Exception e) { + return null; + } + } +} diff --git a/unifiednlp-base/src/main/java/org/microg/tools/selfcheck/NlpStatusChecks.java b/unifiednlp-base/src/main/java/org/microg/tools/selfcheck/NlpStatusChecks.java new file mode 100644 index 0000000..4f25efb --- /dev/null +++ b/unifiednlp-base/src/main/java/org/microg/tools/selfcheck/NlpStatusChecks.java @@ -0,0 +1,107 @@ +/* + * Copyright 2013-2016 microG Project Team + * + * 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 org.microg.tools.selfcheck; + +import android.content.Context; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Bundle; +import android.text.TextUtils; + +import org.microg.nlp.Preferences; +import org.microg.nlp.location.AbstractLocationService; + +import java.util.concurrent.atomic.AtomicBoolean; + +import static android.location.LocationManager.NETWORK_PROVIDER; +import static org.microg.nlp.api.Constants.LOCATION_EXTRA_BACKEND_PROVIDER; +import static org.microg.tools.selfcheck.SelfCheckGroup.Result.Negative; +import static org.microg.tools.selfcheck.SelfCheckGroup.Result.Positive; +import static org.microg.tools.selfcheck.SelfCheckGroup.Result.Unknown; + +public class NlpStatusChecks implements SelfCheckGroup { + @Override + public String getGroupName(Context context) { + return "UnifiedNlp status"; + } + + @Override + public void doChecks(Context context, ResultCollector collector) { + providerWasBound(context, collector); + isLocationProviderSetUp(context, collector); + isProvidingLastLocation(context, collector); + isProvidingLocation(context, collector); + } + + private boolean providerWasBound(Context context, ResultCollector collector) { + collector.addResult("UnifiedNlp is registered in system:", AbstractLocationService.WAS_BOUND ? Positive : Negative, "The system did not bind the UnifiedNlp service. If you just installed UnifiedNlp you should try to reboot this device."); + return AbstractLocationService.WAS_BOUND; + } + + private boolean isLocationProviderSetUp(Context context, ResultCollector collector) { + boolean setupLocationProvider = !TextUtils.isEmpty(new Preferences(context).getLocationBackends()); + collector.addResult("Location backend(s) set up:", setupLocationProvider ? Positive : Negative, "Install and configure a UnifiedNlp location backend to use network-based geolocation,"); + return setupLocationProvider; + } + + private boolean isProvidingLastLocation(Context context, ResultCollector collector) { + LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + Location location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); + boolean hasKnown = location != null && location.getExtras().containsKey(LOCATION_EXTRA_BACKEND_PROVIDER); + collector.addResult("UnifiedNlp has known location:", hasKnown ? Positive : Unknown, "UnifiedNlp has no last known location. This will cause some apps to fail."); + return hasKnown; + } + + private void isProvidingLocation(Context context, final ResultCollector collector) { + final AtomicBoolean result = new AtomicBoolean(false); + LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + new Thread(new Runnable() { + @Override + public void run() { + synchronized (result) { + try { + result.wait(10000); + } catch (InterruptedException e) { + } + collector.addResult("UnifiedNlp provides location updates:", result.get() ? Positive : Unknown, "No UnifiedNlp location was provided by the system within 10 seconds."); + } + } + }).start(); + locationManager.requestSingleUpdate(NETWORK_PROVIDER, new LocationListener() { + @Override + public void onLocationChanged(Location location) { + synchronized (result) { + result.set(location.getExtras().containsKey(LOCATION_EXTRA_BACKEND_PROVIDER)); + result.notifyAll(); + } + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + } + + @Override + public void onProviderEnabled(String provider) { + } + + @Override + public void onProviderDisabled(String provider) { + } + }, null); + } +}