代码改变世界

Keyguard分析

2017-06-20 12:37  cascle  阅读(2243)  评论(0编辑  收藏  举报

    从Android 6.0开始,位于frameworks/bases/packages/Keyguard的Keyguard开始被编译为一个jar包,被SystemUI静态导入,相当于SystemUI的一个界面,这样Keyguard就可以复用SystemUI里关于通知的那一部分代码,这个在Keyuard的Makefile里可以看到

 1 LOCAL_PATH:= $(call my-dir)
 2 17include $(CLEAR_VARS)
 3 18
 4 19LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-subdir-Iaidl-files)
 5 20
 6 21LOCAL_MODULE := Keyguard
 7 22
 8 23LOCAL_CERTIFICATE := platform
 9 24
10 25LOCAL_JAVA_LIBRARIES := SettingsLib
11 26
12 27LOCAL_PRIVILEGED_MODULE := true
13 28
14 29LOCAL_PROGUARD_FLAG_FILES := proguard.flags
15 30
16 31LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
17 32
18 33include $(BUILD_STATIC_JAVA_LIBRARY)
19 34
20 35#include $(call all-makefiles-under,$(LOCAL_PATH))

 

       Keyguard分为两个界面,不用输入密码的一级锁屏界面(SystemUI认为是PhoneStatusBar)与相应源码文件包含Security字样的二级锁屏界面(SystemUI认为是Bouncer)。

       

       一级锁屏界面

 

       

       二级锁屏界面

 

       各个部件调用关系是下边这张图

       

 

       可以看到,Keyguard第一个涉及到的是KeyguardDisplayManager,其由KeyguardViewMediator这个界面中介调用。

       首先,由SystemServer的startSystemUi方法里的StartServiceAsUser连接到SystemUIService。再由SystemUIService(SystemUI/src/com/android/systemui/SystemUIService.java)里的onCreate函数调用(就这一个有用的函数)startServicesIfNeeded方法,开始SystemUI的初始化

1     static final void startSystemUi(Context context) {
2         Intent intent = new Intent();
3         intent.setComponent(new ComponentName("com.android.systemui",
4                     "com.android.systemui.SystemUIService"));
5         intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
6         //Slog.d(TAG, "Starting service: " + intent);
7         context.startServiceAsUser(intent, UserHandle.SYSTEM);
8     }

 

1 public class SystemUIService extends Service {
2 
3     @Override
4     public void onCreate() {
5         super.onCreate();
6         ((SystemUIApplication) getApplication()).startServicesIfNeeded();
7     }

   

       KeyguardViewMediator是SystemUIApplication名为SERVICES数组里的一员,这个数组的东西都是SystemUI要load的Service的class,这个框架流程在start的时候,调用这些类的start方法,并在bootcompleted的时候调用这些类的onbootcompleted方法。

 1  /**
 2 44     * The classes of the stuff to start.
 3 45     */
 4 46    private final Class<?>[] SERVICES = new Class[] {
 5 47            com.android.systemui.tuner.TunerService.class,
 6 48            com.android.systemui.keyguard.KeyguardViewMediator.class,
 7 49            com.android.systemui.recents.Recents.class,
 8 50            com.android.systemui.volume.VolumeUI.class,
 9 51            Divider.class,
10 52            com.android.systemui.statusbar.SystemBars.class,
11 53            com.android.systemui.usb.StorageNotification.class,
12 54            com.android.systemui.power.PowerUI.class,
13 55            com.android.systemui.media.RingtonePlayer.class,
14 56            com.android.systemui.keyboard.KeyboardUI.class,
15 57            com.android.systemui.tv.pip.PipUI.class,
16 58            com.android.systemui.shortcut.ShortcutKeyDispatcher.class,
17 59            com.android.systemui.VendorServices.class
18 60    };

 

    可以看到,在SystemUIApplication这个类的startServicesIfNeeded里,会依次调用SERVICES里的start函数,这里会先调用com.android.systemui.keyguard.KeyguardViewMediator的start方法

 1 final int N = services.length;
 2 156        for (int i=0; i<N; i++) {
 3 157            Class<?> cl = services[i];
 4 158            if (DEBUG) Log.d(TAG, "loading: " + cl);
 5 159            try {
 6 160                Object newService = SystemUIFactory.getInstance().createInstance(cl);
 7 161                mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);
 8 162            } catch (IllegalAccessException ex) {
 9 163                throw new RuntimeException(ex);
10 164            } catch (InstantiationException ex) {
11 165                throw new RuntimeException(ex);
12 166            }
13 167
14 168            mServices[i].mContext = this;
15 169            mServices[i].mComponents = mComponents;
16 170            if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
17 171            mServices[i].start();
18 172
19 173            if (mBootCompleted) {
20 174                mServices[i].onBootCompleted();
21 175            }
22 176        }
23 177        mServicesStarted = true;

 

    

      下面看下KeyguardViewMediator及其start方法

       这个类位于SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java比较长。第三方客户端也可以通过调用KeyguardManager这个类来获取和修改锁屏的信息、状态,这个类是锁屏操作的binder server基础类。

       KeyguardViewMediator是抽象类SystemUI的一个具体实现子类,SystemUI这个类的主要方法是putComponent和getComponent,保存和获取相应类对应的实际组件的映射。还用mContext和mComponents保存相应的SystemUIApplication实例和其中名为component的hashmap。

       KyeguardViewMediator总体负责所有的锁屏状态,并根据状态来决定调用哪些组件。

       KeyguardViewMediator的start方法很简单,初始化锁屏状态,把KeyguardViewMediator的class和KeyguardViewMediator建立映射。

