Wake lock in LocationManagerService

本文探讨了Android设备上遇到的问题:当用户按下电源键关闭屏幕后,系统长时间无法进入休眠状态。通过分析发现Launcher应用程序持有了部分唤醒锁且未释放,导致设备无法正常休眠。文章详细介绍了LocationManagerService的工作原理,并给出了可能的原因分析及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值