Location overhaul, major commit.

Themes: Fused Location, Geofencing, LocationRequest.

API changes
o Fused location is always returned when asking for location by Criteria.
o Fused location is never returned as a LocationProvider object, nor returned
  as a provider String. This wouldn't make sense because the current API
  design assumes that LocationProvider's have fixed properties (accuracy, power
  etc).
o The fused location engine will tune itself based on the criteria passed
  by applications.
o Deprecate LocationProvider. Apps should use fused location (via Criteria
  class), instead of enumerating through LocationProvider objects. It is
  also over-engineered: designed for a world with a plethora of location
  providers that never materialized.
o The Criteria class is also over-engineered, with many methods that aren't
  currently used, but for now we won't deprecate them since they may have
  value in the future. It is now used to tune the fused location engine.
o Deprecate getBestProvider() and getProvider().
o Add getLastKnownLocation(Criteria), so we can return last known
  fused locations.
o Apps with only ACCESS_COARSE_LOCATION _can_ now use the GPS, but the location
  they receive will be fudged to a 1km radius. They can also use NETWORK
  and fused locatoins, which are fudged in the same way if necessary.
o Totally deprecate Criteria, in favor of LocationRequest.
  Criteria was designed to map QOS to a location provider. What we
  really need is to map QOS to _locations_.
  The death knell was the conflicting ACCURACY_ constants on
  Criteria, with values 1, 2, 3, 1, 2. Yes not a typo.
o Totally deprecate LocationProvider.
o Deprecate test/mock provider support. They require a named provider,
  which is a concept we are moving away from. We do not yet have a
  replacement, but I think its ok to deprecate since you also
  need to have 'allow mock locations' checked in developer settings.
  They will continue to work.
o Deprecate event codes associated with provider status. The fused
  provider is _always_ available.
o Introduce Geofence data object to provide an easier path fowards
  for polygons etc.

Implementation changes
o Fused implementation: incoming (GPS and NLP) location fixes are given
  a weight, that exponentially decays with respect to age and accuracy.
  The half-life of age is ~60 seconds, and the half-life of accuracy is
  ~20 meters. The fixes are weighted and combined to output a fused
  location.
o Move Fused Location impl into
  frameworks/base/packages/FusedLocation
o Refactor Fused Location behind the IProvider AIDL interface. This allow us
  to distribute newer versions of Fused Location in a new APK, at run-time.
o Introduce ServiceWatcher.java, to refactor code used for run-time upgrades of
  Fused Location, and the NLP.
o Fused Location is by default run in the system server (but can be moved to
  any process or pacakge, even at run-time).
o Plumb the Criteria requirements through to the Fused Location provider via
  ILocation.sendExtraCommand(). I re-used this interface to avoid modifying the
  ILocation interface, which would have broken run-time upgradability of the
  NLP.
o Switch the geofence manager to using fused location.
o Clean up 'adb shell dumpsys location' output.
o Introduce config_locationProviderPackageNames and
  config_overlay_locationProviderPackageNames to configure the default
  and overlay package names for Geocoder, NLP and FLP.
o Lots of misc cleanup.
o Improve location fudging. Apply random vector then quantize.
o Hide internal POJO's from clients of com.android.location.provider.jar
  (NLP and FLP). Introduce wrappers ProviderRequestUnbundled and
  ProviderPropertiesUnbundled.
o Introduce ProviderProperties to collapse all the provider accuracy/
  bearing/altitude/power plumbing (that is deprecated anyway).
o DELETE lots of code: DummyLocationProvider,
o Rename the (internal) LocationProvider to LocationProviderBase.
o Plumb pid, uid and packageName throughout
  LocationManagerService#Receiver to support future features.

TODO: The FLP and Geofencer have a lot of room to be more intelligent
TODO: Documentation
TODO: test test test

Change-Id: Iacefd2f176ed40ce1e23b090a164792aa8819c55
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index a227ab6..7faf72c 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -16,458 +16,272 @@
 
 package com.android.server.location;
 
-import android.content.ComponentName;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.location.Criteria;
-import android.location.ILocationProvider;
-import android.location.Location;
-import android.net.NetworkInfo;
+import android.location.LocationProvider;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.WorkSource;
 import android.util.Log;
 