1  @Override
2 699    public void start() {
3 700        synchronized (this) {
4 701            setupLocked();
5 702        }
6 703        putComponent(KeyguardViewMediator.class, this);
7 704    }

        初始化的过程在setupLocked方法里完成,首先获取系统的PowerManagerService,WindowManagerService,TrustManagerService并初始化一把partial wakelock锁

1            mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
2 635        mWM = WindowManagerGlobal.getWindowManagerService();
3 636        mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
4 637
5 638        mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
6 639        mShowKeyguardWakeLock.setReferenceCounted(false);

          随后注册DELAYED_KEYGUARD_ACTION和DELAYED_LOCK_PROFILE_ACTION这两个Intent的broadcastreceiver

1 641        mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));
2 642        mContext.registerReceiver(
3 643                mBroadcastReceiver, new IntentFilter(DELAYED_LOCK_PROFILE_ACTION));

           然后创建Keyguard包里的KeyguardDisplayManager和KeyguardUpdateMonitor,还有锁屏模式工具类,获取AlarmManagerService,给KeyguardUpdateMonitor设置当前的用户。

1 645        mKeyguardDisplayManager = new KeyguardDisplayManager(mContext);
2 646
3 647        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
4 648
5 649        mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
6 650
7 651        mLockPatternUtils = new LockPatternUtils(mContext);
8 652        KeyguardUpdateMonitor.setCurrentUser(ActivityManager.getCurrentUser());

            然后设置锁屏状态的变量并调用锁屏状态改变回调函数表,通知TrustManager

1 654        // Assume keyguard is showing (unless it's disabled) until we know for sure...
2 655        setShowingLocked(!shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled(
3 656                KeyguardUpdateMonitor.getCurrentUser()));
4 657        updateInputRestrictedLocked();
5 658        mTrustManager.reportKeyguardShowingChanged();

            然后设置视图显示,通过SystemUIFactory获取StatusBarKeyguardViewManager,并把视图中介回调(mViewMediatorCallback)和锁屏模式工具(mLockPatternUtils)传入。

1 660        mStatusBarKeyguardViewManager =
2 661                SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext,
3 662                        mViewMediatorCallback, mLockPatternUtils);

            SystemUIFactory里的code如下

1 84    public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
2 85            ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
3 86        return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
4 87    }

            StatusBarKeyguardViewManager是SystemUI中的一个状态栏组件,是锁屏的视图。

 

            最后设置锁屏和解锁的声音的文件和音量,获取设备交互状态,加载锁屏隐藏的动画 com.android.internal.R.anim.lock_screen_behind_enter 。


  663        final ContentResolver cr = mContext.getContentResolver();
  664
  665        mDeviceInteractive = mPM.isInteractive();

