frameworks/base/package/Keyguard主要为系统中锁屏模块的代码

frameworks/base/package/SystemUI这个代表的是系统UI,状态栏,通知中心显示,最近任务列表,锁屏的都在这里面控制,只看锁屏模块的话,keyguard相当于处理显示view,而SystemUI是属于对keyguard进行管理的,控制其显示逻辑的。

从SystemUI目录下的Android.mk也可以看出来这两个模块的关系。
LOCAL_STATIC_JAVA_LIBRARIES := \KeyguardLOCAL_RESOURCE_DIR := \frameworks/base/packages/Keyguard/res \SystemUI编译需要是要依赖Keyguard模块的。
修改Keyguard模块的代码后编译此模块不会产生相应的apk而是要在继续编译SystemUI模块的代码产生的SystemUI.apk放到手机里才会起作用,也就是说锁屏不会产生一个单独的apk文件,而是由SystemUI.apk包含了现有手机的锁屏模块。


锁屏分为两部分:

  • 锁屏界面称为Keyguard(常见为锁屏界面时钟,通知界面)
  • 解锁界面称为Bouncer (常见为滑开锁屏界面,显示密码,图案等解锁方式界面)

android 锁屏状态栏 锁屏显示状态栏_锁屏

 

上图为keyguard界面的时序调用关系,这只是展示了锁屏界面,还有锁屏界面后面的解锁界面还需要加载和展示
StatusBarKeyguardViewManager.java中的showBouncerOrKeyguard()方法中,从上图看,调用此方法加载锁屏界面:

protected void showBouncerOrKeyguard() {
        if (mBouncer.needsFullscreenBouncer()) {

            // The keyguard might be showing (already). So we need to hide it.
            mPhoneStatusBar.hideKeyguard();
            mBouncer.show(true /* resetSecuritySelection */);
        } else {
            mPhoneStatusBar.showKeyguard();
            mBouncer.hide(false /* destroyView */);
            mBouncer.prepare();
        }
    }

此方法中就是对于显示Bouncer还是Keyguard的区分,先执行了KeyguardBouncer.java中的needsFullscreenBouncer()方法:

public boolean needsFullscreenBouncer() {
        ensureView();
        if (mKeyguardView != null) {
            SecurityMode mode = mKeyguardView.getSecurityMode();
            return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
        }
        return false;
    }

从此方法可以看出当解锁方式为SimPinSimPuk时才会返回为true,对应着sim卡的两种状态,这个时候要显示一个全屏的界面,必须要输入正确相应的密码才能进入,否则不能使用手机。

4.KeyguardSystemUI的交互

既然Keyguard是作为SystemUI的library存在的,那么在SystemUI中调用Keyguard中的类,就很方便了,只需要直接导入类即可,但是在Keyguard中如何和SystemUI通信呢?主要关注三个类:

  • KeyguardViewMediator.java此类是在SystemUI中做统一调度的,也就是像我们长熟悉的熄屏,亮屏,锁屏等的处理都是在这里面的做的,它是一个对Keyguard的调度者。
  • KeyguardUpdateMonitor.java,从此类的说明中就可以看的出来,它是来处理锁屏更新操作的类,KeyguardViewMediator.java等对于锁屏更新的额相关处理都是在这里面进行的操作。
  • KeyguardUpdateMonitorCallback.java,作为更新之后的回调类,当我们跟新一些状态之后,还需要更新之后反馈一些状态时就会需要用到此类,就像我们进行网络请求,不能就发送网络请求后就不管了,需要反馈结果是连接成功了,还是失败了,需要有明确的信息帮助我们进行下一步操作。

这样我们就清楚了整个SystemUIKeyguard的流程:

android 锁屏状态栏 锁屏显示状态栏_锁屏_02

 

这样我们就知道了整体锁屏是如何通信交流的,关键的两个类就是KeyguardUpdateMonitor.java,
KeyguardUpdateMonitorCallback.java,再明确一下这两个类的作用:

  • KeyguardUpdateMonitor.java做锁屏状态变更等代码的处理
  • KeyguardUpdateMonitorCallback.javaKeyguardSystemUI中有需要回调的类,就需要先注册此Callback然后再有具体实现。

