OSDN Git Service

Make location providers upgradeable.
authorNick Pelly <npelly@google.com>
Sun, 27 May 2012 23:12:45 +0000 (16:12 -0700)
committerNick Pelly <npelly@google.com>
Tue, 29 May 2012 11:36:46 +0000 (13:36 +0200)
Use config_netowrkLocationProviderPackageName and
config_geocodeProviderPackageName as intial packages. If another
package exists (or is later installed) that also implements a
provider, and has the same signatures as the original providers,
and has a hgiher version number, then use that instead.

The old code used a funky fix of package name substring checks
and service checks that was broken and not upgradeable.

Bug: 6499445
Change-Id: Ic58f09cf85d31d9abf47707093e22f31dda25cf0

core/res/res/values/config.xml
core/res/res/values/public.xml
services/java/com/android/server/LocationManagerService.java
services/java/com/android/server/location/GeocoderProxy.java
services/java/com/android/server/location/LocationProviderProxy.java

index 98e7769..09e3fbb 100755 (executable)
     <!-- True if WallpaperService is enabled -->
     <bool name="config_enableWallpaperService">true</bool>
 
-    <!-- Component name of the service providing network location support. -->
-    <string name="config_networkLocationProvider" translatable="false">@null</string>
+    <!-- Package name providing network location support. -->
+    <string name="config_networkLocationProviderPackageName" translatable="false">@null</string>
 
-    <!-- Component name of the service providing geocoder API support. -->
-    <string name="config_geocodeProvider" translatable="false">@null</string>
+    <!-- Package name providing geocoder API support. -->
+    <string name="config_geocodeProviderPackageName" translatable="false">@null</string>
 
     <!-- Boolean indicating if current platform supports bluetooth SCO for off call
     use cases -->
index a143feb..a0980ff 100644 (file)
   <java-symbol type="string" name="car_mode_disable_notification_title" />
   <java-symbol type="string" name="chooser_wallpaper" />
   <java-symbol type="string" name="config_datause_iface" />
-  <java-symbol type="string" name="config_geocodeProvider" />
-  <java-symbol type="string" name="config_networkLocationProvider" />
+  <java-symbol type="string" name="config_geocodeProviderPackageName" />
+  <java-symbol type="string" name="config_networkLocationProviderPackageName" />
   <java-symbol type="string" name="config_wimaxManagerClassname" />
   <java-symbol type="string" name="config_wimaxNativeLibLocation" />
   <java-symbol type="string" name="config_wimaxServiceClassname" />
index 1e707b2..985249d 100644 (file)
@@ -26,7 +26,11 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.location.Address;
@@ -123,8 +127,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
     private static boolean sProvidersLoaded = false;
 
     private final Context mContext;
-    private final String mNetworkLocationProviderPackageName;
-    private final String mGeocodeProviderPackageName;
+    private PackageManager mPackageManager;  // final after initialize()
+    private String mNetworkLocationProviderPackageName;  // only used on handler thread
+    private String mGeocodeProviderPackageName;  // only used on handler thread
     private GeocoderProxy mGeocodeProvider;
     private IGpsStatusProvider mGpsStatusProvider;
     private INetInitiatedListener mNetInitiatedListener;
@@ -490,36 +495,91 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
         addProvider(passiveProvider);
         mEnabledProviders.add(passiveProvider.getName());
 
-        // initialize external network location and geocoder services
-        PackageManager pm = mContext.getPackageManager();
-        if (mNetworkLocationProviderPackageName != null &&
-                pm.resolveService(new Intent(mNetworkLocationProviderPackageName), 0) != null) {
-            mNetworkLocationProvider =
-                new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER,
-                        mNetworkLocationProviderPackageName, mLocationHandler);
-
-            addProvider(mNetworkLocationProvider);
+        // initialize external network location and geocoder services.
+        // The initial value of mNetworkLocationProviderPackageName and
+        // mGeocodeProviderPackageName is just used to determine what
+        // signatures future mNetworkLocationProviderPackageName and
+        // mGeocodeProviderPackageName packages must have. So alternate
+        // providers can be installed under a different package name
+        // so long as they have the same signature as the original
+        // provider packages.
+        if (mNetworkLocationProviderPackageName != null) {
+            String packageName = findBestPackage(LocationProviderProxy.SERVICE_ACTION,
+                    mNetworkLocationProviderPackageName);
+            if (packageName != null) {
+                mNetworkLocationProvider = new LocationProviderProxy(mContext,
+                        LocationManager.NETWORK_PROVIDER,
+                        packageName, mLocationHandler);
+                mNetworkLocationProviderPackageName = packageName;
+                addProvider(mNetworkLocationProvider);
+            }
         }
