Android4.0 Keyguard画面ロック解除機構

14389 ワード

Keyguardロック解除画面はAndroidシステムに不可欠なモジュールであり、ユーザーが電源を入れた後やPowerボタンをクリックして画面が明るくなった後に最初に見た画面がロック解除画面モジュールに対応するインタフェースである.Keyguardモジュールの機能は比較的簡単です.第一に、携帯電話の現在の重要な情報を直感的に表示します.例えば、バッテリー情報、キャリア情報、日付情報、通話メール情報などです.第二に、携帯電話のセキュリティ性能を強化する:セキュリティのために、ユーザーはSettingsのSecureオプションにpassword、pattern、accountなどの異なるセキュリティポリシーを設定し、不正なユーザーが携帯電話システムにアクセスすることを防止することができる.しかし、コード実装の観点から、このモジュールの論理は複雑であり、sim、電話、バッテリー、Carrierなどのシステム内の多くのeventを傍受するだけでなく、画面の異なる状態を正確に反映し、表示する必要がある.Keyguardモジュールの処理ロジックをより明確に理解するために,まずシステム構成の観点からKeyguardロック解除モジュールのフレームワークを要約し,次にロック解除モジュールにおける重要な処理ロジックを詳細に紹介する.一、システム紹介
Keyguardロック解除モジュールのフレームワーククラス図は以下の通りです.
図には、PhoneWindowManagerがロック解除モジュールの外部インタフェースであり、ウィンドウ管理サービス、電源管理サービスなどの外部モジュールがPhoneWindowManagerを介してKeyguard内部機能にアクセスする重要なクラスのみが示されています.KeyguardViewMediatorクラスは、ロック解除モジュールの仲介者であり、仲介者としてkeyguard状態の変化を処理し、event、power管理、PhoneWindowManager通知などの要求を処理し、ロック解除モジュールの他のクラスのコールバックの対象とする.KeyguardUpdateMonitorクラスは、時間、simカード、キャリア情報、バッテリー情報、電話情報などの状態の変化を傍受し、keyguard Viewモジュールに表示の更新を通知するロック解除モジュールの傍受者である.KeyguardViewManagerクラスは、ロック解除ビューモジュールの管理者であり、ロック解除画面の作成、表示、非表示、リセットなどを管理します.LockPatternKeyguardViewクラスは、ロック解除モジュールのViewインタフェースであり、すべてのロック解除インタフェースのhost viewである.設定したセキュリティポリシーに従って、異なるロック解除画面インタフェースが表示されます.Googleオリジナルコードでは6種類のロック解除画面を実現した:1)ロック解除状態表示用2)PatternUnlockScreen:パターンロック解除モード実現3)SimPukUnlockScreen:画面実装SIM PUKコードロック解除モード4)SimUnlockScreen:Sim PINコードロック解除モード実現5)AccountUnlockScreen:GOOGLEアカウント実現ロック解除6)PasswordUnlockScreen:カスタムパスワードロック解除モード2、主要ロジック1、Keyguardモジュール起動、表示ロジック、すなわち携帯電話の起動からロック画面画面表示までのプロセスを実現する.携帯電話システムの起動中に自動的にKeyguardロック解除モジュールが起動します.このモジュールの作成はWindowManagerServiceクラスから始まり、タイミング図は以下の通りです.
1)WindowManagerServiceは起動時にPhoneWindowManagerオブジェクトmPolicyをインスタンス化し、ウィンドウ管理PolicyスレッドPolicyThreadで初期化します.コードは以下の通りです.
         public void run() {
             Looper.prepare();
             WindowManagerPolicyThread.set(this, Looper.myLooper());
             ......
             mPolicy.init(mContext, mService, mService, mPM);
             ......
             Looper.loop();
         }
