StatusBarManagerService.java revision c552b04cb4aac9d31dbaf9744f32ddc14886e222
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.statusbar;
18
19import android.app.StatusBarManager;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.os.Binder;
23import android.os.Bundle;
24import android.os.Handler;
25import android.os.IBinder;
26import android.os.RemoteException;
27import android.os.UserHandle;
28import android.util.ArrayMap;
29import android.util.Slog;
30import com.android.internal.statusbar.IStatusBar;
31import com.android.internal.statusbar.IStatusBarService;
32import com.android.internal.statusbar.NotificationVisibility;
33import com.android.internal.statusbar.StatusBarIcon;
34import com.android.server.LocalServices;
35import com.android.server.notification.NotificationDelegate;
36import com.android.server.wm.WindowManagerService;
37
38import java.io.FileDescriptor;
39import java.io.PrintWriter;
40import java.util.ArrayList;
41import java.util.List;
42
43
44/**
45 * A note on locking:  We rely on the fact that calls onto mBar are oneway or
46 * if they are local, that they just enqueue messages to not deadlock.
47 */
48public class StatusBarManagerService extends IStatusBarService.Stub {
49    private static final String TAG = "StatusBarManagerService";
50    private static final boolean SPEW = false;
51
52    private final Context mContext;
53    private final WindowManagerService mWindowManager;
54    private Handler mHandler = new Handler();
55    private NotificationDelegate mNotificationDelegate;
56    private volatile IStatusBar mBar;
57    private ArrayMap<String, StatusBarIcon> mIcons = new ArrayMap<>();
58
59    // for disabling the status bar
60    private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
61    private IBinder mSysUiVisToken = new Binder();
62    private int mDisabled1 = 0;
63    private int mDisabled2 = 0;
64
65    private Object mLock = new Object();
66    // encompasses lights-out mode and other flags defined on View
67    private int mSystemUiVisibility = 0;
68    private boolean mMenuVisible = false;
69    private int mImeWindowVis = 0;
70    private int mImeBackDisposition;
71    private boolean mShowImeSwitcher;
72    private IBinder mImeToken = null;
73    private int mCurrentUserId;
74
75    private class DisableRecord implements IBinder.DeathRecipient {
76        int userId;
77        String pkg;
78        int what1;
79        int what2;
80        IBinder token;
81
82        public void binderDied() {
83            Slog.i(TAG, "binder died for pkg=" + pkg);
84            disableForUser(0, token, pkg, userId);
85            disable2ForUser(0, token, pkg, userId);
86            token.unlinkToDeath(this, 0);
87        }
88    }
89
90    /**
91     * Construct the service, add the status bar view to the window manager
92     */
93    public StatusBarManagerService(Context context, WindowManagerService windowManager) {
94        mContext = context;
95        mWindowManager = windowManager;
96
97        LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
98    }
99
100    /**
101     * Private API used by NotificationManagerService.
102     */
103    private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() {
104        private boolean mNotificationLightOn;
105
106        @Override
107        public void setNotificationDelegate(NotificationDelegate delegate) {
108            mNotificationDelegate = delegate;
109        }
110
111        @Override
112        public void buzzBeepBlinked() {
113            if (mBar != null) {
114                try {
115                    mBar.buzzBeepBlinked();
116                } catch (RemoteException ex) {
117                }
118            }
119        }
120
121        @Override
122        public void notificationLightPulse(int argb, int onMillis, int offMillis) {
123            mNotificationLightOn = true;
124            if (mBar != null) {
125                try {
126                    mBar.notificationLightPulse(argb, onMillis, offMillis);
127                } catch (RemoteException ex) {
128                }
129            }
130        }
131
132        @Override
133        public void notificationLightOff() {
134            if (mNotificationLightOn) {
135                mNotificationLightOn = false;
136                if (mBar != null) {
137                    try {
138                        mBar.notificationLightOff();
139                    } catch (RemoteException ex) {
140                    }
141                }
142            }
143        }
144
145        @Override
146        public void showScreenPinningRequest() {
147            if (mBar != null) {
148                try {
149                    mBar.showScreenPinningRequest();
150                } catch (RemoteException e) {
151                }
152            }
153        }
154
155        @Override
156        public void showAssistDisclosure() {
157            if (mBar != null) {
158                try {
159                    mBar.showAssistDisclosure();
160                } catch (RemoteException e) {
161                }
162            }
163        }
164
165        @Override
166        public void startAssist(Bundle args) {
167            if (mBar != null) {
168                try {
169                    mBar.startAssist(args);
170                } catch (RemoteException e) {
171                }
172            }
173        }
174
175        @Override
176        public void onCameraLaunchGestureDetected(int source) {
177            if (mBar != null) {
178                try {
179                    mBar.onCameraLaunchGestureDetected(source);
180                } catch (RemoteException e) {
181                }
182            }
183        }
184    };
185
186    // ================================================================================
187    // From IStatusBarService
188    // ================================================================================
189    @Override
190    public void expandNotificationsPanel() {
191        enforceExpandStatusBar();
192
193        if (mBar != null) {
194            try {
195                mBar.animateExpandNotificationsPanel();
196            } catch (RemoteException ex) {
197            }
198        }
199    }
200
201    @Override
202    public void collapsePanels() {
203        enforceExpandStatusBar();
204
205        if (mBar != null) {
206            try {
207                mBar.animateCollapsePanels();
208            } catch (RemoteException ex) {
209            }
210        }
211    }
212
213    @Override
214    public void expandSettingsPanel(String subPanel) {
215        enforceExpandStatusBar();
216
217        if (mBar != null) {
218            try {
219                mBar.animateExpandSettingsPanel(subPanel);
220            } catch (RemoteException ex) {
221            }
222        }
223    }
224
225    @Override
226    public void disable(int what, IBinder token, String pkg) {
227        disableForUser(what, token, pkg, mCurrentUserId);
228    }
229
230    @Override
231    public void disableForUser(int what, IBinder token, String pkg, int userId) {
232        enforceStatusBar();
233
234        synchronized (mLock) {
235            disableLocked(userId, what, token, pkg, 1);
236        }
237    }
238
239    /**
240     * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
241     * To re-enable everything, pass {@link #DISABLE_NONE}.
242     *
243     * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
244     */
245    @Override
246    public void disable2(int what, IBinder token, String pkg) {
247        disableForUser(what, token, pkg, mCurrentUserId);
248    }
249
250    /**
251     * Disable additional status bar features for a given user. Pass the bitwise-or of the
252     * DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}.
253     *
254     * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
255     */
256    @Override
257    public void disable2ForUser(int what, IBinder token, String pkg, int userId) {
258        enforceStatusBar();
259
260        synchronized (mLock) {
261            disableLocked(userId, what, token, pkg, 2);
262        }
263    }
264
265    private void disableLocked(int userId, int what, IBinder token, String pkg, int whichFlag) {
266        // It's important that the the callback and the call to mBar get done
267        // in the same order when multiple threads are calling this function
268        // so they are paired correctly.  The messages on the handler will be
269        // handled in the order they were enqueued, but will be outside the lock.
270        manageDisableListLocked(userId, what, token, pkg, whichFlag);
271
272        // Ensure state for the current user is applied, even if passed a non-current user.
273        final int net1 = gatherDisableActionsLocked(mCurrentUserId, 1);
274        final int net2 = gatherDisableActionsLocked(mCurrentUserId, 2);
275        if (net1 != mDisabled1 || net2 != mDisabled2) {
276            mDisabled1 = net1;
277            mDisabled2 = net2;
278            mHandler.post(new Runnable() {
279                    public void run() {
280                        mNotificationDelegate.onSetDisabled(net1);
281                    }
282                });
283            if (mBar != null) {
284                try {
285                    mBar.disable(net1, net2);
286                } catch (RemoteException ex) {
287                }
288            }
289        }
290    }
291
292    @Override
293    public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
294            String contentDescription) {
295        enforceStatusBar();
296
297        synchronized (mIcons) {
298            StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
299                    iconLevel, 0, contentDescription);
300            //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
301            mIcons.put(slot, icon);
302
303            if (mBar != null) {
304                try {
305                    mBar.setIcon(slot, icon);
306                } catch (RemoteException ex) {
307                }
308            }
309        }
310    }
311
312    @Override
313    public void setIconVisibility(String slot, boolean visibility) {
314        enforceStatusBar();
315
316        synchronized (mIcons) {
317            StatusBarIcon icon = mIcons.get(slot);
318            if (icon == null) {
319                return;
320            }
321            if (icon.visible != visibility) {
322                icon.visible = visibility;
323
324                if (mBar != null) {
325                    try {
326                        mBar.setIcon(slot, icon);
327                    } catch (RemoteException ex) {
328                    }
329                }
330            }
331        }
332    }
333
334    @Override
335    public void removeIcon(String slot) {
336        enforceStatusBar();
337
338        synchronized (mIcons) {
339            mIcons.remove(slot);
340
341            if (mBar != null) {
342                try {
343                    mBar.removeIcon(slot);
344                } catch (RemoteException ex) {
345                }
346            }
347        }
348    }
349
350    /**
351     * Hide or show the on-screen Menu key. Only call this from the window manager, typically in
352     * response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
353     * to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
354     */
355    @Override
356    public void topAppWindowChanged(final boolean menuVisible) {
357        enforceStatusBar();
358
359        if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
360
361        synchronized(mLock) {
362            mMenuVisible = menuVisible;
363            mHandler.post(new Runnable() {
364                    public void run() {
365                        if (mBar != null) {
366                            try {
367                                mBar.topAppWindowChanged(menuVisible);
368                            } catch (RemoteException ex) {
369                            }
370                        }
371                    }
372                });
373        }
374    }
375
376    @Override
377    public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition,
378            final boolean showImeSwitcher) {
379        enforceStatusBar();
380
381        if (SPEW) {
382            Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
383        }
384
385        synchronized(mLock) {
386            // In case of IME change, we need to call up setImeWindowStatus() regardless of
387            // mImeWindowVis because mImeWindowVis may not have been set to false when the
388            // previous IME was destroyed.
389            mImeWindowVis = vis;
390            mImeBackDisposition = backDisposition;
391            mImeToken = token;
392            mShowImeSwitcher = showImeSwitcher;
393            mHandler.post(new Runnable() {
394                public void run() {
395                    if (mBar != null) {
396                        try {
397                            mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher);
398                        } catch (RemoteException ex) {
399                        }
400                    }
401                }
402            });
403        }
404    }
405
406    @Override
407    public void setSystemUiVisibility(int vis, int mask, String cause) {
408        // also allows calls from window manager which is in this process.
409        enforceStatusBarService();
410
411        if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
412
413        synchronized (mLock) {
414            updateUiVisibilityLocked(vis, mask);
415            disableLocked(
416                    mCurrentUserId,
417                    vis & StatusBarManager.DISABLE_MASK,
418                    mSysUiVisToken,
419                    cause, 1);
420        }
421    }
422
423    private void updateUiVisibilityLocked(final int vis, final int mask) {
424        if (mSystemUiVisibility != vis) {
425            mSystemUiVisibility = vis;
426            mHandler.post(new Runnable() {
427                    public void run() {
428                        if (mBar != null) {
429                            try {
430                                mBar.setSystemUiVisibility(vis, mask);
431                            } catch (RemoteException ex) {
432                            }
433                        }
434                    }
435                });
436        }
437    }
438
439    @Override
440    public void toggleRecentApps() {
441        if (mBar != null) {
442            try {
443                mBar.toggleRecentApps();
444            } catch (RemoteException ex) {}
445        }
446    }
447
448    @Override
449    public void preloadRecentApps() {
450        if (mBar != null) {
451            try {
452                mBar.preloadRecentApps();
453            } catch (RemoteException ex) {}
454        }
455    }
456
457    @Override
458    public void cancelPreloadRecentApps() {
459        if (mBar != null) {
460            try {
461                mBar.cancelPreloadRecentApps();
462            } catch (RemoteException ex) {}
463        }
464    }
465
466    @Override
467    public void showRecentApps(boolean triggeredFromAltTab) {
468        if (mBar != null) {
469            try {
470                mBar.showRecentApps(triggeredFromAltTab);
471            } catch (RemoteException ex) {}
472        }
473    }
474
475    @Override
476    public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
477        if (mBar != null) {
478            try {
479                mBar.hideRecentApps(triggeredFromAltTab, triggeredFromHomeKey);
480            } catch (RemoteException ex) {}
481        }
482    }
483
484    @Override
485    public void toggleKeyboardShortcutsMenu() {
486        if (mBar != null) {
487            try {
488                mBar.toggleKeyboardShortcutsMenu();
489            } catch (RemoteException ex) {}
490        }
491    }
492
493    @Override
494    public void requestTvPictureInPicture() {
495        if (mBar != null) {
496            try {
497                mBar.requestTvPictureInPicture();
498            } catch (RemoteException ex) {}
499        }
500    }
501
502    @Override
503    public void setCurrentUser(int newUserId) {
504        if (SPEW) Slog.d(TAG, "Setting current user to user " + newUserId);
505        mCurrentUserId = newUserId;
506    }
507
508    @Override
509    public void setWindowState(int window, int state) {
510        if (mBar != null) {
511            try {
512                mBar.setWindowState(window, state);
513            } catch (RemoteException ex) {}
514        }
515    }
516
517    @Override
518    public void appTransitionPending() {
519        if (mBar != null) {
520            try {
521                mBar.appTransitionPending();
522            } catch (RemoteException ex) {}
523        }
524    }
525
526    @Override
527    public void appTransitionCancelled() {
528        if (mBar != null) {
529            try {
530                mBar.appTransitionCancelled();
531            } catch (RemoteException ex) {}
532        }
533    }
534
535    @Override
536    public void appTransitionStarting(long statusBarAnimationsStartTime,
537            long statusBarAnimationsDuration) {
538        if (mBar != null) {
539            try {
540                mBar.appTransitionStarting(
541                        statusBarAnimationsStartTime, statusBarAnimationsDuration);
542            } catch (RemoteException ex) {}
543        }
544    }
545
546    @Override
547    public void startAssist(Bundle args) {
548        if (mBar != null) {
549            try {
550                mBar.startAssist(args);
551            } catch (RemoteException ex) {}
552        }
553    }
554
555    private void enforceStatusBar() {
556        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
557                "StatusBarManagerService");
558    }
559
560    private void enforceExpandStatusBar() {
561        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR,
562                "StatusBarManagerService");
563    }
564
565    private void enforceStatusBarService() {
566        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
567                "StatusBarManagerService");
568    }
569
570    // ================================================================================
571    // Callbacks from the status bar service.
572    // ================================================================================
573    @Override
574    public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
575            List<StatusBarIcon> iconList, int switches[], List<IBinder> binders) {
576        enforceStatusBarService();
577
578        Slog.i(TAG, "registerStatusBar bar=" + bar);
579        mBar = bar;
580        synchronized (mIcons) {
581            for (String slot : mIcons.keySet()) {
582                iconSlots.add(slot);
583                iconList.add(mIcons.get(slot));
584            }
585        }
586        synchronized (mLock) {
587            switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
588            switches[1] = mSystemUiVisibility;
589            switches[2] = mMenuVisible ? 1 : 0;
590            switches[3] = mImeWindowVis;
591            switches[4] = mImeBackDisposition;
592            switches[5] = mShowImeSwitcher ? 1 : 0;
593            switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
594            binders.add(mImeToken);
595        }
596    }
597
598    /**
599     * @param clearNotificationEffects whether to consider notifications as "shown" and stop
600     *     LED, vibration, and ringing
601     */
602    @Override
603    public void onPanelRevealed(boolean clearNotificationEffects, int numItems) {
604        enforceStatusBarService();
605        long identity = Binder.clearCallingIdentity();
606        try {
607            mNotificationDelegate.onPanelRevealed(clearNotificationEffects, numItems);
608        } finally {
609            Binder.restoreCallingIdentity(identity);
610        }
611    }
612
613    @Override
614    public void clearNotificationEffects() throws RemoteException {
615        enforceStatusBarService();
616        long identity = Binder.clearCallingIdentity();
617        try {
618            mNotificationDelegate.clearEffects();
619        } finally {
620            Binder.restoreCallingIdentity(identity);
621        }
622    }
623
624    @Override
625    public void onPanelHidden() throws RemoteException {
626        enforceStatusBarService();
627        long identity = Binder.clearCallingIdentity();
628        try {
629            mNotificationDelegate.onPanelHidden();
630        } finally {
631            Binder.restoreCallingIdentity(identity);
632        }
633    }
634
635    @Override
636    public void onNotificationClick(String key) {
637        enforceStatusBarService();
638        final int callingUid = Binder.getCallingUid();
639        final int callingPid = Binder.getCallingPid();
640        long identity = Binder.clearCallingIdentity();
641        try {
642            mNotificationDelegate.onNotificationClick(callingUid, callingPid, key);
643        } finally {
644            Binder.restoreCallingIdentity(identity);
645        }
646    }
647
648    @Override
649    public void onNotificationActionClick(String key, int actionIndex) {
650        enforceStatusBarService();
651        final int callingUid = Binder.getCallingUid();
652        final int callingPid = Binder.getCallingPid();
653        long identity = Binder.clearCallingIdentity();
654        try {
655            mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key,
656                    actionIndex);
657        } finally {
658            Binder.restoreCallingIdentity(identity);
659        }
660    }
661
662    @Override
663    public void onNotificationError(String pkg, String tag, int id,
664            int uid, int initialPid, String message, int userId) {
665        enforceStatusBarService();
666        final int callingUid = Binder.getCallingUid();
667        final int callingPid = Binder.getCallingPid();
668        long identity = Binder.clearCallingIdentity();
669        try {
670            // WARNING: this will call back into us to do the remove.  Don't hold any locks.
671            mNotificationDelegate.onNotificationError(callingUid, callingPid,
672                    pkg, tag, id, uid, initialPid, message, userId);
673        } finally {
674            Binder.restoreCallingIdentity(identity);
675        }
676    }
677
678    @Override
679    public void onNotificationClear(String pkg, String tag, int id, int userId) {
680        enforceStatusBarService();
681        final int callingUid = Binder.getCallingUid();
682        final int callingPid = Binder.getCallingPid();
683        long identity = Binder.clearCallingIdentity();
684        try {
685            mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId);
686        } finally {
687            Binder.restoreCallingIdentity(identity);
688        }
689    }
690
691    @Override
692    public void onNotificationVisibilityChanged(
693            NotificationVisibility[] newlyVisibleKeys, NotificationVisibility[] noLongerVisibleKeys)
694            throws RemoteException {
695        enforceStatusBarService();
696        long identity = Binder.clearCallingIdentity();
697        try {
698            mNotificationDelegate.onNotificationVisibilityChanged(
699                    newlyVisibleKeys, noLongerVisibleKeys);
700        } finally {
701            Binder.restoreCallingIdentity(identity);
702        }
703    }
704
705    @Override
706    public void onNotificationExpansionChanged(String key, boolean userAction,
707            boolean expanded) throws RemoteException {
708        enforceStatusBarService();
709        long identity = Binder.clearCallingIdentity();
710        try {
711            mNotificationDelegate.onNotificationExpansionChanged(
712                    key, userAction, expanded);
713        } finally {
714            Binder.restoreCallingIdentity(identity);
715        }
716    }
717
718    @Override
719    public void onClearAllNotifications(int userId) {
720        enforceStatusBarService();
721        final int callingUid = Binder.getCallingUid();
722        final int callingPid = Binder.getCallingPid();
723        long identity = Binder.clearCallingIdentity();
724        try {
725            mNotificationDelegate.onClearAll(callingUid, callingPid, userId);
726        } finally {
727            Binder.restoreCallingIdentity(identity);
728        }
729    }
730
731    // ================================================================================
732    // Can be called from any thread
733    // ================================================================================
734
735    // lock on mDisableRecords
736    void manageDisableListLocked(int userId, int what, IBinder token, String pkg, int which) {
737        if (SPEW) {
738            Slog.d(TAG, "manageDisableList userId=" + userId
739                    + " what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
740        }
741        // update the list
742        final int N = mDisableRecords.size();
743        DisableRecord tok = null;
744        int i;
745        for (i=0; i<N; i++) {
746            DisableRecord t = mDisableRecords.get(i);
747            if (t.token == token && t.userId == userId) {
748                tok = t;
749                break;
750            }
751        }
752        if (what == 0 || !token.isBinderAlive()) {
753            if (tok != null) {
754                mDisableRecords.remove(i);
755                tok.token.unlinkToDeath(tok, 0);
756            }
757        } else {
758            if (tok == null) {
759                tok = new DisableRecord();
760                tok.userId = userId;
761                try {
762                    token.linkToDeath(tok, 0);
763                }
764                catch (RemoteException ex) {
765                    return; // give up
766                }
767                mDisableRecords.add(tok);
768            }
769            if (which == 1) {
770                tok.what1 = what;
771            } else {
772                tok.what2 = what;
773            }
774            tok.token = token;
775            tok.pkg = pkg;
776        }
777    }
778
779    // lock on mDisableRecords
780    int gatherDisableActionsLocked(int userId, int which) {
781        final int N = mDisableRecords.size();
782        // gather the new net flags
783        int net = 0;
784        for (int i=0; i<N; i++) {
785            final DisableRecord rec = mDisableRecords.get(i);
786            if (rec.userId == userId) {
787                net |= (which == 1) ? rec.what1 : rec.what2;
788            }
789        }
790        return net;
791    }
792
793    // ================================================================================
794    // Always called from UI thread
795    // ================================================================================
796
797    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
798        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
799                != PackageManager.PERMISSION_GRANTED) {
800            pw.println("Permission Denial: can't dump StatusBar from from pid="
801                    + Binder.getCallingPid()
802                    + ", uid=" + Binder.getCallingUid());
803            return;
804        }
805
806        synchronized (mLock) {
807            pw.println("  mDisabled1=0x" + Integer.toHexString(mDisabled1));
808            pw.println("  mDisabled2=0x" + Integer.toHexString(mDisabled2));
809            final int N = mDisableRecords.size();
810            pw.println("  mDisableRecords.size=" + N);
811            for (int i=0; i<N; i++) {
812                DisableRecord tok = mDisableRecords.get(i);
813                pw.println("    [" + i + "] userId=" + tok.userId
814                                + " what1=0x" + Integer.toHexString(tok.what1)
815                                + " what2=0x" + Integer.toHexString(tok.what2)
816                                + " pkg=" + tok.pkg
817                                + " token=" + tok.token);
818            }
819        }
820    }
821}
822