fix crashing apps

Bug: 7349330
Change-Id: Iea61bce23cb197c7a28d574098253823df73a99b
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index c5016e6..6948927 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -609,10 +609,9 @@
     }
 
     /**
-     * Throw SecurityException if caller has neither COARSE or FINE.
-     * Otherwise, return the best permission.
+     * Returns the best permission available to the caller.
      */
-    private String checkPermission() {
+    private String getBestCallingPermission() {
         if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
                 PackageManager.PERMISSION_GRANTED) {
             return ACCESS_FINE_LOCATION;
@@ -620,9 +619,20 @@
                 PackageManager.PERMISSION_GRANTED) {
             return ACCESS_COARSE_LOCATION;
         }
+        return null;
+    }
 
-        throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
-                " ACCESS_FINE_LOCATION permission");
+    /**
+     * Throw SecurityException if caller has neither COARSE or FINE.
+     * Otherwise, return the best permission.
+     */
+    private String checkPermission() {
+        String perm = getBestCallingPermission();
+        if (perm == null) {
+            throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
+                    " ACCESS_FINE_LOCATION permission");
+        }
+        return perm;
     }
 
     /**
@@ -635,19 +645,15 @@
         }
     }
 
-    private boolean isAllowedProviderSafe(String provider) {
+    private String getMinimumPermissionForProvider(String provider) {
         if (LocationManager.GPS_PROVIDER.equals(provider) ||
                 LocationManager.PASSIVE_PROVIDER.equals(provider)) {
             // gps and passive providers require FINE permission
-            return mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
-                    == PackageManager.PERMISSION_GRANTED;
+            return ACCESS_FINE_LOCATION;
         } else if (LocationManager.NETWORK_PROVIDER.equals(provider) ||
                 LocationManager.FUSED_PROVIDER.equals(provider)) {
             // network and fused providers are ok with COARSE or FINE
-            return (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
-                    == PackageManager.PERMISSION_GRANTED) ||
-                    (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
-                    == PackageManager.PERMISSION_GRANTED);
+            return ACCESS_COARSE_LOCATION;
         } else {
             // mock providers
             LocationProviderInterface lp = mMockProviders.get(provider);
@@ -656,20 +662,43 @@
                 if (properties != null) {
                     if (properties.mRequiresSatellite) {
                         // provider requiring satellites require FINE permission
-                        return mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
-                                == PackageManager.PERMISSION_GRANTED;
+                        return ACCESS_FINE_LOCATION;
                     } else if (properties.mRequiresNetwork || properties.mRequiresCell) {
                         // provider requiring network and or cell require COARSE or FINE
-                        return (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
-                                == PackageManager.PERMISSION_GRANTED) ||
-                                (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
-                                 == PackageManager.PERMISSION_GRANTED);
+                        return ACCESS_COARSE_LOCATION;
                     }
                 }
             }
         }
 
-        return false;
+        return null;
+    }
+
+    private boolean isPermissionSufficient(String perm, String minPerm) {
+        if (ACCESS_FINE_LOCATION.equals(minPerm)) {
+            return ACCESS_FINE_LOCATION.equals(perm);
+        } else if (ACCESS_COARSE_LOCATION.equals(minPerm)) {
+            return ACCESS_FINE_LOCATION.equals(perm) ||
+                    ACCESS_COARSE_LOCATION.equals(perm);
+        } else {
+            return false;
+        }
+    }
+
+    private void checkPermissionForProvider(String perm, String provider) {
+        String minPerm = getMinimumPermissionForProvider(provider);
+        if (!isPermissionSufficient(perm, minPerm)) {
+            if (ACCESS_FINE_LOCATION.equals(minPerm)) {
+                throw new SecurityException("Location provider \"" + provider +
+                        "\" requires ACCESS_FINE_LOCATION permission.");
+            } else if (ACCESS_COARSE_LOCATION.equals(minPerm)) {
+                throw new SecurityException("Location provider \"" + provider +
+                        "\" requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission.");                
+            } else {
+                throw new SecurityException("Insufficient permission for location provider \"" +
+                        provider + "\".");
+            }
+        }
     }
 
     /**
@@ -703,6 +732,7 @@
     @Override
     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
         ArrayList<String> out;
+        String perm = getBestCallingPermission();
         int callingUserId = UserHandle.getCallingUserId();
         long identity = Binder.clearCallingIdentity();
         try {
@@ -713,7 +743,7 @@
                     if (LocationManager.FUSED_PROVIDER.equals(name)) {
                         continue;
                     }
-                    if (isAllowedProviderSafe(name)) {
+                    if (isPermissionSufficient(perm, getMinimumPermissionForProvider(name))) {
                         if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) {
                             continue;
                         }
@@ -980,26 +1010,12 @@
         return receiver;
     }
 
-    private boolean isProviderAllowedByCoarsePermission(String provider) {
-        if (LocationManager.FUSED_PROVIDER.equals(provider)) {
-            return true;
-        }
-        if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
-            return true;
-        }
-        if (LocationManager.NETWORK_PROVIDER.equals(provider)) {
-            return true;
-        }
-        return false;
-    }
-
     private String checkPermissionAndRequest(LocationRequest request) {
-        String perm = checkPermission();
+        String perm = getBestCallingPermission();
+        String provider = request.getProvider();
+        checkPermissionForProvider(perm, provider);
 
         if (ACCESS_COARSE_LOCATION.equals(perm)) {
-            if (!isProviderAllowedByCoarsePermission(request.getProvider())) {
-                throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
-            }
             switch (request.getQuality()) {
                 case LocationRequest.ACCURACY_FINE:
                     request.setQuality(LocationRequest.ACCURACY_BLOCK);
@@ -1324,7 +1340,7 @@
      */
     @Override
     public ProviderProperties getProviderProperties(String provider) {
-        checkPermission();
+        checkPermissionForProvider(getBestCallingPermission(), provider);
 
         LocationProviderInterface p;
         synchronized (mLock) {
@@ -1337,13 +1353,8 @@
 
     @Override
     public boolean isProviderEnabled(String provider) {
-        String perms = checkPermission();
+        checkPermissionForProvider(getBestCallingPermission(), provider);
         if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-        if (ACCESS_COARSE_LOCATION.equals(perms) &&
-                !isProviderAllowedByCoarsePermission(provider)) {
-            throw new SecurityException("The \"" + provider +
-                    "\" provider requires ACCESS_FINE_LOCATION permission");
-        }
 
         long identity = Binder.clearCallingIdentity();
         try {