コードから、PhoneWindowManagerが独立したスレッドおよびLooperメッセージキューでMessageイベントを処理していることがわかります.このLooperオブジェクトは、すべてのhandlerメッセージを処理するためにスクリーンモジュールをロック解除するために使用されます.
2)mPolicy関数initでロック解除画面モジュールを作成する仲介者であるKeyguardViewMediatorオブジェクト.
3)KeyguardViewMediatorのコンストラクション関数で、L o c k T h t e r n KeyguardViewProperties、KeyguardUpdateMonitor、KeyguardViewManagerなどの重要なオブジェクトを作成します.
public KeyguardViewMediator(Context context, PhoneWindowManager callback,
            LocalPowerManager powerManager) {
        ……
        mUpdateMonitor = new KeyguardUpdateMonitor(context);
        mUpdateMonitor.registerInfoCallback(this);
        mUpdateMonitor.registerSimStateCallback(this);
        mLockPatternUtils = new LockPatternUtils(mContext);
        mKeyguardViewProperties
= new LockPatternKeyguardViewProperties(mLockPatternUtils, mUpdateMonitor);
        mKeyguardViewManager = new KeyguardViewManager(
                context, WindowManagerImpl.getDefault(), this,
                mKeyguardViewProperties, mUpdateMonitor);
       ……
 }

KeyguardViewMediatorにはPhoneWindowManager、PowerManagerなどのオブジェクトが記録されているとともに、Lock PatternKeyguardViewProperties、KeyguardUpdateMonitor、KeyguardViewManagerなどのモジュール内の重要なオブジェクトも保存されており、このクラスは仲介者としてKeyguardモジュールの対外インタラクションおよび内部の各オブジェクト間のインタラクションにおいて重要な役割を果たしている.
4)KeyguardUpdateMonitorコンストラクション関数でmHandlerを作成し、このクラスのリスニングを処理する各イベント状態の変更に応答してhandle処理関数でmInfoCallbackとmSimStateCallbackに保存されているリスニングオブジェクトをmInfoCallbackとmSimStateCallbackに通知し、リスニングイベントにACTION_があるTIME_TICK、ACTION_TIME_CHANGED、ACTION_BATTERY_CHANGED、ACTION_TIMEZONE_CHANGED、ACTION_SIM_STATE_CHANGED、        ACTION_PHONE_STATE_CHANGED、RINGER_MODE_CHANGED_ACTION
これでKeyguardロック解除モジュールの重要なクラスオブジェクトはインスタンス化されましたが、ロック解除画面Viewインタフェースの作成と表示には関与していません.
5)システム起動後に画面をロック解除するインターフェースの最初の表示はWindowManagerServiceのシステムReady関数から始まり、PhoneWindowManagerシステムの準備完了を通知する.コードは以下の通りである.
public void systemReady() {
    mPolicy.systemReady();
}

6)PhoneWindowManagerのsystemReady関数において、ロック解除モジュールの仲介者KeyguardViewMediatorオブジェクトシステム準備完了を通知する7)仲介者KeyguardViewMediatorクラスにおける処理システム準備状況:doKeyguardLocked関数を呼び出してロック解除画面インタフェースを表示する:
public void onSystemReady() {
    synchronized (this) {
         mSystemReady = true;
         doKeyguardLocked();
    }
}
private void doKeyguardLocked() {
        ......
        // if the keyguard is already showing, don't bother
        if (mKeyguardViewManager.isShowing()) {
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
            return;
        }

        .....
        if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
        showLocked();
}
showLocked関数でSHOWメッセージを送信非同期でロック画面の表示を理解する要求.
8)handleShowではインタフェースに表示されるメッセージ要求を処理し、関数ではKeyguardViewManagerの関数showを呼び出して画面をロック解除するインタフェースの真の表示を実現する.
public synchronized void show() {
        ......
        if (mKeyguardHost == null) {
            if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");

            mKeyguardHost = new KeyguardViewHost(mContext, mCallback);
            ......
            mViewManager.addView(mKeyguardHost, lp);
        }
        ......
        mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);

        if (mKeyguardView == null) {
            mKeyguardView = mKeyguardViewProperties.createKeyguardView(mContext, mUpdateMonitor, this);
            ......
            mKeyguardHost.addView(mKeyguardView, lp);
            ......
        }
        ......
}
    
