Changes to support updating location providers.

This reverts commit 20de160ca32a8f2936a80ffd70551a22e2371d25.

Bug: 7242814
Change-Id: I9ec49a14feb835b6683186fc6da4a74ae19fbae2
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 63eeeb3..baacac6 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -23,8 +23,11 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.location.Address;
@@ -246,6 +249,74 @@
         updateProvidersLocked();
     }
 
+    private void ensureFallbackFusedProviderPresentLocked(ArrayList<String> pkgs) {
+        PackageManager pm = mContext.getPackageManager();
+        String systemPackageName = mContext.getPackageName();
+        ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs);
+
+        List<ResolveInfo> rInfos = pm.queryIntentServicesAsUser(
+                new Intent(FUSED_LOCATION_SERVICE_ACTION),
+                PackageManager.GET_META_DATA, mCurrentUserId);
+        for (ResolveInfo rInfo : rInfos) {
+            String packageName = rInfo.serviceInfo.packageName;
+
+            // Check that the signature is in the list of supported sigs. If it's not in
+            // this list the standard provider binding logic won't bind to it.
+            try {
+                PackageInfo pInfo;
+                pInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+                if (!ServiceWatcher.isSignatureMatch(pInfo.signatures, sigSets)) {
+                    Log.w(TAG, packageName + " resolves service " + FUSED_LOCATION_SERVICE_ACTION +
+                            ", but has wrong signature, ignoring");
+                    continue;
+                }
+            } catch (NameNotFoundException e) {
+                Log.e(TAG, "missing package: " + packageName);
+                continue;
+            }
+
+            // Get the version info
+            if (rInfo.serviceInfo.metaData == null) {
+                Log.w(TAG, "Found fused provider without metadata: " + packageName);
+                continue;
+            }
+
+            int version = rInfo.serviceInfo.metaData.getInt(
+                    ServiceWatcher.EXTRA_SERVICE_VERSION, -1);
+            if (version == 0) {
+                // This should be the fallback fused location provider.
+
+                // Make sure it's in the system partition.
+                if ((rInfo.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+                    if (D) Log.d(TAG, "Fallback candidate not in /system: " + packageName);
+                    continue;
+                }
+
+                // Check that the fallback is signed the same as the OS
+                // as a proxy for coreApp="true"
+                if (pm.checkSignatures(systemPackageName, packageName)
+                        != PackageManager.SIGNATURE_MATCH) {
+                    if (D) Log.d(TAG, "Fallback candidate not signed the same as system: "
+                            + packageName);
+                    continue;
+                }
+
+                // Found a valid fallback.
+                if (D) Log.d(TAG, "Found fallback provider: " + packageName);
+                return;
+            } else {
+                if (D) Log.d(TAG, "Fallback candidate not version 0: " + packageName);
+            }
+        }
+
+        throw new IllegalStateException("Unable to find a fused location provider that is in the "
+                + "system partition with version 0 and signed with the platform certificate. "
+                + "Such a package is needed to provide a default fused location provider in the "
+                + "event that no other fused location provider has been installed or is currently "
+                + "available. For example, coreOnly boot mode when decrypting the data "
+                + "partition. The fallback must also be marked coreApp=\"true\" in the manifest");
+    }
+
     private void loadProvidersLocked() {
         // create a passive location provider, which is always enabled
         PassiveProvider passiveProvider = new PassiveProvider(this);
@@ -275,14 +346,13 @@
         */
         Resources resources = mContext.getResources();
         ArrayList<String> providerPackageNames = new ArrayList<String>();
-        String[] pkgs1 = resources.getStringArray(
+        String[] pkgs = resources.getStringArray(
                 com.android.internal.R.array.config_locationProviderPackageNames);
-        String[] pkgs2 = resources.getStringArray(
-                com.android.internal.R.array.config_overlay_locationProviderPackageNames);
-        if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs1));
-        if (D) Log.d(TAG, "overlay location providers: " + Arrays.toString(pkgs2));
-        if (pkgs1 != null) providerPackageNames.addAll(Arrays.asList(pkgs1));
-        if (pkgs2 != null) providerPackageNames.addAll(Arrays.asList(pkgs2));
+        if (D) Log.d(TAG, "certificates for location providers pulled from: " +
+                Arrays.toString(pkgs));
+        if (pkgs != null) providerPackageNames.addAll(Arrays.asList(pkgs));
+
+        ensureFallbackFusedProviderPresentLocked(providerPackageNames);
 
         // bind to network provider
         LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java
