平臺
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);
}
}
}