1
667 mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0); 2 668 String soundPath = Settings.Global.getString(cr, Settings.Global.LOCK_SOUND); 3 669 if (soundPath != null) { 4 670 mLockSoundId = mLockSounds.load(soundPath, 1); 5 671 } 6 672 if (soundPath == null || mLockSoundId == 0) { 7 673 Log.w(TAG, "failed to load lock sound from " + soundPath); 8 674 } 9 675 soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND); 10 676 if (soundPath != null) { 11 677 mUnlockSoundId = mLockSounds.load(soundPath, 1); 12 678 } 13 679 if (soundPath == null || mUnlockSoundId == 0) { 14 680 Log.w(TAG, "failed to load unlock sound from " + soundPath); 15 681 } 16 682 soundPath = Settings.Global.getString(cr, Settings.Global.TRUSTED_SOUND); 17 683 if (soundPath != null) { 18 684 mTrustedSoundId = mLockSounds.load(soundPath, 1); 19 685 } 20 686 if (soundPath == null || mTrustedSoundId == 0) { 21 687 Log.w(TAG, "failed to load trusted sound from " + soundPath); 22 688 } 23 689 24 690 int lockSoundDefaultAttenuation = mContext.getResources().getInteger( 25 691 com.android.internal.R.integer.config_lockSoundVolumeDb); 26 692 mLockSoundVolume = (float)Math.pow(10, (float)lockSoundDefaultAttenuation/20); 27 693 28 694 mHideAnimation = AnimationUtils.loadAnimation(mContext, 29 695 com.android.internal.R.anim.lock_screen_behind_enter); 30 696 }

 

          然后SystemUIApplication会调用  mServices[i].onBootCompleted 方法,会在KeyguardViemMediator的start方法后调用,来发出Intent ACTION_BOOT_COMPLETED,通知其他组件锁屏初始化完成

 

          上边是锁屏的初始化过程,然后就是锁屏的加载过程。锁屏界面的加载有两个地方,第一个是第一次开机的时候;第二个是在灭屏后,这个时候会预加载锁屏界面加速亮屏显示。

           第一次开机时,,

           在按住Power键灭屏的时候,流程如下

           可以看到,KeyguardViewMediator里有两个回调函数被涉及

           第一个是onStartedGoingToSleep。这个方法里做锁屏的一些预处理,并发出锁屏通知给KeyguardUpdateMonitor(这里的状态太多了)

 1 723    /**
 2 724     * Called to let us know the screen was turned off.
 3 725     * @param why either {@link android.view.WindowManagerPolicy#OFF_BECAUSE_OF_USER} or
 4 726     *   {@link android.view.WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT}.
 5 727     */
 6 728    public void onStartedGoingToSleep(int why) {
 7 729        if (DEBUG) Log.d(TAG, "onStartedGoingToSleep(" + why + ")");
 8 730        synchronized (this) {
 9 731            mDeviceInteractive = false;
10 732            mGoingToSleep = true;
11 733
12 734            // Lock immediately based on setting if secure (user has a pin/pattern/password).
13 735            // This also "locks" the device when not secure to provide easy access to the
14 736            // camera while preventing unwanted input.
15 737            int currentUser = KeyguardUpdateMonitor.getCurrentUser();
16 738            final boolean lockImmediately =
17 739                    mLockPatternUtils.getPowerButtonInstantlyLocks(currentUser)
18 740                            || !mLockPatternUtils.isSecure(currentUser);
19 741            long timeout = getLockTimeout(KeyguardUpdateMonitor.getCurrentUser());
20 742            mLockLater = false;
21 743            if (mExitSecureCallback != null) {
22 744                if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled");
23 745                try {
24 746                    mExitSecureCallback.onKeyguardExitResult(false);
25 747                } catch (RemoteException e) {
26 748                    Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
27 749                }
28 750                mExitSecureCallback = null;
29 751                if (!mExternallyEnabled) {
30 752                    hideLocked();
31 753                }
32 754            } else if (mShowing) {
33 755                mPendingReset = true;
34 756            } else if ((why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT && timeout > 0)
35 757                    || (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately)) {
36 758                doKeyguardLaterLocked(timeout);
37 759                mLockLater = true;
38 760            } else if (!mLockPatternUtils.isLockScreenDisabled(currentUser)) {
39 761                mPendingLock = true;
40 762            }
41 763
42 764            if (mPendingLock) {
43 765                playSounds(true);
44 766            }
45 767        }
46 768        KeyguardUpdateMonitor.getInstance(mContext).dispatchStartedGoingToSleep(why);
47 769        notifyStartedGoingToSleep();
48 770    }

 

           第二个是onFinishedGoingToSleep,可以看到核心方法是doKeyguardLocked和doKeyguardForChildProfilesLocked

 1 772    public void onFinishedGoingToSleep(int why, boolean cameraGestureTriggered) {
 2 773        if (DEBUG) Log.d(TAG, "onFinishedGoingToSleep(" + why + ")");
 3 774        synchronized (this) {
 4 775            mDeviceInteractive = false;
 5 776            mGoingToSleep = false;
 6 777
 7 778            resetKeyguardDonePendingLocked();
 8 779            mHideAnimationRun = false;
 9 780
10 781            notifyFinishedGoingToSleep();
11 782
12 783            if (cameraGestureTriggered) {
13 784                Log.i(TAG, "Camera gesture was triggered, preventing Keyguard locking.");
14 785
15 786                // Just to make sure, make sure the device is awake.
16 787                mContext.getSystemService(PowerManager.class).wakeUp(SystemClock.uptimeMillis(),
17 788                        "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK");
18 789                mPendingLock = false;
19 790                mPendingReset = false;
20 791            }
21 792
22 793            if (mPendingReset) {
23 794                resetStateLocked();
24 795                mPendingReset = false;
25 796            }
26 797
27 798            if (mPendingLock) {
28 799                doKeyguardLocked(null);
29 800                mPendingLock = false;
30 801            }
31 802
32 803            // We do not have timeout and power button instant lock setting for profile lock.
33 804            // So we use the personal setting if there is any. But if there is no device
34 805            // we need to make sure we lock it immediately when the screen is off.
35 806            if (!mLockLater && !cameraGestureTriggered) {
36 807                doKeyguardForChildProfilesLocked();
37 808            }
38 809
39 810        }
40 811        KeyguardUpdateMonitor.getInstance(mContext).dispatchFinishedGoingToSleep(why);
41 812    }

         doKeyguardLocked 会先判断要不要锁屏,如果需要,则调用方法showLocked

 1 1192    /**
 2 1193     * Enable the keyguard if the settings are appropriate.
 3 1194     */
 4 1195    private void doKeyguardLocked(Bundle options) {
 5 1196        // if another app is disabling us, don't show
 6 1197        if (!mExternallyEnabled) {
 7 1198            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
 8 1199
 9 1200            // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
10 1201            // for an occasional ugly flicker in this situation:
11 1202            // 1) receive a call with the screen on (no keyguard) or make a call
12 1203            // 2) screen times out
13 1204            // 3) user hits key to turn screen back on
14 1205            // instead, we reenable the keyguard when we know the screen is off and the call
15 1206            // ends (see the broadcast receiver below)
16 1207            // TODO: clean this up when we have better support at the window manager level
17 1208            // for apps that wish to be on top of the keyguard
18 1209            return;
19 1210        }
20 1211
21 1212        // if the keyguard is already showing, don't bother
22 1213        if (mStatusBarKeyguardViewManager.isShowing()) {
23 1214            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
24 1215            resetStateLocked();
25 1216            return;
26 1217        }
27 1218
28 1219        // In split system user mode, we never unlock system user.
29 1220        if (!mustNotUnlockCurrentUser()
30 1221                || !mUpdateMonitor.isDeviceProvisioned()) {
31 1222
32 1223            // if the setup wizard hasn't run yet, don't show
33 1224            final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
34 1225            final boolean absent = SubscriptionManager.isValidSubscriptionId(
35 1226                    mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.ABSENT));
36 1227            final boolean disabled = SubscriptionManager.isValidSubscriptionId(
37 1228                    mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.PERM_DISABLED));
38 1229            final boolean lockedOrMissing = mUpdateMonitor.isSimPinSecure()
39 1230                    || ((absent || disabled) && requireSim);
40 1231
41 1232            if (!lockedOrMissing && shouldWaitForProvisioning()) {
42 1233                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
43 1234                        + " and the sim is not locked or missing");
44 1235                return;
45 1236            }
46 1237
47 1238            if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
48 1239                    && !lockedOrMissing) {
49 1240                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
50 1241                return;
51 1242            }
52 1243
53 1244            if (mLockPatternUtils.checkVoldPassword(KeyguardUpdateMonitor.getCurrentUser())) {
54 1245                if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
55 1246                // Without this, settings is not enabled until the lock screen first appears
56 1247                setShowingLocked(false);
57 1248                hideLocked();
58 1249                mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
59 1250                return;
60 1251            }
61 1252        }
62 1253
63 1254        if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
64 1255        showLocked(options);
65 1256    }

           showLocked方法会发送SHOW消息

 1 1332    /**
 2 1333     * Send message to keyguard telling it to show itself
 3 1334     * @see #handleShow
 4 1335     */
 5 1336    private void showLocked(Bundle options) {
 6 1337        Trace.beginSection("KeyguardViewMediator#showLocked aqcuiring mShowKeyguardWakeLock");
 7 1338        if (DEBUG) Log.d(TAG, "showLocked");
 8 1339        // ensure we stay awake until we are finished displaying the keyguard
 9 1340        mShowKeyguardWakeLock.acquire();
10 1341        Message msg = mHandler.obtainMessage(SHOW, options);
11 1342        mHandler.sendMessage(msg);
12 1343        Trace.endSection();
13 1344    }

           handleShow方法就会被调用,显示mStatusBarKeyguardViewManagermKeyguardDisplayManager的show方法。

 1 1625    /**
 2 1626     * Handle message sent by {@link #showLocked}.
 3 1627     * @see #SHOW
 4 1628     */
 5 1629    private void handleShow(Bundle options) {
 6 1630        Trace.beginSection("KeyguardViewMediator#handleShow");
 7 1631        final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
 8 1632        if (mLockPatternUtils.isSecure(currentUser)) {
 9 1633            mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
10 1634        }
11 1635        synchronized (KeyguardViewMediator.this) {
12 1636            if (!mSystemReady) {
13 1637                if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready.");
14 1638                return;
15 1639            } else {
16 1640                if (DEBUG) Log.d(TAG, "handleShow");
17 1641            }
18 1642
19 1643            setShowingLocked(true);
20 1644            mStatusBarKeyguardViewManager.show(options);
21 1645            mHiding = false;
22 1646            mWakeAndUnlocking = false;
23 1647            resetKeyguardDonePendingLocked();
24 1648            mHideAnimationRun = false;
25 1649            updateActivityLockScreenState();
26 1650            adjustStatusBarLocked();
27 1651            userActivity();
28 1652
29 1653            mShowKeyguardWakeLock.release();
30 1654        }
31 1655        mKeyguardDisplayManager.show();
32 1656        Trace.endSection();
33 1657    }

 

           

           还有一种情况是超时灭屏,与上边的按住Power键灭屏流程基本一样

 

 

 

 

 

 

