Wake lock in LocationManagerService

    xiaoxiao2021-04-18  81

    Problem:

    On my Android device, I met a problem that system can't suspend after a long time after user has turn off screen by pressing the power key.

    From "dumpsys power" command output, I found the following info.

    Sleep timeout: -1 ms Screen off timeout: 30000 ms Screen dim duration: 6000 ms Wake Locks: size=2   PARTIAL_WAKE_LOCK              'LocationManagerService' (uid=1000, pid=658, ws=WorkSource{32071 com.android.launcher})   PARTIAL_WAKE_LOCK              'LocationManagerService' (uid=1000, pid=658, ws=WorkSource{32071 com.android.launcher}) Suspend Blockers: size=4   PowerManagerService.WakeLocks: ref count=1   PowerManagerService.Display: ref count=0   PowerManagerService.Broadcasts: ref count=0   PowerManagerService.WirelessChargerDetector: ref count=0 Display Power: state=OFF

    From the power dump info, we can see that PARTIAL_WAKE_LOCK was acquired twice by com.android.launcher.

    In normal case, this wake lock is used only for ILocationListener callback. and should not hold the lock for long time.

    But in my case, com.android.launcher keep holding the wake lock and don't release it.

    Analysis:

    User can use LocationManager.requestLocationUpdates method to register a listener to listen to location changes.

    The registration is implemented in LocationManagerService.

    Callback functions in listener will be called if the interesting events (location changes, status changes, provider changes....) happen.

    LMS follow the below steps to execute the callback function.

    1. In LocationManagerService, send a message to the message queue in app's (the app has register listener to LocationManagerService) thread.

    2. In LocationManagerService, acquire wake lock. Code is as follows.

    847 public boolean callLocationChangedLocked(Location location) { 848 if (mListener != null) { 849 try { 850 synchronized (this) { 851 // synchronize to ensure incrementPendingBroadcastsLocked() 852 // is called before decrementPendingBroadcasts() 853 mListener.onLocationChanged(new Location(location)); 854 // call this after broadcasting so we do not increment 855 // if we throw an exeption. 856 incrementPendingBroadcastsLocked(); 857 } 858 } catch (RemoteException e) { 859 return false; 860 } 861 } else { 862 Intent locationChanged = new Intent(); 863 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, new Location(location)); 864 try { 865 synchronized (this) { 866 // synchronize to ensure incrementPendingBroadcastsLocked() 867 // is called before decrementPendingBroadcasts() 868 mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler, 869 getResolutionPermission(mAllowedResolutionLevel)); 870 // call this after broadcasting so we do not increment 871 // if we throw an exeption. 872 incrementPendingBroadcastsLocked(); 873 } 874 } catch (PendingIntent.CanceledException e) { 875 return false; 876 } 877 } 878 return true; 879 }

    Call stack:

    W/System.err(  652): java.lang.Exception W/System.err(  652):     at com.android.server.LocationManagerService$Receiver.incrementPendingBroadcastsLocked(LocationManagerService.java:915) W/System.err(  652):     at com.android.server.LocationManagerService$Receiver.callLocationChangedLocked(LocationManagerService.java:823) W/System.err(  652):     at com.android.server.LocationManagerService.handleLocationChangedLocked(LocationManagerService.java:2271) W/System.err(  652):     at com.android.server.LocationManagerService.handleLocationChanged(LocationManagerService.java:2365) W/System.err(  652):     at com.android.server.LocationManagerService.access$1800(LocationManagerService.java:110) W/System.err(  652):     at com.android.server.LocationManagerService$LocationWorkerHandler.handleMessage(LocationManagerService.java:2331) W/System.err(  652):     at android.os.Handler.dispatchMessage(Handler.java:102) W/System.err(  652):     at android.os.Looper.loop(Looper.java:135) W/System.err(  652):     at android.os.HandlerThread.run(HandlerThread.java:61)

    3. In message handling loop of app thread, process the message and call the corresponding callback method.

    4. In app thread, call LocationManagerService.locationCallbackFinished to release wake lock.

    Call stack:

    W/System.err(  652):     at com.android.server.LocationManagerService$Receiver.decrementPendingBroadcastsLocked(LocationManagerService.java:924) W/System.err(  652):     at com.android.server.LocationManagerService$Receiver.access$1500(LocationManagerService.java:604) W/System.err(  652):     at com.android.server.LocationManagerService.locationCallbackFinished(LocationManagerService.java:959) W/System.err(  652):     at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:312) W/System.err(  652):     at android.location.LocationManager$ListenerTransport.access$000(LocationManager.java:224) W/System.err(  652):     at android.location.LocationManager$ListenerTransport$2.handleMessage(LocationManager.java:247) W/System.err(  652):     at android.os.Handler.dispatchMessage(Handler.java:102) W/System.err(  652):     at android.os.Looper.loop(Looper.java:135) W/System.err(  652):     at android.os.HandlerThread.run(HandlerThread.java:61)

    So the possible reason why wake lock is not release is:

    1. LMS has sent a message to app thread, and acquire the wake lock. But app thread do not process the message.

    2. LMS has sent the message and acquired the lock, and app thread is processing the message. But there is an exception happened during callback function.

    226 private class ListenerTransport extends ILocationListener.Stub { 227 private static final int TYPE_LOCATION_CHANGED = 1; 228 private static final int TYPE_STATUS_CHANGED = 2; 229 private static final int TYPE_PROVIDER_ENABLED = 3; 230 private static final int TYPE_PROVIDER_DISABLED = 4; 231 232 private LocationListener mListener; 233 private final Handler mListenerHandler; 234 235 ListenerTransport(LocationListener listener, Looper looper) { 236 mListener = listener; 237 238 if (looper == null) { 239 mListenerHandler = new Handler() { 240 @Override 241 public void handleMessage(Message msg) { 242 _handleMessage(msg); 243 } 244 }; 245 } else { 246 mListenerHandler = new Handler(looper) { 247 @Override 248 public void handleMessage(Message msg) { 249 _handleMessage(msg); 250 } 251 }; 252 } 253 } 254 255 @Override 256 public void onLocationChanged(Location location) { 257 Message msg = Message.obtain(); 258 msg.what = TYPE_LOCATION_CHANGED; 259 msg.obj = location; 260 mListenerHandler.sendMessage(msg); 261 } 262 263 @Override 264 public void onStatusChanged(String provider, int status, Bundle extras) { 265 Message msg = Message.obtain(); 266 msg.what = TYPE_STATUS_CHANGED; 267 Bundle b = new Bundle(); 268 b.putString("provider", provider); 269 b.putInt("status", status); 270 if (extras != null) { 271 b.putBundle("extras", extras); 272 } 273 msg.obj = b; 274 mListenerHandler.sendMessage(msg); 275 } 276 277 @Override 278 public void onProviderEnabled(String provider) { 279 Message msg = Message.obtain(); 280 msg.what = TYPE_PROVIDER_ENABLED; 281 msg.obj = provider; 282 mListenerHandler.sendMessage(msg); 283 } 284 285 @Override 286 public void onProviderDisabled(String provider) { 287 Message msg = Message.obtain(); 288 msg.what = TYPE_PROVIDER_DISABLED; 289 msg.obj = provider; 290 mListenerHandler.sendMessage(msg); 291 } 292 293 private void _handleMessage(Message msg) { 294 switch (msg.what) { 295 case TYPE_LOCATION_CHANGED: 296 Location location = new Location((Location) msg.obj); 297 mListener.onLocationChanged(location); 298 break; 299 case TYPE_STATUS_CHANGED: 300 Bundle b = (Bundle) msg.obj; 301 String provider = b.getString("provider"); 302 int status = b.getInt("status"); 303 Bundle extras = b.getBundle("extras"); 304 mListener.onStatusChanged(provider, status, extras); 305 break; 306 case TYPE_PROVIDER_ENABLED: 307 mListener.onProviderEnabled((String) msg.obj); 308 break; 309 case TYPE_PROVIDER_DISABLED: 310 mListener.onProviderDisabled((String) msg.obj); 311 break; 312 } 313 try { 314 mService.locationCallbackFinished(this); it will not be called if above functions throw an exception 315 } catch (RemoteException e) { 316 throw e.rethrowFromSystemServer(); 317 } 318 } 319 }

    If the above two case happen, there is no chance for app thread to callLocationManagerService.locationCallbackFinished to release wake lock. But user can still release the wake lock by calling LocationManager.removeUpdates.

    In my case, launcher register a location listener by calling LocationManager.requestLocationUpdates.

    In the listener, it does some other operations and calling LocationManager.removeUpdates at the end of callback function.

    So there is no chance to call LocationManagerService.locationCallbackFinishedto release wake lock if callback is not called or there is an exception happen before LocationManager.removeUpdates

    So launcher will keep holding the wake lock.

    Solution:

    To solve the problem, we use CountDownLatch to wait until onLocationChanged is called or timer expire.

    1. register a listener

    2.CountDownLatch.await(5,TimeUnit.SECONDS);

    3. if onLocationChanged is called, stop waiting and remove listener.

    4. if time out, remove listener.

    转载请注明原文地址: https://ju.6miu.com/read-674772.html

    最新回复(0)