location: Add support for location providers outside of the system process.

Also added new permissions android.permission.INSTALL_LOCATION_PROVIDER
and android.permission.INSTALL_LOCATION_COLLECTOR to the public API.

Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/api/current.xml b/api/current.xml
index 5080ca3..ceaaf79 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -529,6 +529,28 @@
  visibility="public"
 >
 </field>
+<field name="INSTALL_LOCATION_COLLECTOR"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.INSTALL_LOCATION_COLLECTOR&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INSTALL_LOCATION_PROVIDER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.INSTALL_LOCATION_PROVIDER&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="INSTALL_PACKAGES"
  type="java.lang.String"
  transient="false"
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index caa1318..aa8fd74 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -214,6 +214,18 @@
         android:label="@string/permlab_accessLocationExtraCommands"
         android:description="@string/permdesc_accessLocationExtraCommands" />
 
+    <!-- Allows an application to install a location provider into the Location Manager -->
+    <permission android:name="android.permission.INSTALL_LOCATION_PROVIDER"
+        android:protectionLevel="signatureOrSystem"
+        android:label="@string/permlab_installLocationProvider"
+        android:description="@string/permdesc_installLocationProvider" />
+
+    <!-- Allows an application to install a location collector into the Location Manager -->
+    <permission android:name="android.permission.INSTALL_LOCATION_COLLECTOR"
+        android:protectionLevel="signatureOrSystem"
+        android:label="@string/permlab_installLocationCollector"
+        android:description="@string/permdesc_installLocationCollector" />
+
     <!-- ======================================= -->
     <!-- Permissions for accessing networks -->
     <!-- ======================================= -->
@@ -921,7 +933,7 @@
     <permission android:name="android.permission.CONTROL_LOCATION_UPDATES"
         android:label="@string/permlab_locationUpdates"
         android:description="@string/permdesc_locationUpdates"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signatureOrSystem" />
 
     <!-- Allows read/write access to the "properties" table in the checkin
          database, to change values that get uploaded. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c35676c..6e2da4b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -753,6 +753,19 @@
         or other location sources.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_installLocationProvider">permission to install a location provider</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_installLocationProvider">Create mock location sources for testing.
+        Malicious applications can use this to override the location and/or status returned by real
+        location sources such as GPS or Network providers.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_installLocationCollector">permission to install a location collector</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_installLocationCollector">Create mock location sources for testing.
+        Malicious applications can use this to monitor and report your location to an external source.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessFineLocation">fine (GPS) location</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_accessFineLocation">Access fine location sources such as the
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 096622a..2c214c9 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -59,9 +59,9 @@
     boolean isProviderEnabled(String provider);
 
     Location getLastKnownLocation(String provider);
-    
+
     /* used by location providers to tell the location manager when it has a new location */
-    void setLocation(in Location location);
+    void reportLocation(in Location location);
 
     String getFromLocation(double latitude, double longitude, int maxResults,
         String language, String country, String variant, String appName, out List<Address> addrs);
@@ -82,7 +82,7 @@
     void clearTestProviderStatus(String provider);
 
     /* for installing external Location Providers */
-    void setNetworkLocationProvider(ILocationProvider provider);
-    void setLocationCollector(ILocationCollector collector);
-    void setGeocodeProvider(IGeocodeProvider provider);
+    void installLocationProvider(String name, ILocationProvider provider);
+    void installLocationCollector(ILocationCollector collector);
+    void installGeocodeProvider(IGeocodeProvider provider);
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index dacfeb9..aef8985 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1255,4 +1255,85 @@
             return false;
         }
     }
+
+    /**
+     * Installs a network location provider.
+     *
+     * @param name of the location provider
+     * @param provider Binder interface for the location provider
+     *
+     * @return true if the command succeeds.
+     *
+     * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
+     *
+     * {@hide}
+     */
+    public boolean installLocationProvider(String name, ILocationProvider provider) {
+        try {
+            mService.installLocationProvider(name, provider);
+            return true;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in installLocationProvider: ", e);
+            return false;
+        }
+    }
+
+    /**
+     * Installs a location collector.
+     *
+     * @param provider Binder interface for the location collector
+     *
+     * @return true if the command succeeds.
+     *
+     * Requires the android.permission.INSTALL_LOCATION_COLLECTOR permission.
+     *
+     * {@hide}
+     */
+    public boolean installLocationCollector(ILocationCollector collector) {
+        try {
+            mService.installLocationCollector(collector);
+            return true;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in setLocationCollector: ", e);
+            return false;
+        }
+    }
+
+    /**
+     * Installs a geocoder server.
+     *
+     * @param provider Binder interface for the geocoder provider
+     *
+     * @return true if the command succeeds.
+     *
+     * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
+     *
+     * {@hide}
+     */
+    public boolean installGeocodeProvider(IGeocodeProvider provider) {
+        try {
+            mService.installGeocodeProvider(provider);
+            return true;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in setGeocodeProvider: ", e);
+            return false;
+        }
+    }
+
+    /**
+     * Used by location providers to report new locations.
+     *
+     * @param location new Location to report
+     *
+     * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
+     *
+     * {@hide}
+     */
+    public void reportLocation(Location location) {
+        try {
+            mService.reportLocation(location);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in reportLocation: ", e);
+        }
+    }
 }
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 693848b..5877dd1 100644
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -708,7 +708,7 @@
             }
 
             try {
-                mLocationManager.setLocation(mLocation);
+                mLocationManager.reportLocation(mLocation);
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException calling reportLocation");
             }