-
-        if (mGeocodeProviderPackageName != null &&
-                pm.resolveService(new Intent(mGeocodeProviderPackageName), 0) != null) {
-            mGeocodeProvider = new GeocoderProxy(mContext, mGeocodeProviderPackageName);
+        if (mGeocodeProviderPackageName != null) {
+            String packageName = findBestPackage(GeocoderProxy.SERVICE_ACTION,
+                    mGeocodeProviderPackageName);
+            if (packageName != null) {
+                mGeocodeProvider = new GeocoderProxy(mContext, packageName);
+                mGeocodeProviderPackageName = packageName;
+            }
         }
 
         updateProvidersLocked();
     }
 
     /**
+     * Pick the best (network location provider or geocode provider) package.
+     * The best package:
+     * - implements serviceIntentName
+     * - has signatures that match that of sigPackageName
+     * - has the highest version value in a meta-data field in the service component
+     */
+    String findBestPackage(String serviceIntentName, String sigPackageName) {
+        Intent intent = new Intent(serviceIntentName);
+        List<ResolveInfo> infos = mPackageManager.queryIntentServices(intent,
+                PackageManager.GET_META_DATA);
+        if (infos == null) return null;
+
+        int bestVersion = Integer.MIN_VALUE;
+        String bestPackage = null;
+        for (ResolveInfo info : infos) {
+            String packageName = info.serviceInfo.packageName;
+            // check signature
+            if (mPackageManager.checkSignatures(packageName, sigPackageName) !=
+                    PackageManager.SIGNATURE_MATCH) {
+                Slog.w(TAG, packageName + " implements " + serviceIntentName +
+                       " but its signatures don't match those in " + sigPackageName +
+                       ", ignoring");
+                continue;
+            }
+            // read version
+            int version = 0;
+            if (info.serviceInfo.metaData != null) {
+                version = info.serviceInfo.metaData.getInt("version", 0);
+            }
+            if (LOCAL_LOGV) Slog.v(TAG, packageName + " implements " + serviceIntentName +
+                    " with version " + version);
+            if (version > bestVersion) {
+                bestVersion = version;
+                bestPackage = packageName;
+            }
+        }
+
+        return bestPackage;
+    }
+
+    /**
      * @param context the context that the LocationManagerService runs in
      */
     public LocationManagerService(Context context) {
         super();
         mContext = context;
         Resources resources = context.getResources();
+
         mNetworkLocationProviderPackageName = resources.getString(
-                com.android.internal.R.string.config_networkLocationProvider);
+                com.android.internal.R.string.config_networkLocationProviderPackageName);
         mGeocodeProviderPackageName = resources.getString(
-                com.android.internal.R.string.config_geocodeProvider);
+                com.android.internal.R.string.config_geocodeProviderPackageName);
+
         mPackageMonitor.register(context, null, true);
 
         if (LOCAL_LOGV) {
@@ -537,6 +597,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
         // Create a wake lock, needs to be done before calling loadProviders() below
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
+        mPackageManager = mContext.getPackageManager();
 
         // Load providers
         loadProviders();
@@ -1886,16 +1947,33 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
                     }
                 } else if (msg.what == MESSAGE_PACKAGE_UPDATED) {
                     String packageName = (String) msg.obj;
-                    String packageDot = packageName + ".";
 
-                    // reconnect to external providers after their packages have been updated
-                    if (mNetworkLocationProvider != null &&
-                        mNetworkLocationProviderPackageName.startsWith(packageDot)) {
-                        mNetworkLocationProvider.reconnect();
+                    // reconnect to external providers if there is a better package
+                    if (mNetworkLocationProviderPackageName != null &&
+                            mPackageManager.resolveService(
+                            new Intent(LocationProviderProxy.SERVICE_ACTION)
+                            .setPackage(packageName), 0) != null) {
+                        // package implements service, perform full check
+                        String bestPackage = findBestPackage(
+                                LocationProviderProxy.SERVICE_ACTION,
+                                mNetworkLocationProviderPackageName);
+                        if (packageName.equals(bestPackage)) {
+                            mNetworkLocationProvider.reconnect(bestPackage);
+                            mNetworkLocationProviderPackageName = packageName;
+                        }
                     }
-                    if (mGeocodeProvider != null &&
-                        mGeocodeProviderPackageName.startsWith(packageDot)) {
-                        mGeocodeProvider.reconnect();
+                    if (mGeocodeProviderPackageName != null &&
+                            mPackageManager.resolveService(
+                            new Intent(GeocoderProxy.SERVICE_ACTION)
+                            .setPackage(packageName), 0) != null) {
+                        // package implements service, perform full check
+                        String bestPackage = findBestPackage(
+                                GeocoderProxy.SERVICE_ACTION,
+                                mGeocodeProviderPackageName);
+                        if (packageName.equals(bestPackage)) {
+                            mGeocodeProvider.reconnect(bestPackage);
+                            mGeocodeProviderPackageName = packageName;
+                        }
                     }
                 }
             } catch (Exception e) {
@@ -2004,6 +2082,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
             // Called by main thread; divert work to LocationWorker.
             Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
         }
+        @Override
+        public void onPackageAdded(String packageName, int uid) {
+            // Called by main thread; divert work to LocationWorker.
+            Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
+        }
     };
 
     // Wake locks