-import com.android.internal.location.DummyLocationProvider;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ProviderRequest;
+import com.android.server.LocationManagerService;
+import com.android.server.ServiceWatcher;
 
 /**
- * A class for proxying location providers implemented as services.
- *
- * {@hide}
+ * Proxy for ILocationProvider implementations.
  */
 public class LocationProviderProxy implements LocationProviderInterface {
-
     private static final String TAG = "LocationProviderProxy";
-
-    public static final String SERVICE_ACTION =
-        "com.android.location.service.NetworkLocationProvider";
+    private static final boolean D = LocationManagerService.D;
 
     private final Context mContext;
     private final String mName;
-    private final Intent mIntent;
-    private final Handler mHandler;
-    private final Object mMutex = new Object();  // synchronizes access to non-final members
-    private Connection mServiceConnection;  // never null after ctor
+    private final ServiceWatcher mServiceWatcher;
 
-    // cached values set by the location manager
-    private boolean mLocationTracking = false;
+    private Object mLock = new Object();
+
+    // cached values set by the location manager, synchronized on mLock
+    private ProviderProperties mProperties;
     private boolean mEnabled = false;
-    private long mMinTime = -1;
-    private WorkSource mMinTimeSource = new WorkSource();
-    private int mNetworkState;
-    private NetworkInfo mNetworkInfo;
+    private ProviderRequest mRequest = null;
+    private WorkSource mWorksource = new WorkSource();
 
-    // constructor for proxying location providers implemented in a separate service
-    public LocationProviderProxy(Context context, String name, String packageName,
-            Handler handler) {
+    public static LocationProviderProxy createAndBind(Context context, String name, String action,
+            List<String> initialPackageNames, Handler handler) {
+        LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
+                initialPackageNames, handler);
+        if (proxy.bind()) {
+            return proxy;
+        } else {
+            return null;
+        }
+    }
+
+    private LocationProviderProxy(Context context, String name, String action,
+            List<String> initialPackageNames, Handler handler) {
         mContext = context;
         mName = name;
-        mIntent = new Intent(SERVICE_ACTION);
-        mHandler = handler;
-        reconnect(packageName);
+        mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames,
+                mNewServiceWork, handler);
     }
 