当我们需要新加处理代码时就知道了,在KeyguardUpdateMonitorCallback.java接口中声明,在KeyguardUpdateMonitor.java中调用生命函数,在所要更新的类中注册,当接收到回调借口函数是,执行方法即可。

5.如何通过回调函数通知多个类(借鉴源码额外分析理解)

我们都会使用回调函数,但是一般写法的回调函数只能是1对1的,也就是说A,和B类之间通过接口回调,但是如果a类需要发送消息,b类,c类等其他类都需要执行回调函数时,我们需要怎么做呢?查看此处源码,我们就会使用了这个方法,通过在b类,c类中注册相应的接口,这样在a类中遍历所注册的接口,然后即可调用b,c类中的方法实现,这样也就是a类实现状态变化,同时跟新了b,c类等。

示例代码:
我们定义了三个类分别为:
CallbackUpdateMonitor.java

public class CallbackUpdateMonitor {

    private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();

    private volatile static CallbackUpdateMonitor sInstance;

    public static CallbackUpdateMonitor getInstance() {
        if (sInstance == null) {
            synchronized (CallbackUpdateMonitor.class) {
                if (sInstance == null) {
                    sInstance = new CallbackUpdateMonitor();
                }
            }
        }
        return sInstance;
    }

    void udpateCurrentState() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            Callback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.updateOtherState();
            }
        }
    }

    void registerCallback(Callback callback) {
        for (int i = 0; i < mCallbacks.size(); i++) {
            if (mCallbacks.get(i).get() == callback) {
                Log.d("callbacktest", "already add callback = " + callback, new Exception("Called by"));
                return;
            }
        }
        mCallbacks.add(new WeakReference<Callback>(callback));
        removeCallback(null); // remove unused references
    }

    void removeCallback(Callback callback) {
        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
            if (mCallbacks.get(i).get() == callback) {
                mCallbacks.remove(i);
            }
        }
    }

    public interface Callback {
        void updateOtherState();
    }

}

CallbackTestB

public class CallbackTestB {

    CallbackUpdateMonitor.Callback callback = new CallbackUpdateMonitor.Callback() {
        @Override
        public void updateOtherState() {
            Log.d("callbacktest", "you update b");
        }
    };

    CallbackTestB() {
        CallbackUpdateMonitor.getInstance().registerCallback(callback);
    }

}

CallbackTestC

public class CallBackTestC {

    CallbackUpdateMonitor.Callback callback = new CallbackUpdateMonitor.Callback() {
        @Override
        public void updateOtherState() {
            Log.d("callbacktest", "you update c");
        }
    };

    CallBackTestC() {
        CallbackUpdateMonitor.getInstance().registerCallback(callback);
    }
}

然后在一个Activity中调用更新方法即可

public class MainActivity extends AppCompatActivity {

    Button mButton;
    CallbackUpdateMonitor callbackTestA;
    CallbackTestB callbackTestB;
    CallBackTestC callBackTestC;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        callbackTestA = CallbackUpdateMonitor.getInstance();
        callbackTestB = new CallbackTestB();
        callBackTestC = new CallBackTestC();

        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                callbackTestA.udpateCurrentState();
            }
        });
    }
}

android 锁屏状态栏 锁屏显示状态栏_android 锁屏状态栏_03

log截图.png

从上述log截图可以看出来,当我们点击了button时,即可遍历代码更新所注册两个类的方法。
我们将接口和管理分发的中间代码都放到CallbackUpdateMonitor.java类中进行,当我们需要在添加其他类的时候,只需要通过注册之后通过CallbackUpdateMonitor来就可以进行通信了。

6.接下来我们再看一下锁屏方式和设置的关联

当我们在设置中选择不同的锁屏方式,为什么就可以设置不同的解锁密码,这个是由什么控制的?
这里只列出关键代码:
Settings应用包中的ChooseLockGeneric.java中的

