1. 程式人生 > >Android按鍵訊息傳播流程(WindowManagerService.java)

Android按鍵訊息傳播流程(WindowManagerService.java)

主要涉及的檔案有:

WindowManagerService.java   frameworks\base\services\java\com\android\server\

PhoneWindow.java                     frameworks\policies\base\phone\com\android\internal\policy\impl

KeyInputQueue.java                   frameworks\base\services\java\com\android\server

com_android_server_KeyInputQueue.cpp    frameworks\base\services\jni

EventHub.cpp                                                       frameworks\base\libs\ui

WindowManagerService.java主要有兩個執行緒,一個負責分發按鍵的InputDisapath Thread,另一個負責從底層讀取按鍵訊息InputDeviceRender Thread。

WindowManagerService.java的成員類KeyQ(),負責獲取各種按鍵裝置的狀態,它繼承於KeyInputQueue類。通過執行緒InputDeviceRender Thread的readEvent對按鍵訊息不停讀取,然後呼叫KeyQ例項化後的processEvent函式告訴該按鍵是否應該傳給上層。接著WindowManagerService通過InputDisPatch Thread在按鍵訊息佇列裡取出,並進行分發。

由此可知,InputDisapath執行緒負責分發,InputDeviceRender執行緒通過jni方式呼叫android_server_KeyInputQueue_readEvent(),在這裡負責轉化C++的按鍵訊息為java的格式,android_server_KeyInputQueue_readEvent在EventHub.cpp中獲取按鍵訊息。

具體一些細節程式碼如下:

WindowManagerService中的KeyQ()類,preporcessEvent函式負責對按鍵進行預處理, 主要的事件型別包括EV_KEY(按鍵事件)、EV_REL(相對值,如滑鼠移動,報告相對於最後一次位置的偏移)和EV_ABS(絕對值,如觸控式螢幕)。

[java] view plaincopyprint?
  1. @Override
  2. boolean preprocessEvent(InputDevice device, RawInputEvent event) {  
  3.     if (mPolicy.preprocessInputEventTq(event)) {  
  4.         returntrue;  
  5.     }  
  6.     switch (event.type) {  
  7.         case RawInputEvent.EV_KEY: {  
  8.             // XXX begin hack
  9.             if (DEBUG) {  
  10.                 if (event.keycode == KeyEvent.KEYCODE_G) {  
  11.                     if (event.value != 0) {  
  12.                         // G down
  13.                         mPolicy.screenTurnedOff(WindowManagerPolicy.OFF_BECAUSE_OF_USER);  
  14.                     }  
  15.                     returnfalse;  
  16.                 }  
  17.                 if (event.keycode == KeyEvent.KEYCODE_D) {  
  18.                     if (event.value != 0) {  
  19.                         //dump();
  20.                     }  
  21.                     returnfalse;  
  22.                 }  
  23.             }  
  24.             // XXX end hack
  25.             boolean screenIsOff = !mPowerManager.isScreenOn();  
  26.             boolean screenIsDim = !mPowerManager.isScreenBright();  
  27.             int actions = mPolicy.interceptKeyTq(event, !screenIsOff);/**********按鍵預處理********//  
  28.             if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) {  
  29.                 mPowerManager.goToSleep(event.when);  
  30.             }  
  31.             if (screenIsOff) {  
  32.                 event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;  
  33.             }  
  34.             if (screenIsDim) {  
  35.                 event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;  
  36.             }  
  37.             if ((actions & WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY) != 0) {  
  38.                 mPowerManager.userActivity(event.when, false,  
  39.                         LocalPowerManager.BUTTON_EVENT, false);  
  40.             }  
  41.             if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) {  
  42.                 if (event.value != 0 && mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)) {  
  43.                     filterQueue(this);  
  44.                     mKeyWaiter.appSwitchComing();  
  45.                 }  
  46.                 returntrue;  
  47.             } else {  
  48.                 returnfalse;  
  49.             }  
  50.         }  
  51.         case RawInputEvent.EV_REL: {  
  52.             boolean screenIsOff = !mPowerManager.isScreenOn();  
  53.             boolean screenIsDim = !mPowerManager.isScreenBright();  
  54.             if (screenIsOff) {  
  55.                 if (!mPolicy.isWakeRelMovementTq(event.deviceId,  
  56.                         device.classes, event)) {  
  57.                     //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey");
  58.                     returnfalse;  
  59.                 }  
  60.                 event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;  
  61.             }  
  62.             if (screenIsDim) {  
  63.                 event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;  
  64.             }  
  65.             returntrue;  
  66.         }  
  67.         case RawInputEvent.EV_ABS: {  
  68.             boolean screenIsOff = !mPowerManager.isScreenOn();  
  69.             boolean screenIsDim = !mPowerManager.isScreenBright();  
  70.             if (screenIsOff) {  
  71.                 if (!mPolicy.isWakeAbsMovementTq(event.deviceId,  
  72.                         device.classes, event)) {  
  73.                     //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey");
  74.                     returnfalse;  
  75.                 }  
  76.                 event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;  
  77.             }  
  78.             if (screenIsDim) {  
  79.                 event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;  
  80.             }  
  81.             returntrue;  
  82.         }  
  83.         default:  
  84.             returntrue;  
  85.     }  
  86. }  