-    /** Bind to service. Will reconnect if already connected */
-    public void reconnect(String packageName) {
-        synchronized (mMutex) {
-            if (mServiceConnection != null) {
-                mContext.unbindService(mServiceConnection);
-            }
-            mServiceConnection = new Connection();
-            mIntent.setPackage(packageName);
-            mContext.bindService(mIntent, mServiceConnection,
-                    Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND |
-                    Context.BIND_ALLOW_OOM_MANAGEMENT);
-        }
+    private boolean bind () {
+        return mServiceWatcher.start();
     }
 
-    private class Connection implements ServiceConnection, Runnable {
+    private ILocationProvider getService() {
+        return ILocationProvider.Stub.asInterface(mServiceWatcher.getBinder());
+    }
 
-        private ILocationProvider mProvider;
+    public String getConnectedPackageName() {
+        return mServiceWatcher.getBestPackageName();
+    }
 
-        // for caching requiresNetwork, requiresSatellite, etc.
-        private DummyLocationProvider mCachedAttributes;  // synchronized by mMutex
-
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            synchronized (this) {
-                mProvider = ILocationProvider.Stub.asInterface(service);
-                if (mProvider != null) {
-                    mHandler.post(this);
-                }
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            synchronized (this) {
-                mProvider = null;
-            }
-        }
-
-        public synchronized ILocationProvider getProvider() {
-            return mProvider;
-        }
-
-        public synchronized DummyLocationProvider getCachedAttributes() {
-            return mCachedAttributes;
-        }
-
+    /**
+     * Work to apply current state to a newly connected provider.
+     * Remember we can switch the service that implements a providers
+     * at run-time, so need to apply current state.
+     */
+    private Runnable mNewServiceWork = new Runnable() {
+        @Override
         public void run() {
-            synchronized (mMutex) {
-                if (mServiceConnection != this) {
-                    // This ServiceConnection no longer the one we want to bind to.
-                    return;
-                }
-                ILocationProvider provider = getProvider();
-                if (provider == null) {
-                    return;
+            if (D) Log.d(TAG, "applying state to connected service");
+
+            boolean enabled;
+            ProviderProperties properties = null;
+            ProviderRequest request;
+            WorkSource source;
+            ILocationProvider service;
+            synchronized (mLock) {
+                enabled = mEnabled;
+                request = mRequest;
+                source = mWorksource;
+                service = getService();
+            }
+
+            if (service == null) return;
+
+            try {
+                // load properties from provider
+                properties = service.getProperties();
+                if (properties == null) {
+                    Log.e(TAG, mServiceWatcher.getBestPackageName() +
+                            " has invalid locatino provider properties");
                 }
 
-                // resend previous values from the location manager if the service has restarted
-                try {
-                    if (mEnabled) {
-                        provider.enable();
+                // apply current state to new service
+                if (enabled) {
+                    service.enable();
+                    if (request != null) {
+                        service.setRequest(request, source);
                     }
-                    if (mLocationTracking) {
-                        provider.enableLocationTracking(true);
-                    }
-                    if (mMinTime >= 0) {
-                        provider.setMinTime(mMinTime, mMinTimeSource);
-                    }
-                    if (mNetworkInfo != null) {
-                        provider.updateNetworkState(mNetworkState, mNetworkInfo);
-                    }
-                } catch (RemoteException e) {
                 }
+            } catch (RemoteException e) {
+                Log.w(TAG, e);
+            } catch (Exception e) {
+                // never let remote service crash system server
+                Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+            }
 
-                // init cache of parameters
-                if (mCachedAttributes == null) {
-                    try {
-                        mCachedAttributes = new DummyLocationProvider(mName, null);
-                        mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
-                        mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
-                        mCachedAttributes.setRequiresCell(provider.requiresCell());
-                        mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost());
-                        mCachedAttributes.setSupportsAltitude(provider.supportsAltitude());
-                        mCachedAttributes.setSupportsSpeed(provider.supportsSpeed());
-                        mCachedAttributes.setSupportsBearing(provider.supportsBearing());
-                        mCachedAttributes.setPowerRequirement(provider.getPowerRequirement());
-                        mCachedAttributes.setAccuracy(provider.getAccuracy());
-                    } catch (RemoteException e) {
-                        mCachedAttributes = null;
-                    }
-                }
+            synchronized (mLock) {
+                mProperties = properties;
             }
         }
     };
 
+    @Override
     public String getName() {
         return mName;
     }
 
-    private DummyLocationProvider getCachedAttributes() {
-        synchronized (mMutex) {
-            return mServiceConnection.getCachedAttributes();
+    @Override
+    public ProviderProperties getProperties() {
+        synchronized (mLock) {
+            return mProperties;
         }
     }
 
-    public boolean requiresNetwork() {
-        DummyLocationProvider cachedAttributes = getCachedAttributes();
-        if (cachedAttributes != null) {
-            return cachedAttributes.requiresNetwork();
-        } else {
-            return false;
-        }
-    }
-
-    public boolean requiresSatellite() {
-        DummyLocationProvider cachedAttributes = getCachedAttributes();
-        if (cachedAttributes != null) {
-            return cachedAttributes.requiresSatellite();
-        } else {
-            return false;
-        }
-    }
-
-    public boolean requiresCell() {
-        DummyLocationProvider cachedAttributes = getCachedAttributes();
-        if (cachedAttributes != null) {
-            return cachedAttributes.requiresCell();
-        } else {
-            return false;
-        }
-    }
-
-    public boolean hasMonetaryCost() {
-        DummyLocationProvider cachedAttributes = getCachedAttributes();
-        if (cachedAttributes != null) {
-            return cachedAttributes.hasMonetaryCost();
-        } else {
-            return false;
-        }
-    }
-
-    public boolean supportsAltitude() {
-        DummyLocationProvider cachedAttributes = getCachedAttributes();
-        if (cachedAttributes != null) {
-            return cachedAttributes.supportsAltitude();
-        } else {
-            return false;
-        }
-    }
-
-    public boolean supportsSpeed() {
-        DummyLocationProvider cachedAttributes = getCachedAttributes();
-        if (cachedAttributes != null) {
-            return cachedAttributes.supportsSpeed();
-        } else {
-            return false;
-        }
-    }
-
-     public boolean supportsBearing() {
-        DummyLocationProvider cachedAttributes = getCachedAttributes();
-        if (cachedAttributes != null) {
-            return cachedAttributes.supportsBearing();
-        } else {
-            return false;
-        }
-    }
-
-    public int getPowerRequirement() {
-        DummyLocationProvider cachedAttributes = getCachedAttributes();
-        if (cachedAttributes != null) {
-            return cachedAttributes.getPowerRequirement();
-        } else {
-            return -1;
-        }
-    }
-
-    public int getAccuracy() {
-        DummyLocationProvider cachedAttributes = getCachedAttributes();
-        if (cachedAttributes != null) {
-            return cachedAttributes.getAccuracy();
-        } else {
-            return -1;
-        }
-    }
-
-    public boolean meetsCriteria(Criteria criteria) {
-        synchronized (mMutex) {
-            ILocationProvider provider = mServiceConnection.getProvider();
-            if (provider != null) {
-                try {
-                    return provider.meetsCriteria(criteria);
-                } catch (RemoteException e) {
-                }
-            }
-        }
-        // default implementation if we lost connection to the provider
-        if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
-            (criteria.getAccuracy() < getAccuracy())) {
-            return false;
-        }
-        int criteriaPower = criteria.getPowerRequirement();
-        if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
-            (criteriaPower < getPowerRequirement())) {
-            return false;
-        }
-        if (criteria.isAltitudeRequired() && !supportsAltitude()) {
-            return false;
-        }
-        if (criteria.isSpeedRequired() && !supportsSpeed()) {
-            return false;
-        }
-        if (criteria.isBearingRequired() && !supportsBearing()) {
-            return false;
-        }
-        return true;
-    }
-
+    @Override
     public void enable() {
-        synchronized (mMutex) {
+        synchronized (mLock) {
             mEnabled = true;
-            ILocationProvider provider = mServiceConnection.getProvider();
-            if (provider != null) {
-                try {
-                    provider.enable();
-                } catch (RemoteException e) {
-                }
-            }
+        }
+        ILocationProvider service = getService();
+        if (service == null) return;
+
+        try {
+            service.enable();
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+        } catch (Exception e) {
+            // never let remote service crash system server
+            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
         }
     }
 
+    @Override
     public void disable() {
-        synchronized (mMutex) {
+        synchronized (mLock) {
             mEnabled = false;
-            ILocationProvider provider = mServiceConnection.getProvider();
-            if (provider != null) {
-                try {
-                    provider.disable();
-                } catch (RemoteException e) {
-                }
-            }
+        }
+        ILocationProvider service = getService();
+        if (service == null) return;
+
+        try {
+            service.disable();
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+        } catch (Exception e) {
+            // never let remote service crash system server
+            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
         }
     }
 
+    @Override
     public boolean isEnabled() {
-        synchronized (mMutex) {
+        synchronized (mLock) {
             return mEnabled;
         }
     }
 
+    @Override
+    public void setRequest(ProviderRequest request, WorkSource source) {
+        synchronized (mLock) {
+            mRequest = request;
+            mWorksource = source;
+        }
+        ILocationProvider service = getService();
+        if (service == null) return;
+
+        try {
+            service.setRequest(request, source);
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+        } catch (Exception e) {
+            // never let remote service crash system server
+            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+        }
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.append("REMOTE SERVICE");
+        pw.append(" name=").append(mName);
+        pw.append(" pkg=").append(mServiceWatcher.getBestPackageName());
+        pw.append(" version=").append("" + mServiceWatcher.getBestVersion());
+        pw.append('\n');
+
+        ILocationProvider service = getService();
+        if (service == null) {
+            pw.println("service down (null)");
+            return;
+        }
+        pw.flush();
+
+        try {
+            service.asBinder().dump(fd, args);
+        } catch (RemoteException e) {
+            pw.println("service down (RemoteException)");
+            Log.w(TAG, e);
+        } catch (Exception e) {
+            pw.println("service down (Exception)");
+            // never let remote service crash system server
+            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+        }
+    }
+
+    @Override
     public int getStatus(Bundle extras) {
-        ILocationProvider provider;
-        synchronized (mMutex) {
-            provider = mServiceConnection.getProvider();
+        ILocationProvider service = getService();
+        if (service == null) return LocationProvider.TEMPORARILY_UNAVAILABLE;
+
+        try {
+            return service.getStatus(extras);
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+        } catch (Exception e) {
+            // never let remote service crash system server
+            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
         }
-        if (provider != null) {
-            try {
-                return provider.getStatus(extras);
-            } catch (RemoteException e) {
-            }
-        }
-        return 0;
+        return LocationProvider.TEMPORARILY_UNAVAILABLE;
     }
 
+    @Override
     public long getStatusUpdateTime() {
-        ILocationProvider provider;
-        synchronized (mMutex) {
-            provider = mServiceConnection.getProvider();
-        }
-        if (provider != null) {
-            try {
-                return provider.getStatusUpdateTime();
-            } catch (RemoteException e) {
-            }
+        ILocationProvider service = getService();
+        if (service == null) return 0;
+
+        try {
+            return service.getStatusUpdateTime();
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+        } catch (Exception e) {
+            // never let remote service crash system server
+            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
         }
         return 0;
-     }
-
-    public String getInternalState() {
-        ILocationProvider provider;
-        synchronized (mMutex) {
-            provider = mServiceConnection.getProvider();
-        }
-        if (provider != null) {
-            try {
-                return provider.getInternalState();
-            } catch (RemoteException e) {
-                Log.e(TAG, "getInternalState failed", e);
-            }
-        }
-        return null;
     }
 
-    public boolean isLocationTracking() {
-        synchronized (mMutex) {
-            return mLocationTracking;
-        }
-    }
-
-    public void enableLocationTracking(boolean enable) {
-        synchronized (mMutex) {
-            mLocationTracking = enable;
-            if (!enable) {
-                mMinTime = -1;
-                mMinTimeSource.clear();
-            }
-            ILocationProvider provider = mServiceConnection.getProvider();
-            if (provider != null) {
-                try {
-                    provider.enableLocationTracking(enable);
-                } catch (RemoteException e) {
-                }
-            }
-        }
-    }
-
-    public boolean requestSingleShotFix() {
-        return false;
-    }
-
-    public long getMinTime() {
-        synchronized (mMutex) {
-            return mMinTime;
-        }
-    }
-
-    public void setMinTime(long minTime, WorkSource ws) {
-        synchronized (mMutex) {
-            mMinTime = minTime;
-            mMinTimeSource.set(ws);
-            ILocationProvider provider = mServiceConnection.getProvider();
-            if (provider != null) {
-                try {
-                    provider.setMinTime(minTime, ws);
-                } catch (RemoteException e) {
-                }
-            }
-        }
-    }
-
-    public void updateNetworkState(int state, NetworkInfo info) {
-        synchronized (mMutex) {
-            mNetworkState = state;
-            mNetworkInfo = info;
-            ILocationProvider provider = mServiceConnection.getProvider();
-            if (provider != null) {
-                try {
-                    provider.updateNetworkState(state, info);
-                } catch (RemoteException e) {
-                }
-            }
-        }
-    }
-
-    public void updateLocation(Location location) {
-        synchronized (mMutex) {
-            ILocationProvider provider = mServiceConnection.getProvider();
-            if (provider != null) {
-                try {
-                    provider.updateLocation(location);
-                } catch (RemoteException e) {
-                }
-            }
-        }
-    }
-
+    @Override
     public boolean sendExtraCommand(String command, Bundle extras) {
-        synchronized (mMutex) {
-            ILocationProvider provider = mServiceConnection.getProvider();
-            if (provider != null) {
-                try {
-                    return provider.sendExtraCommand(command, extras);
-                } catch (RemoteException e) {
-                }
-            }
+        ILocationProvider service = getService();
+        if (service == null) return false;
+
+        try {
+            return service.sendExtraCommand(command, extras);
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+        } catch (Exception e) {
+            // never let remote service crash system server
+            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
         }
         return false;
     }
-
-    public void addListener(int uid) {
-        synchronized (mMutex) {
-            ILocationProvider provider = mServiceConnection.getProvider();
-            if (provider != null) {
-                try {
-                    provider.addListener(uid);
-                } catch (RemoteException e) {
-                }
-            }
-        }
-    }
-
-    public void removeListener(int uid) {
-        synchronized (mMutex) {
-            ILocationProvider provider = mServiceConnection.getProvider();
-            if (provider != null) {
-                try {
-                    provider.removeListener(uid);
-                } catch (RemoteException e) {
-                }
-            }
-        }
-    }
-}
+ }