Merge "Align GnssVisibilityControl location on/off with GnssLocationProvider" into qt-dev
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index ff0af13..ec0f8b8 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -935,6 +935,9 @@
             mGnssMeasurementsProvider.onGpsEnabledChanged();
             mGnssNavigationMessageProvider.onGpsEnabledChanged();
             mGnssBatchingProvider.enable();
+            if (mGnssVisibilityControl != null) {
+                mGnssVisibilityControl.onGpsEnabledChanged(mEnabled);
+            }
         } else {
             mEnabled = false;
             Log.w(TAG, "Failed to enable location provider");
@@ -951,6 +954,9 @@
         mAlarmManager.cancel(mWakeupIntent);
         mAlarmManager.cancel(mTimeoutIntent);
 
+        if (mGnssVisibilityControl != null) {
+            mGnssVisibilityControl.onGpsEnabledChanged(mEnabled);
+        }
         mGnssBatchingProvider.disable();
         // do this before releasing wakelock
         native_cleanup();
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index 24ee389..60be70e 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -23,13 +23,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.location.LocationManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -49,18 +46,14 @@
     private static final String TAG = "GnssVisibilityControl";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    // Constants related to non-framework (NFW) location access permission proxy apps.
-    private static final String NFW_PROXY_APP_PKG_ACTIVITY_NAME_SUFFIX =
-            ".NonFrameworkLocationAccessActivity";
-    private static final String NFW_INTENT_ACTION_NFW_LOCATION_ACCESS_SUFFIX =
-            ".intent.action.NON_FRAMEWORK_LOCATION_ACCESS";
-    private static final String NFW_INTENT_TYPE = "text/plain";
-
     private static final String LOCATION_PERMISSION_NAME =
             "android.permission.ACCESS_FINE_LOCATION";
 
     private static final String[] NO_LOCATION_ENABLED_PROXY_APPS = new String[0];
 
+    // Max wait time for synchronous method onGpsEnabledChanged() to run.
+    private static final long ON_GPS_ENABLED_CHANGED_TIMEOUT_MILLIS = 3 * 1000;
+
     // Wakelocks
     private static final String WAKELOCK_KEY = TAG;
     private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
@@ -72,7 +65,7 @@
     private final Handler mHandler;
     private final Context mContext;
 
-    private boolean mIsDeviceLocationSettingsEnabled;
+    private boolean mIsGpsEnabled;
 
     // Number of non-framework location access proxy apps is expected to be small (< 5).
     private static final int ARRAY_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
@@ -95,6 +88,30 @@
         runOnHandler(this::handleInitialize);
     }
 
+    void onGpsEnabledChanged(boolean isEnabled) {
+        // The GnssLocationProvider's methods: handleEnable() calls this method after native_init()
+        // and handleDisable() calls this method before native_cleanup(). This method must be
+        // executed synchronously so that the NFW location access permissions are disabled in
+        // the HAL before native_cleanup() method is called.
+        //
+        // NOTE: Since improper use of runWithScissors() method can result in deadlocks, the method
+        // doc recommends limiting its use to cases where some initialization steps need to be
+        // executed in sequence before continuing which fits this scenario.
+        if (mHandler.runWithScissors(() -> handleGpsEnabledChanged(isEnabled),
+                ON_GPS_ENABLED_CHANGED_TIMEOUT_MILLIS)) {
+            return;
+        }
+
+        // After timeout, the method remains posted in the queue and hence future enable/disable
+        // calls to this method will all get executed in the correct sequence. But this timeout
+        // situation should not even arise because runWithScissors() will run in the caller's
+        // thread without blocking as it is the same thread as mHandler's thread.
+        if (!isEnabled) {
+            Log.w(TAG, "Native call to disable non-framework location access in GNSS HAL may"
+                    + " get executed after native_cleanup().");
+        }
+    }
+
     void updateProxyApps(List<String> nfwLocationAccessProxyApps) {
         runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps));
     }
@@ -110,12 +127,6 @@
     private void handleInitialize() {
         disableNfwLocationAccess(); // Disable until config properties are loaded.
         listenForProxyAppsPackageUpdates();
-        listenForDeviceLocationSettingsUpdate();
-        mIsDeviceLocationSettingsEnabled = getDeviceLocationSettings();
-    }
-
-    private boolean getDeviceLocationSettings() {
-        return mContext.getSystemService(LocationManager.class).isLocationEnabled();
     }
 
     private void listenForProxyAppsPackageUpdates() {
@@ -145,18 +156,6 @@
         }, UserHandle.ALL, intentFilter, null, mHandler);
     }
 
-    private void listenForDeviceLocationSettingsUpdate() {
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
-                true,
-                new ContentObserver(mHandler) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        handleDeviceLocationSettingsUpdated();
-                    }
-                }, UserHandle.USER_ALL);
-    }
-
     private void handleProxyAppPackageUpdate(String pkgName, String action) {
         final Boolean locationPermission = mProxyAppToLocationPermissions.get(pkgName);
         if (locationPermission == null) {
@@ -213,22 +212,21 @@
         return false;
     }
 
-    private void handleDeviceLocationSettingsUpdated() {
-        final boolean enabled = getDeviceLocationSettings();
-        Log.i(TAG, "Device location settings enabled: " + enabled);
+    private void handleGpsEnabledChanged(boolean isEnabled) {
+        if (DEBUG) Log.d(TAG, "handleGpsEnabledChanged, isEnabled: " + isEnabled);
 
-        if (mIsDeviceLocationSettingsEnabled == enabled) {
+        if (mIsGpsEnabled == isEnabled) {
             return;
         }
 
-        mIsDeviceLocationSettingsEnabled = enabled;
-        if (!mIsDeviceLocationSettingsEnabled) {
+        mIsGpsEnabled = isEnabled;
+        if (!mIsGpsEnabled) {
             disableNfwLocationAccess();
             return;
         }
 
-        // When device location settings was disabled, we already set the proxy app list
-        // to empty in GNSS HAL. Update only if the proxy app list is not empty.
+        // When GNSS was disabled, we already set the proxy app list to empty in GNSS HAL.
+        // Update only if the proxy app list is not empty.
         String[] locationPermissionEnabledProxyApps = getLocationPermissionEnabledProxyApps();
         if (locationPermissionEnabledProxyApps.length != 0) {
             setNfwLocationAccessProxyAppsInGnssHal(locationPermissionEnabledProxyApps);
@@ -335,7 +333,7 @@
     }
 
     private void updateNfwLocationAccessProxyAppsInGnssHal() {
-        if (!mIsDeviceLocationSettingsEnabled) {
+        if (!mIsGpsEnabled) {
             return; // Keep non-framework location access disabled.
         }
         setNfwLocationAccessProxyAppsInGnssHal(getLocationPermissionEnabledProxyApps());