index b38ea13..07f3125 100644 (file)
@@ -39,27 +39,28 @@ public class GeocoderProxy {
 
     private static final String TAG = "GeocoderProxy";
 
+    public static final String SERVICE_ACTION =
+        "com.android.location.service.GeocodeProvider";
+
     private final Context mContext;
     private final Intent mIntent;
     private final Object mMutex = new Object();  // synchronizes access to mServiceConnection
-    private Connection mServiceConnection = new Connection();  // never null
+    private Connection mServiceConnection;  // never null after ctor
 
-    public GeocoderProxy(Context context, String serviceName) {
+    public GeocoderProxy(Context context, String packageName) {
         mContext = context;
-        mIntent = new Intent(serviceName);
-        mContext.bindService(mIntent, mServiceConnection,
-                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
-                | Context.BIND_ALLOW_OOM_MANAGEMENT);
+        mIntent = new Intent(SERVICE_ACTION);
+        reconnect(packageName);
     }
 
-    /**
-     * When unbundled NetworkLocationService package is updated, we
-     * need to unbind from the old version and re-bind to the new one.
-     */
-    public void reconnect() {
+    /** Bind to service. Will reconnect if already connected */
+    public void reconnect(String packageName) {
         synchronized (mMutex) {
-            mContext.unbindService(mServiceConnection);
+            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);
index 0bc1664..a227ab6 100644 (file)
@@ -42,12 +42,15 @@ public class LocationProviderProxy implements LocationProviderInterface {
 
     private static final String TAG = "LocationProviderProxy";
 
+    public static final String SERVICE_ACTION =
+        "com.android.location.service.NetworkLocationProvider";
+
     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 = new Connection();  // never null
+    private Connection mServiceConnection;  // never null after ctor
 
     // cached values set by the location manager
     private boolean mLocationTracking = false;
@@ -58,28 +61,26 @@ public class LocationProviderProxy implements LocationProviderInterface {
     private NetworkInfo mNetworkInfo;
 
     // constructor for proxying location providers implemented in a separate service
-    public LocationProviderProxy(Context context, String name, String serviceName,
+    public LocationProviderProxy(Context context, String name, String packageName,
             Handler handler) {
         mContext = context;
         mName = name;
-        mIntent = new Intent(serviceName);
+        mIntent = new Intent(SERVICE_ACTION);
         mHandler = handler;
-        mContext.bindService(mIntent, mServiceConnection,
-                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
-                | Context.BIND_ALLOW_OOM_MANAGEMENT);
+        reconnect(packageName);
     }
 
-    /**
-     * When unbundled NetworkLocationService package is updated, we
-     * need to unbind from the old version and re-bind to the new one.
-     */
-    public void reconnect() {
+    /** Bind to service. Will reconnect if already connected */
+    public void reconnect(String packageName) {
         synchronized (mMutex) {
-            mContext.unbindService(mServiceConnection);
+            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);
+                    Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND |
+                    Context.BIND_ALLOW_OOM_MANAGEMENT);
         }
     }