基本流程分析完了,下面看看Keyguard里的具体每个类

先看KeyguardDisplayManager这个类,这个类是控制手机远程显示的。如果手机远程连接上了电视这样的设备,就先一个一个KeyguardPresentation对话框,是个时钟

 1     protected void updateDisplays(boolean showing) {
 2         if (showing) {
 3             MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
 4                     MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
 5             boolean useDisplay = route != null
 6                     && route.getPlaybackType() == MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
 7             Display presentationDisplay = useDisplay ? route.getPresentationDisplay() : null;
 8 
 9             if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
10                 if (DEBUG) Slog.v(TAG, "Display gone: " + mPresentation.getDisplay());
11                 mPresentation.dismiss();
12                 mPresentation = null;
13             }
14 
15             if (mPresentation == null && presentationDisplay != null) {
16                 if (DEBUG) Slog.i(TAG, "Keyguard enabled on display: " + presentationDisplay);
17                 mPresentation = new KeyguardPresentation(mContext, presentationDisplay,
18                         R.style.keyguard_presentation_theme);
19                 mPresentation.setOnDismissListener(mOnDismissListener);
20                 try {
21                     mPresentation.show();
22                 } catch (WindowManager.InvalidDisplayException ex) {
23                     Slog.w(TAG, "Invalid display:", ex);
24                     mPresentation = null;
25                 }
26             }
27         } else {
28             if (mPresentation != null) {
29                 mPresentation.dismiss();
30                 mPresentation = null;
31             }
32         }
33     }