frameworks/base/package/Keyguard
主要为系统中锁屏模块的代码
frameworks/base/package/SystemUI
这个代表的是系统UI,状态栏,通知中心显示,最近任务列表,锁屏的都在这里面控制,只看锁屏模块的话,keyguard
相当于处理显示view,而SystemUI
是属于对keyguard
进行管理的,控制其显示逻辑的。
从SystemUI目录下的Android.mk
也可以看出来这两个模块的关系。LOCAL_STATIC_JAVA_LIBRARIES := \Keyguard
,LOCAL_RESOURCE_DIR := \frameworks/base/packages/Keyguard/res \
SystemUI
编译需要是要依赖Keyguard
模块的。
修改Keyguard
模块的代码后编译此模块不会产生相应的apk而是要在继续编译SystemUI
模块的代码产生的SystemUI.apk
放到手机里才会起作用,也就是说锁屏不会产生一个单独的apk
文件,而是由SystemUI.apk
包含了现有手机的锁屏模块。
锁屏分为两部分:
- 锁屏界面称为
Keyguard
(常见为锁屏界面时钟,通知界面) - 解锁界面称为
Bouncer
(常见为滑开锁屏界面,显示密码,图案等解锁方式界面)
上图为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;
}
从此方法可以看出当解锁方式为SimPin
和SimPuk
时才会返回为true
,对应着sim
卡的两种状态,这个时候要显示一个全屏的界面,必须要输入正确相应的密码才能进入,否则不能使用手机。
4.Keyguard
与SystemUI
的交互
既然Keyguard
是作为SystemUI
的library存在的,那么在SystemUI
中调用Keyguard
中的类,就很方便了,只需要直接导入类即可,但是在Keyguard
中如何和SystemUI
通信呢?主要关注三个类:
-
KeyguardViewMediator.java
此类是在SystemUI
中做统一调度的,也就是像我们长熟悉的熄屏,亮屏,锁屏等的处理都是在这里面的做的,它是一个对Keyguard
的调度者。 -
KeyguardUpdateMonitor.java
,从此类的说明中就可以看的出来,它是来处理锁屏更新操作的类,KeyguardViewMediator.java
等对于锁屏更新的额相关处理都是在这里面进行的操作。 -
KeyguardUpdateMonitorCallback.java
,作为更新之后的回调类,当我们跟新一些状态之后,还需要更新之后反馈一些状态时就会需要用到此类,就像我们进行网络请求,不能就发送网络请求后就不管了,需要反馈结果是连接成功了,还是失败了,需要有明确的信息帮助我们进行下一步操作。
这样我们就清楚了整个SystemUI
和Keyguard
的流程:
这样我们就知道了整体锁屏是如何通信交流的,关键的两个类就是KeyguardUpdateMonitor.java
,KeyguardUpdateMonitorCallback.java
,再明确一下这两个类的作用:
-
KeyguardUpdateMonitor.java
做锁屏状态变更等代码的处理 -
KeyguardUpdateMonitorCallback.java
当Keyguard
和SystemUI
中有需要回调的类,就需要先注册此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();
}
});
}
}
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
#修改中文