この関数では、主にFrameLayoutに継承され、スクリーンビューをロック解除するルートビューであるmKeyguardHostとmKeyguardViewの2つの重要なオブジェクトを表示するKeyguardが作成されます.9)オブジェクトmKeyguardViewの作成時に、ロック解除画面modeに従ってロック解除画面を作成する:
protected void updateScreen(Mode mode, boolean force) {
         ......
        // Re-create the lock screen if necessary
        if (mode == Mode.LockScreen || mShowLockBeforeUnlock) {
            if (force || mLockScreen == null) {
                recreateLockScreen();
            }
        }

        // Re-create the unlock screen if necessary. This is primarily required to properly handle
        // SIM state changes. This typically happens when this method is called by reset()
        if (mode == Mode.UnlockScreen) {
            final UnlockMode unlockMode = getUnlockMode();
            if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) {
                recreateUnlockScreen(unlockMode);
            }
        }
        ......
    }
   
10)関数createLockScreenまたはcreateUnlockScreenForに特定のLockまたはUnlock Viewインタフェースを作成し、show関数を呼び出して表示します.これで、Keyguardロック解除モジュールのシステム起動からインタフェース表示までの処理ロジックが完了しました.
2、Powerボタンを2回押すと画面が明るくなる→暗くなる→明るくなる過程でロックモジュールの処理ロジックが2回連続してPowerボタンを押すと画面が明るくなる→暗くなる→明るくなる過程でロックモジュールの処理ロジックを解除するタイミング図は以下の通りである.
1)関数PowerManagerServices:setPowerStateでPowerボタンの押下に応答し、コードは以下の通りである.
private void setPowerState(int newState, boolean noChangeLights, int reason)
    {
        synchronized (mLocks) {
            ……
            if (oldScreenOn != newScreenOn) {
                if (newScreenOn) {
                    // When the user presses the power button, we need to always send out the
                    // notification that it's going to sleep so the keyguard goes on.  But
                    // we can't do that until the screen fades out, so we don't show the keyguard
                    // too early.
                    if (mStillNeedSleepNotification) {
                        sendNotificationLocked(false, WindowManagerPolicy.OFF_BECAUSE_OF_USER);
                    }
                    ……
                    if (err == 0) {
                        sendNotificationLocked(true, -1);
                        // Update the lights *after* taking care of turning the
                        // screen on, so we do this after our notifications are
                        // enqueued and thus will delay turning on the screen light
                        // until the windows are correctly displayed.
                        if (stateChanged) {
                            updateLightsLocked(newState, 0);
                        }
                        mPowerState |= SCREEN_ON_BIT;
                    }

                } else {
                    ……
                    if (!mScreenBrightness.animating) {
                        err = screenOffFinishedAnimatingLocked(reason);
                    }
                    ……
                }
            }
            ……
        }
}    

上のコードロジックによると、画面が暗くなると関数screenOffFinishedAnimatingLockedが呼び出され、画面が明るくなると関数sendNotificationLockedが呼び出されます.
.
2)関数sendNotificationLockedはNotification Taskスレッドをhandlerに送信し、非同期に通知ロック解除モジュールを実行して状態更新を行う:
 private Runnable mNotificationTask = new Runnable()
    {
        public void run()
        {
            while (true) {
                ......
                if (value == 1) {
                    policy.screenTurningOn(mScreenOnListener);
                    ......
                }
                else if (value == 0) {
                    policy.screenTurnedOff(why);
                   ......
                }
                else {
                    // If we're in this case, then this handler is running for a previous
                    // paired transaction.  mBroadcastWakeLock will already have been released.
                    break;
                }
            }
        }
    };
上のスレッド関数runでは、画面が暗くなる場合と明るくなる場合をそれぞれ処理しています.Powerボタンを押すと画面が暗くなると関数screenTurnedOffが呼び出されます.whyは暗くなる原因です.ここで値はOFF_です.BECAUSE_OF_USER.
3)KeyguardViewMediatorでは、画面が暗くなる原因に応じて、画面が暗くなるイベントをそれぞれ処理します.
 /**
     * Called to let us know the screen was turned off.
     * @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER},
     *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or
     *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}.
     */