index 5598b0a..2e7c6d1 100644
--- a/services/java/com/android/server/ServiceWatcher.java
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -43,7 +43,7 @@
  */
 public class ServiceWatcher implements ServiceConnection {
     private static final boolean D = false;
-    private static final String EXTRA_VERSION = "version";
+    public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
 
     private final String mTag;
     private final Context mContext;
@@ -58,9 +58,27 @@
     // all fields below synchronized on mLock
     private IBinder mBinder;   // connected service
     private String mPackageName;  // current best package
-    private int mVersion;  // current best version
+    private int mVersion = Integer.MIN_VALUE;  // current best version
     private int mCurrentUserId;
 
+    public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
+            List<String> initialPackageNames) {
+        PackageManager pm = context.getPackageManager();
+        ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
+        for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
+            String pkg = initialPackageNames.get(i);
+            try {
+                HashSet<Signature> set = new HashSet<Signature>();
+                Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
+                set.addAll(Arrays.asList(sigs));
+                sigSets.add(set);
+            } catch (NameNotFoundException e) {
+                Log.w("ServiceWatcher", pkg + " not found");
+            }
+        }
+        return sigSets;
+    }
+
     public ServiceWatcher(Context context, String logTag, String action,
             List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) {
         mContext = context;
@@ -71,20 +89,7 @@
         mHandler = handler;
         mCurrentUserId = userId;
 
-        mSignatureSets = new ArrayList<HashSet<Signature>>();
-        for (int i=0; i < initialPackageNames.size(); i++) {
-            String pkg = initialPackageNames.get(i);
-            HashSet<Signature> set = new HashSet<Signature>();
-            try {
-                Signature[] sigs =
-                        mPm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
-                set.addAll(Arrays.asList(sigs));
-                mSignatureSets.add(set);
-            } catch (NameNotFoundException e) {
-                Log.w(logTag, pkg + " not found");
-            }
-        }
-
+        mSignatureSets = getSignatureSets(context, initialPackageNames);
     }
 
     public boolean start() {
@@ -132,15 +137,16 @@
             // check version
             int version = 0;
             if (rInfo.serviceInfo.metaData != null) {
-                version = rInfo.serviceInfo.metaData.getInt(EXTRA_VERSION, 0);
+                version = rInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION, 0);
             }
+
             if (version > mVersion) {
                 bestVersion = version;
                 bestPackage = packageName;
             }
         }
 
-        if (D) Log.d(mTag, String.format("bindBestPackage %s found %d, %s",
+        if (D) Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
                 (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "),
                 rInfos.size(),
                 (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
@@ -174,7 +180,8 @@
                 | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId);
     }
 
-    private boolean isSignatureMatch(Signature[] signatures) {
+    public static boolean isSignatureMatch(Signature[] signatures,
+            List<HashSet<Signature>> sigSets) {
         if (signatures == null) return false;
 
         // build hashset of input to test against
@@ -184,7 +191,7 @@
         }
 
         // test input against each of the signature sets
-        for (HashSet<Signature> referenceSet : mSignatureSets) {
+        for (HashSet<Signature> referenceSet : sigSets) {
             if (referenceSet.equals(inputSet)) {
                 return true;
             }
@@ -192,6 +199,10 @@
         return false;
     }
 
+    private boolean isSignatureMatch(Signature[] signatures) {
+        return isSignatureMatch(signatures, mSignatureSets);
+    }
+
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         /**
          * Called when package has been reinstalled