Make location providers upgradeable.

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
This commit is contained in:
Nick Pelly
2012-05-27 16:12:45 -07:00
parent bbedfa0036
commit 00355d5a59
5 changed files with 142 additions and 57 deletions

View 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,25 +495,78 @@ 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
*/
@ -516,10 +574,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
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

View 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);

View 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);
}
}