preporcessEvent呼叫了InterceptKeyTQ

PhoneWindowManager.java中的InterceptKeyTQ判斷該按鍵是否應該送給上層,還是在此層進行擷取,如待機休眠喚醒則在此層進行擷取。

[java] view plaincopyprint?
  1. /** {@inheritDoc} */
[java] view plaincopyprint?
  1. //2.3中名為interceptKeyBeforeQueueing
  2.     publicint interceptKeyTq(RawInputEvent event, boolean screenIsOn) {  
  3.         int result = ACTION_PASS_TO_USER;  
  4.         finalboolean isWakeKey = isWakeKeyTq(event);  
  5.         // If screen is off then we treat the case where the keyguard is open but hidden
  6.         // the same as if it were open and in front.
  7.         // This will prevent any keys other than the power button from waking the screen
  8.         // when the keyguard is hidden by another activity.
  9.         finalboolean keyguardActive = (screenIsOn ?  
  10.                                         mKeyguardMediator.isShowingAndNotHidden() :  
  11.                                         mKeyguardMediator.isShowing());  
  12.         if (false) {  
  13.             Log.d(TAG, "interceptKeyTq event=" + event + " keycode=" + event.keycode  
  14.                   + " screenIsOn=" + screenIsOn + " keyguardActive=" + keyguardActive);  
  15.         }  
  16.         if (keyguardActive) {  
  17.             if (screenIsOn) {  
  18.                 // when the screen is on, always give the event to the keyguard
  19.                 result |= ACTION_PASS_TO_USER;  
  20.             } else {  
  21.                 // otherwise, don't pass it to the user
  22.                 result &= ~ACTION_PASS_TO_USER;  
  23.                 finalboolean isKeyDown =  
  24.                         (event.type == RawInputEvent.EV_KEY) && (event.value != 0);  
  25.                 if (isWakeKey && isKeyDown) {  
  26.                     // tell the mediator about a wake key, it may decide to
  27.                     // turn on the screen depending on whether the key is
  28.                     // appropriate.
  29.                     if (!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(event.keycode)  
  30.                             && (event.keycode == KeyEvent.KEYCODE_VOLUME_DOWN  
  31.                                 || event.keycode == KeyEvent.KEYCODE_VOLUME_UP)) {  
  32.                         // when keyguard is showing and screen off, we need
  33.                         // to handle the volume key for calls and  music here
  34.                         if (isInCall()) {  
  35.                             handleVolumeKey(AudioManager.STREAM_VOICE_CALL, event.keycode);  
  36.                         } elseif (isMusicActive()) {  
  37.                             handleVolumeKey(AudioManager.STREAM_MUSIC, event.keycode);  
  38.                         }  
  39.                     }  
  40.                 }  
  41.             }  
  42.         } elseif (!screenIsOn) {  
  43.             // If we are in-call with screen off and keyguard is not showing,
  44.             // then handle the volume key ourselves.
  45.             // This is necessary because the phone app will disable the keyguard
  46.             // when the proximity sensor is in use.
  47.             if (isInCall() && event.type == RawInputEvent.EV_KEY &&  
  48.                      (event.keycode == KeyEvent.KEYCODE_VOLUME_DOWN  
  49.                                 || event.keycode == KeyEvent.KEYCODE_VOLUME_UP)) {  
  50.                 result &= ~ACTION_PASS_TO_USER;  
  51.                 handleVolumeKey(AudioManager.STREAM_VOICE_CALL, event.keycode);  
  52.             }  
  53.             if (isWakeKey) {  
  54.                 // a wake key has a sole purpose of waking the device; don't pass
  55.                 // it to the user
  56.                 result |= ACTION_POKE_USER_ACTIVITY;  
  57.                 result &= ~ACTION_PASS_TO_USER;  
  58.             }  
  59.         }  
  60.         int type = event.type;  
  61.         int code = event.keycode;  
  62.         boolean down = event.value != 0;  
  63.         if (type == RawInputEvent.EV_KEY) {  
  64.             if (code == KeyEvent.KEYCODE_ENDCALL  
  65.                     || code == KeyEvent.KEYCODE_POWER) {  
  66.                 if (down) {  
  67.                     boolean handled = false;  
  68.                     boolean hungUp = false;  
  69.                     // key repeats are generated by the window manager, and we don't see them
  70.                     // here, so unless the driver is doing something it shouldn't be, we know
  71.                     // this is the real press event.
  72.                     ITelephony phoneServ = getPhoneInterface();  
  73.                     if (phoneServ != null) {  
  74.                         try {  
  75.                             if (code == KeyEvent.KEYCODE_ENDCALL) {  
  76.                                 handled = hungUp = phoneServ.endCall();  
  77.                             } elseif (code == KeyEvent.KEYCODE_POWER) {  
  78.                                 if (phoneServ.isRinging()) {  
  79.                                     // Pressing Power while there's a ringing incoming
  80.                                     // call should silence the ringer.
  81.                                     phoneServ.silenceRinger();  
  82.                                     handled = true;  
  83.                                 } elseif (phoneServ.isOffhook() &&  
  84.                                            ((mIncallPowerBehavior  
  85.                                              & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP)  
  86.                                             != 0)) {  
  87.                                     // Otherwise, if "Power button ends call" is enabled,
  88.                                     // the Power button will hang up any current active call.
  89.                                     handled = hungUp = phoneServ.endCall();  
  90.                                 }  
  91.                             }  
  92.                         } catch (RemoteException ex) {  
  93.                             Log.w(TAG, "ITelephony threw RemoteException" + ex);  
  94.                         }  
  95.                     } else {  
  96.                         Log.w(TAG, "!!! Unable to find ITelephony interface !!!");  
  97.                     }  
  98.                     if (!screenIsOn  
  99.                             || (handled && code != KeyEvent.KEYCODE_POWER)  
  100.                             || (handled && hungUp && code == KeyEvent.KEYCODE_POWER)) {  
  101.                         mShouldTurnOffOnKeyUp = false;  
  102.                     } else {  
  103.                         // only try to turn off the screen if we didn't already hang up
  104.                         mShouldTurnOffOnKeyUp = true;  
  105.                         mHandler.postDelayed(mPowerLongPress,  
  106.                                 ViewConfiguration.getGlobalActionKeyTimeout());  
  107.                         result &= ~ACTION_PASS_TO_USER;  
  108.                     }  
  109.                 } else {  
  110.                     mHandler.removeCallbacks(mPowerLongPress);  
  111.                     if (mShouldTurnOffOnKeyUp) {  
  112.                         mShouldTurnOffOnKeyUp = false;  
  113.                         boolean gohome, sleeps;  
  114.                         if (code == KeyEvent.KEYCODE_ENDCALL) {  
  115.                             gohome = (mEndcallBehavior  
  116.                                       & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0;  
  117.                             sleeps = (mEndcallBehavior  
  118.                                       & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0;  
  119.                         } else {  
  120.                             gohome = false;  
  121.                             sleeps = true;  
  122.                         }  
  123.                         if (keyguardActive  
  124.                                 || (sleeps && !gohome)  
  125.                                 || (gohome && !goHome() && sleeps)) {  
  126.                             // they must already be on the keyguad or home screen,
  127.                             // go to sleep instead
  128.                             Log.d(TAG, "I'm tired mEndcallBehavior=0x"
  129.                                     + Integer.toHexString(mEndcallBehavior));  
  130.                             result &= ~ACTION_POKE_USER_ACTIVITY;  
  131.                             result |= ACTION_GO_TO_SLEEP;  
  132.                         }  
  133.                         result &= ~ACTION_PASS_TO_USER;  
  134.                     }  
  135.                 }  
  136.             } elseif (isMediaKey(code)) {  
  137.                 // This key needs to be handled even if the screen is off.
  138.                 // If others need to be handled while it's off, this is a reasonable
  139.                 // pattern to follow.
  140.                 if ((result & ACTION_PASS_TO_USER) == 0) {  
  141.                     // Only do this if we would otherwise not pass it to the user. In that
  142.                     // case, the PhoneWindow class will do the same thing, except it will
  143.                     // only do it if the showing app doesn't process the key on its own.
  144.                     KeyEvent keyEvent = new KeyEvent(event.when, event.when,  
  145.                             down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,  
  146.                             code, 0);  
  147.                     mBroadcastWakeLock.acquire();  
  148.                     mHandler.post(new PassHeadsetKey(keyEvent));  
  149.                 }  
  150.             } elseif (code == KeyEvent.KEYCODE_CALL) {  
  151.                 // If an incoming call is ringing, answer it!
  152.                 // (We handle this key here, rather than in the InCallScreen, to make
  153.                 // sure we'll respond to the key even if the InCallScreen hasn't come to
  154.                 // the foreground yet.)
  155.                 // We answer the call on the DOWN event, to agree with
  156.                 // the "fallback" behavior in the InCallScreen.
  157.                 if (down) {  
  158.                     try {  
  159.                         ITelephony phoneServ = getPhoneInterface();  
  160.                         if (phoneServ != null) {  
  161.                             if (phoneServ.isRinging()) {  
  162.                                 Log.i(TAG, "interceptKeyTq:"
  163.                                       + " CALL key-down while ringing: Answer the call!");  
  164.                                 phoneServ.answerRingingCall();  
  165.                                 // And *don't* pass this key thru to the current activity
  166.                                 // (which is presumably the InCallScreen.)
  167.                                 result &= ~ACTION_PASS_TO_USER;  
  168.                             }  
  169.                         } else {  
  170.                             Log.w(TAG, "CALL button: Unable to find ITelephony interface");  
  171.                         }  
  172.                     } catch (RemoteException ex) {  
  173.                         Log.w(TAG, "CALL button: RemoteException from getPhoneInterface()", ex);  
  174.                     }  
  175.                 }  
  176.             } elseif ((code == KeyEvent.KEYCODE_VOLUME_UP)  
  177.                        || (code == KeyEvent.KEYCODE_VOLUME_DOWN)) {  
  178.                 // If an incoming call is ringing, either VOLUME key means
  179.                 // "silence ringer".  We handle these keys here, rather than
  180.                 // in the InCallScreen, to make sure we'll respond to them
  181.                 // even if the InCallScreen hasn't come to the foreground yet.
  182.                 // Look for the DOWN event here, to agree with the "fallback"
  183.                 // behavior in the InCallScreen.
  184.                 if (down) {  
  185.                     try {  
  186.                         ITelephony phoneServ = getPhoneInterface();  
  187.                         if (phoneServ != null) {  
  188.                             if (phoneServ.isRinging()) {  
  189.                                 Log.i(TAG, "interceptKeyTq:"
  190.                                       + " VOLUME key-down while ringing: Silence ringer!");  
  191.                                 // Silence the ringer.  (It's safe to call this
  192.                                 // even if the ringer has already been silenced.)
  193.                                 phoneServ.silenceRinger();  
  194.                                 // And *don't* pass this key thru to the current activity
  195.                                 // (which is probably the InCallScreen.)
  196.                                 result &= ~ACTION_PASS_TO_USER;  
  197.                             }  
  198.                         } else {  
  199.                             Log.w(TAG, "VOLUME button: Unable to find ITelephony interface");  
  200.                         }  
  201.                     } catch (RemoteException ex) {  
  202.                         Log.w(TAG, "VOLUME button: RemoteException from getPhoneInterface()", ex);  
  203.                     }  
  204.                 }  
  205.             }  
  206.         }  
  207.         return result;  
  208.     }  
WindowManagerService中的InputDispatcherThread執行緒process,在裡頭呼叫mQueue(KeyQ類)的getEvent函式來獲取佇列中的訊息,處理後分發。 [java] view plaincopyprint?
  1. privatevoid process() {  
  2.            android.os.Process.setThreadPriority(  
  3.                    android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);  
  4.            // The last key event we saw
  5.            KeyEvent lastKey = null;  
  6.            // Last keydown time for auto-repeating keys
  7.            long lastKeyTime = SystemClock.uptimeMillis();  
  8.            long nextKeyTime = lastKeyTime+LONG_WAIT;  
  9.            long downTime = 0;  
  10.            // How many successive repeats we generated
  11.            int keyRepeatCount = 0;  
  12.            // Need to report that configuration has changed?
  13.            boolean configChanged = false;  
  14.            while (true) {  
  15.                long curTime = SystemClock.uptimeMillis();  
  16.                if (DEBUG_INPUT) Slog.v(  
  17.                    TAG, "Waiting for next key: now=" + curTime  
  18.                    + ", repeat @ " + nextKeyTime);  
  19.                // Retrieve next event, waiting only as long as the next
  20.                // repeat timeout.  If the configuration has changed, then
  21.                // don't wait at all -- we'll report the change as soon as
  22.                // we have processed all events.
  23.                QueuedEvent ev = mQueue.getEvent(//*****獲取佇列中的訊息***//
  24.                    (int)((!configChanged && curTime < nextKeyTime)  
  25.                            ? (nextKeyTime-curTime) : 0));  
  26.                if (DEBUG_INPUT && ev != null) Slog.v(  
  27.                        TAG, "Event: type=" + ev.classType + " data=" + ev.event);  
  28.                if (MEASURE_LATENCY) {  
  29.                    lt.sample("2 got event              ", System.nanoTime() - ev.whenNano);  
  30.                }  
  31.                if (lastKey != null && !mPolicy.allowKeyRepeat()) {  
  32.                    // cancel key repeat at the request of the policy.
  33.                    lastKey = null;  
  34.                    downTime = 0;  
  35.                    lastKeyTime = curTime;  
  36.                    nextKeyTime = curTime + LONG_WAIT;  
  37.                }  
  38.                try {  
  39.                    if (ev != null) {  
  40.                        curTime = SystemClock.uptimeMillis();  
  41.                        int eventType;  
  42.                        if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) {  
  43.                            eventType = eventType((MotionEvent)ev.event);  
  44.                        } elseif (ev.classType == RawInputEvent.CLASS_KEYBOARD ||  
  45.                                    ev.classType == RawInputEvent.CLASS_TRACKBALL) {  
  46.                            eventType = LocalPowerManager.BUTTON_EVENT;  
  47.                        } else {  
  48.                            eventType = LocalPowerManager.OTHER_EVENT;  
  49.                        }  
  50.                        try {  
  51.                            if ((curTime - mLastBatteryStatsCallTime)  
  52.                                    >= MIN_TIME_BETWEEN_USERACTIVITIES) {  
  53.                                mLastBatteryStatsCallTime = curTime;  
  54.                                mBatteryStats.noteInputEvent();  
  55.                            }  
  56.                        } catch (RemoteException e) {  
  57.                            // Ignore
  58.                        }  
  59.                        if (ev.classType == RawInputEvent.CLASS_CONFIGURATION_CHANGED) {  
  60.                            // do not wake screen in this case
  61.                        } elseif (eventType != TOUCH_EVENT  
  62.                                && eventType != LONG_TOUCH_EVENT  
  63.                                && eventType != CHEEK_EVENT) {  
  64.                            mPowerManager.userActivity(curTime, false,  
  65.                                    eventType, false);  
  66.                        } elseif (mLastTouchEventType != eventType  
  67.                                || (curTime - mLastUserActivityCallTime)  
  68.                                >= MIN_TIME_BETWEEN_USERACTIVITIES) {  
  69.                            mLastUserActivityCallTime = curTime;  
  70.                            mLastTouchEventType = eventType;  
  71.                            mPowerManager.userActivity(curTime, false,  
  72.                                    eventType, false);  
  73.                        }  
  74.                        switch (ev.classType) {  
  75.                            case RawInputEvent.CLASS_KEYBOARD:  
  76.                                KeyEvent ke = (KeyEvent)ev.event;  
  77.                                if (ke.isDown()) {  
  78.                                    lastKey = ke;  
  79.                                    downTime = curTime;  
  80.                                    keyRepeatCount = 0;  
  81.                                    lastKeyTime = curTime;  
  82.                                    nextKeyTime = lastKeyTime  
  83.                                            + ViewConfiguration.getLongPressTimeout();  
  84.                                    if (DEBUG_INPUT) Slog.v(  
  85.                                        TAG, "Received key down: first repeat @ "
  86.                                        + nextKeyTime);  
  87.                                } else {  
  88.                                    lastKey = null;  
  89.                                    downTime = 0;  
  90.                                    // Arbitrary long timeout.
  91.                                    lastKeyTime = curTime;  
  92.                                    nextKeyTime = curTime + LONG_WAIT;  
  93.                                    if (DEBUG_INPUT) Slog.v(  
  94.                                        TAG, "Received key up: ignore repeat @ "
  95.                                        + nextKeyTime);  
  96.                                }  
  97.                                dispatchKey((KeyEvent)ev.event, 00);  
  98.                                mQueue.recycleEvent(ev);  
  99.                                break;  
  100.                            case RawInputEvent.CLASS_TOUCHSCREEN:  
  101.                                //Slog.i(TAG, "Read next event " + ev);
  102.                                dispatchPointer(ev, (MotionEvent)ev.event, 00);  
  103.                                break;  
  104.                            case RawInputEvent.CLASS_TRACKBALL:  
  105.                                dispatchTrackball(ev, (MotionEvent)ev.event, 00);  
  106.                                break;  
  107.                            case RawInputEvent.CLASS_CONFIGURATION_CHANGED:  
  108.                                configChanged = true;  
  109.                                break;  
  110.                            default:  
  111.                                mQueue.recycleEvent(ev);  
  112.                            break;  
  113.                        }  
  114.                    } elseif (configChanged) {  
  115.                        configChanged = false;  
  116.                        sendNewConfiguration();  
  117.                    } elseif (lastKey != null) {  
  118.                        curTime = SystemClock.uptimeMillis();  
  119.                        // Timeout occurred while key was down.  If it is at or
  120.                        // past the key repeat time, dispatch the repeat.
  121.                        if (DEBUG_INPUT) Slog.v(  
  122.                            TAG, "Key timeout: repeat=" + nextKeyTime  
  123.                            + ", now=" + curTime);  
  124.                        if (curTime < nextKeyTime) {  
  125.                            continue;  
  126.                        }  
  127.                        lastKeyTime = nextKeyTime;  
  128.                        nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY;  
  129.                        keyRepeatCount++;  
  130.                        if (DEBUG_INPUT) Slog.v(  
  131.                            TAG, "Key repeat: count=" + keyRepeatCount  
  132.                            + ", next @ " + nextKeyTime);  
  133.                        KeyEvent newEvent;  
  134.                        if (downTime != 0 && (downTime  
  135.                                + ViewConfiguration.getLongPressTimeout())  
  136.                                <= curTime) {  
  137.                            newEvent = KeyEvent.changeTimeRepeat(lastKey,  
  138.                                    curTime, keyRepeatCount,  
  139.                                    lastKey.getFlags() | KeyEvent.FLAG_LONG_PRESS);  
  140.                            downTime = 0;  
  141.                        } else {  
  142.                            newEvent = KeyEvent.changeTimeRepeat(lastKey,  
  143.                                    curTime, keyRepeatCount);  
  144.                        }  
  145.                        dispatchKey(newEvent, 00);  
  146.                    } else {  
  147.                        curTime = SystemClock.uptimeMillis();  
  148.                        lastKeyTime = curTime;  
  149.                        nextKeyTime = curTime + LONG_WAIT;  
  150.                    }  
  151.