private boolean setUnlockMethod(String unlockMethod) {
            EventLog.writeEvent(EventLogTags.LOCK_SCREEN_TYPE, unlockMethod);
            if (KEY_UNLOCK_SET_OFF.equals(unlockMethod)) {
                updateUnlockMethodAndFinish(
                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true /* disabled */ );
            } else if (KEY_UNLOCK_SET_NONE.equals(unlockMethod)) {
                updateUnlockMethodAndFinish(
                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, false /* disabled */ );
            } else if (KEY_UNLOCK_SET_MANAGED.equals(unlockMethod)) {
                maybeEnableEncryption(DevicePolicyManager.PASSWORD_QUALITY_MANAGED, false);
            } else if (KEY_UNLOCK_SET_PATTERN.equals(unlockMethod)) {
                maybeEnableEncryption(
                        DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false);
            } else if (KEY_UNLOCK_SET_PIN.equals(unlockMethod)) {
                maybeEnableEncryption(
                        DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false);
            } else if (KEY_UNLOCK_SET_PASSWORD.equals(unlockMethod)) {
                maybeEnableEncryption(
                        DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, false);
            } else {
                Log.e(TAG, "Encountered unknown unlock method to set: " + unlockMethod);
                return false;
            }
            return true;
        }

一个关键类为LockPatternUtils.java它是用来设置和锁屏交互的类,设置,更新解锁方式和加密等操作都是通过此类来完成的。我们看到它设置锁屏方式就是设置了不同的DevicePolicyManager,这样的话我们就需要看在锁屏中的代码,根据搜关键字DevicePolicyManager我们就可以发现使如何设置当前的锁屏方式的。
keyguard包中的代码KeyguardSecurityModel.java

public enum SecurityMode {
        Invalid, // NULL state
        None, // No security enabled
        Pattern, // Unlock by drawing a pattern.
        Password, // Unlock by entering an alphanumeric password
        PIN, // Strictly numeric password
        SimPin, // Unlock by entering a sim pin.
        SimPuk // Unlock by entering a sim puk
    }
SecurityMode getSecurityMode() {
        KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);

        if (mIsPukScreenAvailable && SubscriptionManager.isValidSubscriptionId(
                monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED))) {
            return SecurityMode.SimPuk;
        }

        if (SubscriptionManager.isValidSubscriptionId(
                monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED))) {
            return SecurityMode.SimPin;
        }

        final int security = mLockPatternUtils.getActivePasswordQuality(
                KeyguardUpdateMonitor.getCurrentUser());
        switch (security) {
            case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
            case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
                return SecurityMode.PIN;

            case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
            case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
            case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
            case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
                return SecurityMode.Password;

            case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
                return SecurityMode.Pattern;
            case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
                return SecurityMode.None;

            default:
                throw new IllegalStateException("Unknown security quality:" + security);
        }
    }

从而可以看出设置和锁屏中通过LockPatternUtils设置和保存不同的密码等来区分当前的解锁方式,然后再看到它的调用出的代码KeyguardSecurityContainer类,此类是屏幕 解锁界面的父view,同过此view跟新当前解锁方式view:

private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
        KeyguardSecurityView view = null;
        final int children = mSecurityViewFlipper.getChildCount();
        for (int child = 0; child < children; child++) {
            if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
                view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
                break;
            }
        }
        int layoutId = getLayoutIdFor(securityMode);
        if (view == null && layoutId != 0) {
            final LayoutInflater inflater = LayoutInflater.from(mContext);
            if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
            View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);
            mSecurityViewFlipper.addView(v);
            updateSecurityView(v);
            view = (KeyguardSecurityView)v;
        }

        return view;
    }
protected int getLayoutIdFor(SecurityMode securityMode) {
        switch (securityMode) {
            case Pattern: return R.layout.keyguard_pattern_view;
            case PIN: return R.layout.keyguard_pin_view;
            case Password: return R.layout.keyguard_password_view;
            case SimPin: return R.layout.keyguard_sim_pin_view;
            case SimPuk: return R.layout.keyguard_sim_puk_view;
            default:
                return 0;
        }
    }

这样就可以看到不同的解锁方式对应者不同的自定义布局例如:
密码布局为:KeyguardPasswordView.java Pin码为:KeyguardPINView.java 图案为:KeyguardPatternView 等等.
这样能够我们就从SystemUI和Keyguard来分析了锁屏的逻辑和相关操作,主体就是这样。

 


cd lineage-16.0ForMi5sPlus/frameworks/base/packages/SystemUI/res/values-zh-rCN
#修改中文