平台
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
确认FaceLock是否已安装
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@FaceLock@FaceLock.apk@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); } } }; //mHandler 消息处理 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 //空函数 /** * Called when the state of face unlock changed. */ 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 /* updateAlways */); } } 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 // UnlockMethodCache.OnUnlockMethodChangedListener 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); } } }