public void onScreenTurnedOff(int why) {
   synchronized (this) {
   ……
   else if (mShowing) { //  (mShowing)       ,          
                notifyScreenOffLocked();
                resetStateLocked();
            } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT) {
                // if the screen turned off because of timeout, set an alarm
                // to enable it a little bit later (i.e, give the user a chance
                // to turn the screen back on within a certain window without
                // having to unlock the screen)
                ……

                if (timeout <= 0) {
                    // Lock now
                    mSuppressNextLockSound = true;
                    doKeyguardLocked();
                } else {
                    // Lock in the future
                    long when = SystemClock.elapsedRealtime() + timeout;
                    Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
                    intent.putExtra("seq", mDelayedShowingSequence);
                    PendingIntent sender = PendingIntent.getBroadcast(mContext,
                            0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when,
                            sender);
                }
            } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
                // Do not enable the keyguard if the prox sensor forced the screen off.
            } else { //      
                doKeyguardLocked();
            }
        }
}
)doKeyguardLockedを呼び出して画面ロックインタフェースを再表示し、その後の画面ロックインタフェース表示ロジックはKeyguardモジュール起動表示の8~10ステップと同様であり、これ以上説明しない.
5)Powerボタンを押すと画面が暗い->明るい時コード処理ロジックによって1~2ステップ再実行され、第2ステップで画面が明るくなると呼び出される関数はPhoneWindowManager:screenTurningOnである.
6)関数screenTurningOnで仲介者KeyguardViewMediatorの関数onScreenTurnedOnを呼び出します.この関数は画面が明るくなる非同期通知関数KeyguardViewMediator:notifyScreenOnLockedを直接呼び出し、ロック解除モジュールの画面が明るくなることを知らせます.
7)関数handleNotifyScreenOn応答画面が明るくなる通知
8)プログラムは、LockPatternKeyguardView:onScreenTurnedOn関数に実行され、show関数を呼び出して画面インタフェースの表示をロック解除します.コードは以下の通りです.
public void show() {
        if (mMode == Mode.LockScreen) {
            ((KeyguardScreen) mLockScreen).onResume();
        } else {
            ((KeyguardScreen) mUnlockScreen).onResume();
        }
        ......
 }
これで、論理処理が完了する.
3、カスタムパスワードロック解除ロジック
カスタムパスワードのロック解除は、PasswordUnlockScreenから開始します.タイミングチャートは次のとおりです.
1)解錠画面画面にパスワードを入力して確定ボタンをクリックした後、関数onEditorActionで応答する:
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        // Check if this was the result of hitting the enter key
        if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
                || actionId == EditorInfo.IME_ACTION_NEXT) {
            verifyPasswordAndUnlock();
            return true;
        }
        return false;
}
)関数verifyPasswordAndUnlockで入力されたパスワードを判定し、入力が正しければkeyguardDoneがロック解除完了の操作に応答することを測定する.mCallback.keyguardDone(true)呼び出しは、すべてのロック解除画面modeの場合、ロック解除に成功した後に呼び出さなければならない関数であり、その後の処理ロジックは、異なるロック解除画面インタフェースについても同じである.
3)KeyguardScreenCallbackとKeyguardViewMediatorの関数keyguardDoneをコールバックし、後者のkeyguardDone関数ではkeyDoneイベントを非同期で送信する:
    public void keyguardDone(boolean authenticated, boolean wakeup) {
        synchronized (this) {
            ……
            Message msg = mHandler.obtainMessage(KEYGUARD_DONE);
            msg.arg1 = wakeup ? 1 : 0;
            mHandler.sendMessage(msg);
            ……
        }
    }
)関数KeyguardViewMediator:handleKeyguardDone keyguardDoneイベントを非同期で処理し、handleHideを呼び出してスクリーンロックインタフェースを非表示にします.
   5)KeyguardViewManager.hide関数で画面ロックインタフェースの破棄を呼び出すLockPatternKeyguardView:cleanUp数破棄インタフェースを以下のように隠します.
    public void cleanUp() {
        if (mLockScreen != null) {
            ((KeyguardScreen) mLockScreen).onPause();
            ((KeyguardScreen) mLockScreen).cleanUp();
            this.removeView(mLockScreen);
            mLockScreen = null;
        }
        if (mUnlockScreen != null) {
            ((KeyguardScreen) mUnlockScreen).onPause();
            ((KeyguardScreen) mUnlockScreen).cleanUp();
            this.removeView(mUnlockScreen);
            mUnlockScreen = null;
        }
        ......
    }
これで、ロック解除が完了しました.