summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZhentao Sun <robinvane@google.com>2013-04-17 17:47:53 -0700
committerZhentao Sun <robinvane@google.com>2013-04-22 10:02:08 -0700
commitc5fc9988f11acc58229fb6ae80d346277318ada4 (patch)
tree164c0888771ce16e2a8d4ae8b146cd6f20c9c312
parent339ac85483145972da010ad34cbcb29ed70cb822 (diff)
downloadframeworks_base-c5fc9988f11acc58229fb6ae80d346277318ada4.zip
frameworks_base-c5fc9988f11acc58229fb6ae80d346277318ada4.tar.gz
frameworks_base-c5fc9988f11acc58229fb6ae80d346277318ada4.tar.bz2
Made it easier to disable overlay mechanism of location components.
Fixed b/8276827 Vendor might want to provide their own implementation of "network location", "fused location" and "geocoder" service. Location manager now allows those service to be replaced by packages that have the same signature as one of the packages in config_locationProviderPackageNames. Such behavior might not be desirable on some devices. This change make this behavior configurable by 3 boolean flags. Details: - Added three boolean flags in core/res/res/values/config.xml to enable or disable NLP/FLP/Geocoder overlay - Added 3 package name for the stock NLP/FLP/Geocoder. They are needed only when overlay is disabled because LocationManagerService need to know which package is preferred when searching for NLP/FLP/Geocoder service. - Made ServiceWatcher able to handle non-overlayable services. - Fixed an NPE isue in ServiceWatcher. mPm.queryIntentServicesAsUser might return null. - Fixed an bug: justCheckThisPackage in bindBestPackageLocked is always ignored. Change-Id: Id221961ac7c3aa8ad44b894f9523f04f770ae237
-rw-r--r--core/res/res/values/config.xml54
-rw-r--r--core/res/res/values/symbols.xml8
-rw-r--r--services/java/com/android/server/LocationManagerService.java29
-rw-r--r--services/java/com/android/server/ServiceWatcher.java134
-rw-r--r--services/java/com/android/server/location/GeocoderProxy.java14
-rw-r--r--services/java/com/android/server/location/GeofenceProxy.java15
-rw-r--r--services/java/com/android/server/location/LocationProviderProxy.java15
7 files changed, 204 insertions, 65 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 84e300a..5c772b2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -633,6 +633,60 @@
<!-- True if WallpaperService is enabled -->
<bool name="config_enableWallpaperService">true</bool>
+ <!-- Whether to enable network location overlay which allows network
+ location provider to be replaced by an app at run-time. When disabled,
+ only the config_networkLocationProviderPackageName package will be
+ searched for network location provider, otherwise packages whose
+ signature matches the signatures of config_locationProviderPackageNames
+ will be searched, and the service with the highest version number will
+ be picked. Anyone who wants to disable the overlay mechanism can set it
+ to false.
+ -->
+ <bool name="config_enableNetworkLocationOverlay" translatable="false">true</bool>
+ <!-- Package name providing network location support. Used only when
+ config_enableNetworkLocationOverlay is false. -->
+ <string name="config_networkLocationProviderPackageName" translatable="false">@null</string>
+
+ <!-- Whether to enable fused location provider overlay which allows fused
+ location provider to be replaced by an app at run-time. When disabled,
+ only the config_fusedLocationProviderPackageName package will be
+ searched for fused location provider, otherwise packages whose
+ signature matches the signatures of config_locationProviderPackageNames
+ will be searched, and the service with the highest version number will
+ be picked. Anyone who wants to disable the overlay mechanism can set it
+ to false.
+ -->
+ <bool name="config_enableFusedLocationOverlay" translatable="false">true</bool>
+ <!-- Package name providing fused location support. Used only when
+ config_enableFusedLocationOverlay is false. -->
+ <string name="config_fusedLocationProviderPackageName" translatable="false">com.android.location.fused</string>
+
+ <!-- Whether to enable geocoder overlay which allows geocoder to be replaced
+ by an app at run-time. When disabled, only the
+ config_geocoderProviderPackageName package will be searched for
+ geocoder, otherwise packages whose signature matches the signatures of
+ config_locationProviderPackageNames will be searched, and the service
+ with the highest version number will be picked. Anyone who wants to
+ disable the overlay mechanism can set it to false.
+ -->
+ <bool name="config_enableGeocoderOverlay" translatable="false">true</bool>
+ <!-- Package name providing geocoder API support. Used only when
+ config_enableGeocoderOverlay is false. -->
+ <string name="config_geocoderProviderPackageName" translatable="false">@null</string>
+
+ <!-- Whether to enable geofence overlay which allows geofence to be replaced
+ by an app at run-time. When disabled, only the
+ config_geofenceProviderPackageName package will be searched for
+ geofence implementation, otherwise packages whose signature matches the
+ signatures of config_locationProviderPackageNames will be searched, and
+ the service with the highest version number will be picked. Anyone who
+ wants to disable the overlay mechanism can set it to false.
+ -->
+ <bool name="config_enableGeofenceOverlay" translatable="false">true</bool>
+ <!-- Package name providing geofence API support. Used only when
+ config_enableGeofenceOverlay is false. -->
+ <string name="config_geofenceProviderPackageName" translatable="false">@null</string>
+
<!-- Package name(s) containing location provider support.
These packages can contain services implementing location providers,
such as the Geocode Provider, Network Location Provider, and
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fdf7c8f..2158e90 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1534,6 +1534,10 @@
<java-symbol type="array" name="config_notificationFallbackVibePattern" />
<java-symbol type="bool" name="config_animateScreenLights" />
<java-symbol type="bool" name="config_automatic_brightness_available" />
+ <java-symbol type="bool" name="config_enableFusedLocationOverlay" />
+ <java-symbol type="bool" name="config_enableGeocoderOverlay" />
+ <java-symbol type="bool" name="config_enableGeofenceOverlay" />
+ <java-symbol type="bool" name="config_enableNetworkLocationOverlay" />
<java-symbol type="bool" name="config_sf_limitedAlpha" />
<java-symbol type="bool" name="config_unplugTurnsOnScreen" />
<java-symbol type="bool" name="config_wifi_background_scan_support" />
@@ -1618,6 +1622,10 @@
<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_fusedLocationProviderPackageName" />
+ <java-symbol type="string" name="config_geocoderProviderPackageName" />
+ <java-symbol type="string" name="config_geofenceProviderPackageName" />
+ <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" />
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index b47e8a0..2675309 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -380,7 +380,10 @@ public class LocationManagerService extends ILocationManager.Stub {
mContext,
LocationManager.NETWORK_PROVIDER,
NETWORK_LOCATION_SERVICE_ACTION,
- providerPackageNames, mLocationHandler);
+ com.android.internal.R.bool.config_enableNetworkLocationOverlay,
+ com.android.internal.R.string.config_networkLocationProviderPackageName,
+ com.android.internal.R.array.config_locationProviderPackageNames,
+ mLocationHandler);
if (networkProvider != null) {
mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
mProxyProviders.add(networkProvider);
@@ -394,7 +397,10 @@ public class LocationManagerService extends ILocationManager.Stub {
mContext,
LocationManager.FUSED_PROVIDER,
FUSED_LOCATION_SERVICE_ACTION,
- providerPackageNames, mLocationHandler);
+ com.android.internal.R.bool.config_enableFusedLocationOverlay,
+ com.android.internal.R.string.config_fusedLocationProviderPackageName,
+ com.android.internal.R.array.config_locationProviderPackageNames,
+ mLocationHandler);
if (fusedLocationProvider != null) {
addProviderLocked(fusedLocationProvider);
mProxyProviders.add(fusedLocationProvider);
@@ -406,15 +412,22 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// bind to geocoder provider
- mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames,
+ mGeocodeProvider = GeocoderProxy.createAndBind(mContext,
+ com.android.internal.R.bool.config_enableGeocoderOverlay,
+ com.android.internal.R.string.config_geocoderProviderPackageName,
+ com.android.internal.R.array.config_locationProviderPackageNames,
mLocationHandler);
if (mGeocodeProvider == null) {
Slog.e(TAG, "no geocoder provider found");
}
// bind to geofence provider
- GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, providerPackageNames,
- mLocationHandler, gpsProvider.getGpsGeofenceProxy());
+ GeofenceProxy provider = GeofenceProxy.createAndBind(mContext,
+ com.android.internal.R.bool.config_enableGeofenceOverlay,
+ com.android.internal.R.string.config_geofenceProviderPackageName,
+ com.android.internal.R.array.config_locationProviderPackageNames,
+ mLocationHandler,
+ gpsProvider.getGpsGeofenceProxy());
if (provider == null) {
Slog.e(TAG, "no geofence provider found");
}
@@ -1290,14 +1303,14 @@ public class LocationManagerService extends ILocationManager.Stub {
if (name == null) {
throw new IllegalArgumentException("provider name must not be null");
}
+
+ if (D) Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
+ + " " + name + " " + request + " from " + packageName + "(" + uid + ")");
LocationProviderInterface provider = mProvidersByName.get(name);
if (provider == null) {
throw new IllegalArgumentException("provider doesn't exisit: " + provider);
}
- if (D) Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
- + " " + name + " " + request + " from " + packageName + "(" + uid + ")");
-
UpdateRecord record = new UpdateRecord(name, request, receiver);
UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
if (oldRecord != null) {
diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java
index 6078d8a..5c7bfab 100644
--- a/services/java/com/android/server/ServiceWatcher.java
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -27,6 +27,7 @@ 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.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
@@ -36,6 +37,7 @@ import com.android.internal.content.PackageMonitor;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -53,6 +55,13 @@ public class ServiceWatcher implements ServiceConnection {
private final PackageManager mPm;
private final List<HashSet<Signature>> mSignatureSets;
private final String mAction;
+
+ /**
+ * If mServicePackageName is not null, only this package will be searched for the service that
+ * implements mAction. When null, all packages in the system that matches one of the signature
+ * in mSignatureSets are searched.
+ */
+ private final String mServicePackageName;
private final Runnable mNewServiceWork;
private final Handler mHandler;
@@ -87,19 +96,40 @@ public class ServiceWatcher implements ServiceConnection {
}
public ServiceWatcher(Context context, String logTag, String action,
- List<String> initialPackageNames, Runnable newServiceWork, Handler handler) {
+ int overlaySwitchResId, int defaultServicePackageNameResId,
+ int initialPackageNamesResId, Runnable newServiceWork,
+ Handler handler) {
mContext = context;
mTag = logTag;
mAction = action;
mPm = mContext.getPackageManager();
mNewServiceWork = newServiceWork;
mHandler = handler;
+ Resources resources = context.getResources();
+
+ // Whether to enable service overlay.
+ boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
+ ArrayList<String> initialPackageNames = new ArrayList<String>();
+ if (enableOverlay) {
+ // A list of package names used to create the signatures.
+ String[] pkgs = resources.getStringArray(initialPackageNamesResId);
+ if (pkgs != null) initialPackageNames.addAll(Arrays.asList(pkgs));
+ mServicePackageName = null;
+ if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
+ } else {
+ // The default package name that is searched for service implementation when overlay is
+ // disabled.
+ String servicePackageName = resources.getString(defaultServicePackageNameResId);
+ if (servicePackageName != null) initialPackageNames.add(servicePackageName);
+ mServicePackageName = servicePackageName;
+ if (D) Log.d(mTag, "Overlay disabled, default package=" + servicePackageName);
+ }
mSignatureSets = getSignatureSets(context, initialPackageNames);
}
public boolean start() {
synchronized (mLock) {
- if (!bindBestPackageLocked(null)) return false;
+ if (!bindBestPackageLocked(mServicePackageName)) return false;
}
// listen for user change
@@ -115,8 +145,10 @@ public class ServiceWatcher implements ServiceConnection {
}
}, UserHandle.ALL, intentFilter, null, mHandler);
- // listen for relevant package changes
- mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+ // listen for relevant package changes if service overlay is enabled.
+ if (mServicePackageName == null) {
+ mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+ }
return true;
}
@@ -133,50 +165,55 @@ public class ServiceWatcher implements ServiceConnection {
if (justCheckThisPackage != null) {
intent.setPackage(justCheckThisPackage);
}
- List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction),
+ List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent,
PackageManager.GET_META_DATA, UserHandle.USER_OWNER);
int bestVersion = Integer.MIN_VALUE;
String bestPackage = null;
boolean bestIsMultiuser = false;
- for (ResolveInfo rInfo : rInfos) {
- String packageName = rInfo.serviceInfo.packageName;
-
- // check signature
- try {
- PackageInfo pInfo;
- pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
- if (!isSignatureMatch(pInfo.signatures)) {
- Log.w(mTag, packageName + " resolves service " + mAction +
- ", but has wrong signature, ignoring");
+ if (rInfos != null) {
+ for (ResolveInfo rInfo : rInfos) {
+ String packageName = rInfo.serviceInfo.packageName;
+
+ // check signature
+ try {
+ PackageInfo pInfo;
+ pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ if (!isSignatureMatch(pInfo.signatures)) {
+ Log.w(mTag, packageName + " resolves service " + mAction
+ + ", but has wrong signature, ignoring");
+ continue;
+ }
+ } catch (NameNotFoundException e) {
+ Log.wtf(mTag, e);
continue;
}
- } catch (NameNotFoundException e) {
- Log.wtf(mTag, e);
- continue;
- }
- // check metadata
- int version = Integer.MIN_VALUE;
- boolean isMultiuser = false;
- if (rInfo.serviceInfo.metaData != null) {
- version = rInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION,
- Integer.MIN_VALUE);
- isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
+ // check metadata
+ int version = Integer.MIN_VALUE;
+ boolean isMultiuser = false;
+ if (rInfo.serviceInfo.metaData != null) {
+ version = rInfo.serviceInfo.metaData.getInt(
+ EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
+ isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
+ }
+
+ if (version > mVersion) {
+ bestVersion = version;
+ bestPackage = packageName;
+ bestIsMultiuser = isMultiuser;
+ }
}
- if (version > mVersion) {
- bestVersion = version;
- bestPackage = packageName;
- bestIsMultiuser = isMultiuser;
+ 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 package: " + bestPackage)));
}
+ } else {
+ if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction);
}
-
- 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 package: "
- + bestPackage)));
-
if (bestPackage != null) {
bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser);
return true;
@@ -243,8 +280,9 @@ public class ServiceWatcher implements ServiceConnection {
// package updated, make sure to rebind
unbindLocked();
}
- // check the updated package in case it is better
- bindBestPackageLocked(packageName);
+ // Need to check all packages because this method is also called when a
+ // system app is uninstalled and the stock version in reinstalled.
+ bindBestPackageLocked(null);
}
}
@@ -256,7 +294,7 @@ public class ServiceWatcher implements ServiceConnection {
unbindLocked();
}
// check the new package is case it is better
- bindBestPackageLocked(packageName);
+ bindBestPackageLocked(null);
}
}
@@ -271,6 +309,20 @@ public class ServiceWatcher implements ServiceConnection {
}
}
}
+
+ @Override
+ public boolean onPackageChanged(String packageName, int uid, String[] components) {
+ synchronized (mLock) {
+ if (packageName.equals(mPackageName)) {
+ // service enabled or disabled, make sure to rebind
+ unbindLocked();
+ }
+ // the service might be disabled, need to search for a new
+ // package
+ bindBestPackageLocked(null);
+ }
+ return super.onPackageChanged(packageName, uid, components);
+ }
};
@Override
@@ -323,7 +375,7 @@ public class ServiceWatcher implements ServiceConnection {
synchronized (mLock) {
if (!mIsMultiuser) {
unbindLocked();
- bindBestPackageLocked(null);
+ bindBestPackageLocked(mServicePackageName);
}
}
}
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
index 8103695..5d4a770 100644
--- a/services/java/com/android/server/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -40,8 +40,10 @@ public class GeocoderProxy {
private final ServiceWatcher mServiceWatcher;
public static GeocoderProxy createAndBind(Context context,
- List<String> initialPackageNames, Handler handler) {
- GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames, handler);
+ int overlaySwitchResId, int defaultServicePackageNameResId,
+ int initialPackageNamesResId, Handler handler) {
+ GeocoderProxy proxy = new GeocoderProxy(context, overlaySwitchResId,
+ defaultServicePackageNameResId, initialPackageNamesResId, handler);
if (proxy.bind()) {
return proxy;
} else {
@@ -49,11 +51,13 @@ public class GeocoderProxy {
}
}
- public GeocoderProxy(Context context, List<String> initialPackageNames, Handler handler) {
+ private GeocoderProxy(Context context,
+ int overlaySwitchResId, int defaultServicePackageNameResId,
+ int initialPackageNamesResId, Handler handler) {
mContext = context;
- mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, initialPackageNames,
- null, handler);
+ mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, overlaySwitchResId,
+ defaultServicePackageNameResId, initialPackageNamesResId, null, handler);
}
private boolean bind () {
diff --git a/services/java/com/android/server/location/GeofenceProxy.java b/services/java/com/android/server/location/GeofenceProxy.java
index 36e9fcc..f6be27b 100644
--- a/services/java/com/android/server/location/GeofenceProxy.java
+++ b/services/java/com/android/server/location/GeofenceProxy.java
@@ -59,8 +59,10 @@ public final class GeofenceProxy {
};
public static GeofenceProxy createAndBind(Context context,
- List<String> initialPackageNames, Handler handler, IGpsGeofenceHardware gpsGeofence) {
- GeofenceProxy proxy = new GeofenceProxy(context, initialPackageNames, handler, gpsGeofence);
+ int overlaySwitchResId, int defaultServicePackageNameResId,
+ int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence) {
+ GeofenceProxy proxy = new GeofenceProxy(context, overlaySwitchResId,
+ defaultServicePackageNameResId, initialPackageNamesResId, handler, gpsGeofence);
if (proxy.bindGeofenceProvider()) {
return proxy;
} else {
@@ -68,11 +70,12 @@ public final class GeofenceProxy {
}
}
- private GeofenceProxy(Context context, List<String> initialPackageName, Handler handler,
- IGpsGeofenceHardware gpsGeofence) {
+ private GeofenceProxy(Context context,
+ int overlaySwitchResId, int defaultServicePackageNameResId,
+ int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence) {
mContext = context;
- mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, initialPackageName,
- mRunnable, handler);
+ mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
+ defaultServicePackageNameResId, initialPackageNamesResId, mRunnable, handler);
mGpsGeofenceHardware = gpsGeofence;
bindHardwareGeofence();
}
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index 7faf72c..14db862 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -53,10 +53,13 @@ public class LocationProviderProxy implements LocationProviderInterface {
private ProviderRequest mRequest = null;
private WorkSource mWorksource = new WorkSource();
- public static LocationProviderProxy createAndBind(Context context, String name, String action,
- List<String> initialPackageNames, Handler handler) {
+ public static LocationProviderProxy createAndBind(
+ Context context, String name, String action,
+ int overlaySwitchResId, int defaultServicePackageNameResId,
+ int initialPackageNamesResId, Handler handler) {
LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
- initialPackageNames, handler);
+ overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId,
+ handler);
if (proxy.bind()) {
return proxy;
} else {
@@ -65,10 +68,12 @@ public class LocationProviderProxy implements LocationProviderInterface {
}
private LocationProviderProxy(Context context, String name, String action,
- List<String> initialPackageNames, Handler handler) {
+ int overlaySwitchResId, int defaultServicePackageNameResId,
+ int initialPackageNamesResId, Handler handler) {
mContext = context;
mName = name;
- mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames,
+ mServiceWatcher = new ServiceWatcher(mContext, TAG + "-" + name, action, overlaySwitchResId,
+ defaultServicePackageNameResId, initialPackageNamesResId,
mNewServiceWork, handler);
}