平台
RK3288 + Android 7.1
需求
支持人脸解锁
方案说明
使用Smart Lock 中的 "可信面孔" 功能实现人脸解锁
实现步骤
- 内置GMS(若PUSH进去, 请重置)
GMS包内容如下:
drwxrwxrwx 2 root root 4096 2013-01-21 17:32 FaceLock
drwxrwxrwx 2 root root 4096 2013-01-21 17:32 GoogleBackupTransport
drwxrwxrwx 2 root root 4096 2013-01-21 17:32 GoogleContactsSyncAdapter
drwxrwxrwx 3 root root 4096 2013-01-21 17:32 GoogleGmsCore
drwxrwxrwx 2 root root 4096 2013-01-21 17:32 GoogleLoginService
drwxrwxrwx 2 root root 4096 2013-01-21 17:33 GooglePartnerSetup
drwxrwxrwx 3 root root 4096 2013-01-21 17:32 GooglePlay
drwxrwxrwx 2 root root 4096 2013-01-21 17:33 GoogleServicesFramework
rk3288:/system/priv-app # dumpsys package com.android.facelock
Permissions:
Permission [com.google.android.gms.auth.permission.FACE_UNLOCK] (e0424d2):
sourcePackage=com.android.facelock
uid=10024 gids=null type=0 prot=signature
perm=Permission{767d9a3 com.google.android.gms.auth.permission.FACE_UNLOCK}
packageSetting=PackageSetting{29984a0 com.android.facelock/10024}
Key Set Manager:
[com.android.facelock]
Signing KeySets: 6
Packages:
Package [com.android.facelock] (29984a0):
userId=10024
pkg=Package{5866859 com.android.facelock}
codePath=/system/priv-app/FaceLock
resourcePath=/system/priv-app/FaceLock
legacyNativeLibraryDir=/system/priv-app/FaceLock/lib
primaryCpuAbi=null
secondaryCpuAbi=null
versionCode=25 minSdk=15 targetSdk=25
versionName=7.1.2
splits=[base]
apkSigningVersion=2
applicationInfo=ApplicationInfo{bdbbc1e com.android.facelock}
flags=[ SYSTEM HAS_CODE ALLOW_CLEAR_USER_DATA ]
privateFlags=[ PRIVILEGED RESIZEABLE_ACTIVITIES ]
dataDir=/data/user/0/com.android.facelock
supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity]
timeStamp=2019-10-15 10:09:32
firstInstallTime=2019-10-15 10:09:32
lastUpdateTime=2019-10-15 10:09:32
signatures=PackageSignatures{d146cff [e3ca78d8]}
installPermissionsFixed=false installStatus=1
pkgFlags=[ SYSTEM HAS_CODE ALLOW_CLEAR_USER_DATA ]
declared permissions:
com.google.android.gms.auth.permission.FACE_UNLOCK: prot=signature, INSTALLED
requested permissions:
android.permission.CAMERA
com.google.android.gms.auth.permission.FACE_UNLOCK
install permissions:
com.google.android.gms.auth.permission.FACE_UNLOCK: granted=true
User 0: ceDataInode=513 installed=true hidden=false suspended=false stopped=false notLaunched=false enabled=0
runtime permissions:
Dexopt state:
[com.android.facelock]
Instruction Set: arm
path: /system/priv-app/FaceLock/FaceLock.apk
status: /data/dalvik-cache/arm/system@priv-app@[email protected]@classes.dex [compilation_filter=interpret-onl
y, status=kOatUpToDate]
Compiler stats:
[com.android.facelock]
FaceLock.apk - 252
- 连接VPN
内置的GMS包版本较旧, 需要联接GOOGLE更新和支持库等内容.
- 设置锁屏方式
设置>安全>屏幕锁定
要使用smart lock, 需设置屏幕锁定方式为图案/PIN码/密码
- 打开 可信面孔
设置>安全>Smart Lock > 可信面孔, 按操作即可
杂项
一些问题
- 在GMS更新的过程中, Smart Lock中的选项可能出现的个数在0-3之间, 不稳定
- Smart Lock的人脸解锁并不会直接跳过锁屏, 在实际使用过程中, 用户使用电源唤醒设备, 设备开始识别人脸, 识别成功后, 屏幕下方会有解锁动画, 表示已成功解锁, 但 不会 直接进入系统, 需要用户执行上滑动画以完成解锁动作.
- 在Android Q(10)开始, GOOGLE出于安全原因, 删除了可信面孔
锁屏相关代码
锁屏程序接收面部识别广播
|--frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
private static final String ACTION_FACE_UNLOCK_STARTED
= "com.android.facelock.FACE_UNLOCK_STARTED";
private static final String ACTION_FACE_UNLOCK_STOPPED
= "com.android.facelock.FACE_UNLOCK_STOPPED";
private final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(action)) {
mHandler.sendEmptyMessage(MSG_TIME_UPDATE);
} else if (Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_INFO_CHANGED,
intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()), 0));
} else if (ACTION_FACE_UNLOCK_STARTED.equals(action)) {
Trace.beginSection("KeyguardUpdateMonitor.mBroadcastAllReceiver#onReceive ACTION_FACE_UNLOCK_STARTED");
mHandler.sendMessage(mHandler.obtainMessage(MSG_FACE_UNLOCK_STATE_CHANGED, 1,
getSendingUserId()));
Trace.endSection();
} else if (ACTION_FACE_UNLOCK_STOPPED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_FACE_UNLOCK_STATE_CHANGED, 0,
getSendingUserId()));
} else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
.equals(action)) {
mHandler.sendEmptyMessage(MSG_DPM_STATE_CHANGED);
} else if (ACTION_USER_UNLOCKED.equals(action)) {
mHandler.sendEmptyMessage(MSG_USER_UNLOCKED);
}
}
};
case MSG_FACE_UNLOCK_STATE_CHANGED:
Trace.beginSection("KeyguardUpdateMonitor#handler MSG_FACE_UNLOCK_STATE_CHANGED");
handleFaceUnlockStateChanged(msg.arg1 != 0, msg.arg2);
Trace.endSection();
break;
private void handleFaceUnlockStateChanged(boolean running, int userId) {
mUserFaceUnlockRunning.put(userId, running);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onFaceUnlockStateChanged(running, userId);
}
}
}
|--frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
public void onFaceUnlockStateChanged(boolean running, int userId) { }
|--frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onFaceUnlockStateChanged(boolean running, int userId) {
update(false );
}
}
private void update(boolean updateAlways) {
Trace.beginSection("UnlockMethodCache#update");
int user = KeyguardUpdateMonitor.getCurrentUser();
boolean secure = mLockPatternUtils.isSecure(user);
boolean canSkipBouncer = !secure || mKeyguardUpdateMonitor.getUserCanSkipBouncer(user);
boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user);
boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user);
boolean faceUnlockRunning = mKeyguardUpdateMonitor.isFaceUnlockRunning(user)
&& trustManaged;
boolean changed = secure != mSecure || canSkipBouncer != mCanSkipBouncer ||
trustManaged != mTrustManaged || faceUnlockRunning != mFaceUnlockRunning;
if (changed || updateAlways) {
mSecure = secure;
mCanSkipBouncer = canSkipBouncer;
mTrusted = trusted;
mTrustManaged = trustManaged;
mFaceUnlockRunning = faceUnlockRunning;
notifyListeners();
}
Trace.endSection();
}
private void notifyListeners() {
for (OnUnlockMethodChangedListener listener : mListeners) {
listener.onUnlockMethodStateChanged();
}
}
|--frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@Override
public void onUnlockMethodStateChanged() {
logStateToEventlog();
}
private void logStateToEventlog() {
boolean isShowing = mStatusBarKeyguardViewManager.isShowing();
boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded();
boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
boolean isSecure = mUnlockMethodCache.isMethodSecure();
boolean canSkipBouncer = mUnlockMethodCache.canSkipBouncer();
int stateFingerprint = getLoggingFingerprint(mState,
isShowing,
isOccluded,
isBouncerShowing,
isSecure,
canSkipBouncer);
if (stateFingerprint != mLastLoggedStateFingerprint) {
EventLogTags.writeSysuiStatusBarState(mState,
isShowing ? 1 : 0,
isOccluded ? 1 : 0,
isBouncerShowing ? 1 : 0,
isSecure ? 1 : 0,
canSkipBouncer ? 1 : 0);
mLastLoggedStateFingerprint = stateFingerprint;
}
}
|--frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
UnlockMethodCache.OnUnlockMethodChangedListener,
AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener {
@Override
public void onUnlockMethodStateChanged() {
mLockIcon.update();
updateCameraVisibility();
}
}
|--frameworks/base/services/core/java/com/android/server/trust/TrustManagerService.java
public void updateTrust(int userId, int flags) {
boolean managed = aggregateIsTrustManaged(userId);
dispatchOnTrustManagedChanged(managed, userId);
if (mStrongAuthTracker.isTrustAllowedForUser(userId)
&& isTrustUsuallyManagedInternal(userId) != managed) {
updateTrustUsuallyManaged(userId, managed);
}
boolean trusted = aggregateIsTrusted(userId);
boolean changed;
synchronized (mUserIsTrusted) {
changed = mUserIsTrusted.get(userId) != trusted;
mUserIsTrusted.put(userId, trusted);
}
dispatchOnTrustChanged(trusted, userId, flags);
if (changed) {
refreshDeviceLockedForUser(userId);
}
}
private void dispatchOnTrustChanged(boolean enabled, int userId, int flags) {
if (DEBUG) {
Log.i(TAG, "onTrustChanged(" + enabled + ", " + userId + ", 0x"
+ Integer.toHexString(flags) + ")");
}
if (!enabled) flags = 0;
for (int i = 0; i < mTrustListeners.size(); i++) {
try {
mTrustListeners.get(i).onTrustChanged(enabled, userId, flags);
} catch (DeadObjectException e) {
Slog.d(TAG, "Removing dead TrustListener.");
mTrustListeners.remove(i);
i--;
} catch (RemoteException e) {
Slog.e(TAG, "Exception while notifying TrustListener.", e);
}
}
}