Android支持Smart Lock 人臉解鎖

平臺

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@[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 > 可信面孔, 按操作即可

雜項

一些問題
  1. 在GMS更新的過程中, Smart Lock中的選項可能出現的個數在0-3之間, 不穩定
  2. Smart Lock的人臉解鎖並不會直接跳過鎖屏, 在實際使用過程中, 用戶使用電源喚醒設備, 設備開始識別人臉, 識別成功後, 屏幕下方會有解鎖動畫, 表示已成功解鎖, 但 不會 直接進入系統, 需要用戶執行上滑動畫以完成解鎖動作.
  3. 在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);
            }
        }
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章