diff --git a/location/java/com/android/internal/location/MockProvider.java b/location/java/com/android/internal/location/MockProvider.java
index 6fa2c29..d81d0ab 100644
--- a/location/java/com/android/internal/location/MockProvider.java
+++ b/location/java/com/android/internal/location/MockProvider.java
@@ -138,7 +138,7 @@
         mLocation.set(l);
         mHasLocation = true;
         try {
-            mLocationManager.setLocation(mLocation);
+            mLocationManager.reportLocation(mLocation);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException calling reportLocation");
         }
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index d44abaa..9af729e 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -106,6 +106,10 @@
         android.Manifest.permission.ACCESS_MOCK_LOCATION;
     private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
         android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
+    private static final String INSTALL_LOCATION_PROVIDER =
+        android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
+    private static final String INSTALL_LOCATION_COLLECTOR =
+        android.Manifest.permission.INSTALL_LOCATION_COLLECTOR;
 
     // Set of providers that are explicitly enabled
     private final Set<String> mEnabledProviders = new HashSet<String>();
@@ -626,36 +630,39 @@
         Looper.loop();
     }
 
-    public void setNetworkLocationProvider(ILocationProvider provider) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException(
-                "Installing location providers outside of the system is not supported");
+    public void installLocationProvider(String name, ILocationProvider provider) {
+        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
         }
 
         synchronized (mLock) {
-            mNetworkLocationProvider =
-                    new LocationProviderProxy(LocationManager.NETWORK_PROVIDER, provider);
-            addProvider(mNetworkLocationProvider);
-            updateProvidersLocked();
-            
-            // notify NetworkLocationProvider of any events it might have missed
-            mNetworkLocationProvider.updateNetworkState(mNetworkState);
+            // FIXME - only network location provider supported for now
+            if (LocationManager.NETWORK_PROVIDER.equals(name)) {
+                mNetworkLocationProvider = new LocationProviderProxy(name, provider);
+                addProvider(mNetworkLocationProvider);
+                updateProvidersLocked();
+
+                // notify NetworkLocationProvider of any events it might have missed
+                mNetworkLocationProvider.updateNetworkState(mNetworkState);
+            }
         }
     }
 
-    public void setLocationCollector(ILocationCollector collector) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException(
-                "Installing location collectors outside of the system is not supported");
+    public void installLocationCollector(ILocationCollector collector) {
+        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_COLLECTOR)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires INSTALL_LOCATION_COLLECTOR permission");
         }
 
+        // FIXME - only support one collector
         mCollector = collector;
     }
 
-    public void setGeocodeProvider(IGeocodeProvider provider) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException(
-                "Installing location providers outside of the system is not supported");
+    public void installGeocodeProvider(IGeocodeProvider provider) {
+        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
         }
 
         mGeocodeProvider = provider;
@@ -1472,7 +1479,12 @@
         }
     }
 
-    public void setLocation(Location location) {
+    public void reportLocation(Location location) {
+        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
+        }
+
         mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
         Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
         mLocationHandler.sendMessageAtFrontOfQueue(m);
diff --git a/test-runner/android/test/TestLocationProvider.java b/test-runner/android/test/TestLocationProvider.java
index 500786d..08603e3 100644
--- a/test-runner/android/test/TestLocationProvider.java
+++ b/test-runner/android/test/TestLocationProvider.java
@@ -189,7 +189,7 @@
         mLocation.setExtras(extras);
         mLocation.setTime(time);
         try {
-            mLocationManager.setLocation(mLocation);
+            mLocationManager.reportLocation(mLocation);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException calling updateLocation");
         }