diff options
Diffstat (limited to 'services')
35 files changed, 1862 insertions, 884 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index a0407b9..fa49592 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -21,7 +21,8 @@ LOCAL_SHARED_LIBRARIES := \ libhardware \ libhardware_legacy \ libeffects \ - libdl + libdl \ + libpowermanager LOCAL_STATIC_LIBRARIES := \ libcpustats \ diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index cb1f921..4e068b2 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -53,6 +53,7 @@ #include <audio_effects/effect_visualizer.h> #include <cpustats/ThreadCpuUsage.h> +#include <powermanager/PowerManager.h> // #define DEBUG_CPU_USAGE 10 // log statistics every n wall clock seconds // ---------------------------------------------------------------------------- @@ -887,14 +888,18 @@ void AudioFlinger::removeClient_l(pid_t pid) AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id, uint32_t device) : Thread(false), mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0), - mFrameSize(1), mFormat(0), mStandby(false), mId(id), mExiting(false), mDevice(device) + mFrameSize(1), mFormat(0), mStandby(false), mId(id), mExiting(false), + mDevice(device) { + mDeathRecipient = new PMDeathRecipient(this); } AudioFlinger::ThreadBase::~ThreadBase() { mParamCond.broadcast(); mNewParameters.clear(); + // do not lock the mutex in destructor + releaseWakeLock_l(); } void AudioFlinger::ThreadBase::exit() @@ -1061,6 +1066,69 @@ status_t AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String1 return NO_ERROR; } +void AudioFlinger::ThreadBase::acquireWakeLock() +{ + Mutex::Autolock _l(mLock); + acquireWakeLock_l(); +} + +void AudioFlinger::ThreadBase::acquireWakeLock_l() +{ + if (mPowerManager == 0) { + // use checkService() to avoid blocking if power service is not up yet + sp<IBinder> binder = + defaultServiceManager()->checkService(String16("power")); + if (binder == 0) { + LOGW("Thread %s cannot connect to the power manager service", mName); + } else { + mPowerManager = interface_cast<IPowerManager>(binder); + binder->linkToDeath(mDeathRecipient); + } + } + if (mPowerManager != 0) { + sp<IBinder> binder = new BBinder(); + status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK, + binder, + String16(mName)); + if (status == NO_ERROR) { + mWakeLockToken = binder; + } + LOGV("acquireWakeLock_l() %s status %d", mName, status); + } +} + +void AudioFlinger::ThreadBase::releaseWakeLock() +{ + Mutex::Autolock _l(mLock); + releaseWakeLock(); +} + +void AudioFlinger::ThreadBase::releaseWakeLock_l() +{ + if (mWakeLockToken != 0) { + LOGV("releaseWakeLock_l() %s", mName); + if (mPowerManager != 0) { + mPowerManager->releaseWakeLock(mWakeLockToken, 0); + } + mWakeLockToken.clear(); + } +} + +void AudioFlinger::ThreadBase::clearPowerManager() +{ + Mutex::Autolock _l(mLock); + releaseWakeLock_l(); + mPowerManager.clear(); +} + +void AudioFlinger::ThreadBase::PMDeathRecipient::binderDied(const wp<IBinder>& who) +{ + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + thread->clearPowerManager(); + } + LOGW("power manager service died !!!"); +} // ---------------------------------------------------------------------------- @@ -1072,6 +1140,8 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output), mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false) { + snprintf(mName, kNameLength, "AudioOut_%d", id); + readOutputParameters(); mMasterVolume = mAudioFlinger->masterVolume(); @@ -1170,12 +1240,7 @@ status_t AudioFlinger::PlaybackThread::readyToRun() void AudioFlinger::PlaybackThread::onFirstRef() { - const size_t SIZE = 256; - char buffer[SIZE]; - - snprintf(buffer, SIZE, "Playback Thread %p", this); - - run(buffer, ANDROID_PRIORITY_URGENT_AUDIO); + run(mName, ANDROID_PRIORITY_URGENT_AUDIO); } // PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held @@ -1522,6 +1587,8 @@ bool AudioFlinger::MixerThread::threadLoop() const CentralTendencyStatistics& stats = cpu.statistics(); #endif + acquireWakeLock(); + while (!exitPending()) { #ifdef DEBUG_CPU_USAGE @@ -1585,10 +1652,12 @@ bool AudioFlinger::MixerThread::threadLoop() if (exitPending()) break; + releaseWakeLock_l(); // wait until we have something to do... LOGV("MixerThread %p TID %d going to sleep\n", this, gettid()); mWaitWorkCV.wait(mLock); LOGV("MixerThread %p TID %d waking up\n", this, gettid()); + acquireWakeLock_l(); if (mMasterMute == false) { char value[PROPERTY_VALUE_MAX]; @@ -1689,6 +1758,8 @@ bool AudioFlinger::MixerThread::threadLoop() mOutput->stream->common.standby(&mOutput->stream->common); } + releaseWakeLock(); + LOGV("MixerThread %p exiting", this); return false; } @@ -2176,6 +2247,8 @@ bool AudioFlinger::DirectOutputThread::threadLoop() // hardware resources as soon as possible nsecs_t standbyDelay = microseconds(activeSleepTime*2); + acquireWakeLock(); + while (!exitPending()) { bool rampVolume; @@ -2215,9 +2288,11 @@ bool AudioFlinger::DirectOutputThread::threadLoop() if (exitPending()) break; + releaseWakeLock_l(); LOGV("DirectOutputThread %p TID %d going to sleep\n", this, gettid()); mWaitWorkCV.wait(mLock); LOGV("DirectOutputThread %p TID %d waking up in active mode\n", this, gettid()); + acquireWakeLock_l(); if (mMasterMute == false) { char value[PROPERTY_VALUE_MAX]; @@ -2436,6 +2511,8 @@ bool AudioFlinger::DirectOutputThread::threadLoop() mOutput->stream->common.standby(&mOutput->stream->common); } + releaseWakeLock(); + LOGV("DirectOutputThread %p exiting", this); return false; } @@ -2561,6 +2638,8 @@ bool AudioFlinger::DuplicatingThread::threadLoop() uint32_t sleepTime = idleSleepTime; Vector< sp<EffectChain> > effectChains; + acquireWakeLock(); + while (!exitPending()) { processConfigEvents(); @@ -2601,9 +2680,12 @@ bool AudioFlinger::DuplicatingThread::threadLoop() if (exitPending()) break; + releaseWakeLock_l(); LOGV("DuplicatingThread %p TID %d going to sleep\n", this, gettid()); mWaitWorkCV.wait(mLock); LOGV("DuplicatingThread %p TID %d waking up\n", this, gettid()); + acquireWakeLock_l(); + if (mMasterMute == false) { char value[PROPERTY_VALUE_MAX]; property_get("ro.audio.silent", value, "0"); @@ -2690,6 +2772,8 @@ bool AudioFlinger::DuplicatingThread::threadLoop() effectChains.clear(); } + releaseWakeLock(); + return false; } @@ -3814,6 +3898,9 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, mInput(input), mTrack(NULL), mResampler(0), mRsmpOutBuffer(0), mRsmpInBuffer(0) { mType = ThreadBase::RECORD; + + snprintf(mName, kNameLength, "AudioIn_%d", id); + mReqChannelCount = popcount(channels); mReqSampleRate = sampleRate; readInputParameters(); @@ -3831,12 +3918,7 @@ AudioFlinger::RecordThread::~RecordThread() void AudioFlinger::RecordThread::onFirstRef() { - const size_t SIZE = 256; - char buffer[SIZE]; - - snprintf(buffer, SIZE, "Record Thread %p", this); - - run(buffer, PRIORITY_URGENT_AUDIO); + run(mName, PRIORITY_URGENT_AUDIO); } bool AudioFlinger::RecordThread::threadLoop() @@ -3847,6 +3929,8 @@ bool AudioFlinger::RecordThread::threadLoop() nsecs_t lastWarning = 0; + acquireWakeLock(); + // start recording while (!exitPending()) { @@ -3863,10 +3947,12 @@ bool AudioFlinger::RecordThread::threadLoop() if (exitPending()) break; + releaseWakeLock_l(); LOGV("RecordThread: loop stopping"); // go to sleep mWaitWorkCV.wait(mLock); LOGV("RecordThread: loop starting"); + acquireWakeLock_l(); continue; } if (mActiveTrack != 0) { @@ -3907,8 +3993,6 @@ bool AudioFlinger::RecordThread::threadLoop() for (size_t i = 0; i < effectChains.size(); i ++) { effectChains[i]->process_l(); } - // enable changes in effect chain - unlockEffectChains(effectChains); buffer.frameCount = mFrameCount; if (LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) { @@ -4008,9 +4092,9 @@ bool AudioFlinger::RecordThread::threadLoop() // clear the overflow. usleep(kRecordThreadSleepUs); } - } else { - unlockEffectChains(effectChains); } + // enable changes in effect chain + unlockEffectChains(effectChains); effectChains.clear(); } @@ -4021,6 +4105,8 @@ bool AudioFlinger::RecordThread::threadLoop() mStartStopCond.broadcast(); + releaseWakeLock(); + LOGV("RecordThread %p exiting", this); return false; } @@ -5581,13 +5667,11 @@ size_t AudioFlinger::EffectModule::removeHandle(const wp<EffectHandle>& handle) } } - // Release effect engine here so that it is done immediately. Otherwise it will be released - // by the destructor when the last strong reference on the this object is released which can - // happen after next process is called on this effect. - if (size == 0 && mEffectInterface != NULL) { - // release effect engine - EffectRelease(mEffectInterface); - mEffectInterface = NULL; + // Prevent calls to process() and other functions on effect interface from now on. + // The effect engine will be released by the destructor when the last strong reference on + // this object is released which can happen after next process is called. + if (size == 0) { + mState = DESTROYED; } return size; @@ -5637,7 +5721,7 @@ void AudioFlinger::EffectModule::updateState() { mState = IDLE; } break; - default: //IDLE , ACTIVE + default: //IDLE , ACTIVE, DESTROYED break; } } @@ -5646,7 +5730,7 @@ void AudioFlinger::EffectModule::process() { Mutex::Autolock _l(mLock); - if (mEffectInterface == NULL || + if (mState == DESTROYED || mEffectInterface == NULL || mConfig.inputCfg.buffer.raw == NULL || mConfig.outputCfg.buffer.raw == NULL) { return; @@ -5822,6 +5906,12 @@ status_t AudioFlinger::EffectModule::start_l() return status; } +status_t AudioFlinger::EffectModule::stop() +{ + Mutex::Autolock _l(mLock); + return stop_l(); +} + status_t AudioFlinger::EffectModule::stop_l() { if (mEffectInterface == NULL) { @@ -5858,7 +5948,7 @@ status_t AudioFlinger::EffectModule::command(uint32_t cmdCode, Mutex::Autolock _l(mLock); // LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface); - if (mEffectInterface == NULL) { + if (mState == DESTROYED || mEffectInterface == NULL) { return NO_INIT; } status_t status = (*mEffectInterface)->command(mEffectInterface, @@ -5907,6 +5997,8 @@ status_t AudioFlinger::EffectModule::setEnabled(bool enabled) case ACTIVE: mState = STOPPING; break; + case DESTROYED: + return NO_ERROR; // simply ignore as we are being destroyed } for (size_t i = 1; i < mHandles.size(); i++) { sp<EffectHandle> h = mHandles[i].promote(); @@ -5928,6 +6020,7 @@ bool AudioFlinger::EffectModule::isEnabled() case IDLE: case STOPPING: case STOPPED: + case DESTROYED: default: return false; } @@ -5943,6 +6036,7 @@ bool AudioFlinger::EffectModule::isProcessEnabled() return true; case IDLE: case STARTING: + case DESTROYED: default: return false; } @@ -6544,6 +6638,10 @@ size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect) for (i = 0; i < size; i++) { if (effect == mEffects[i]) { + // calling stop here will remove pre-processing effect from the audio HAL. + // This is safe as we hold the EffectChain mutex which guarantees that we are not in + // the middle of a read from audio HAL + mEffects[i]->stop(); if (type == EFFECT_FLAG_TYPE_AUXILIARY) { delete[] effect->inBuffer(); } else { diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index e2cf946..7b6215f 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -43,6 +43,8 @@ #include "AudioBufferProvider.h" +#include <powermanager/IPowerManager.h> + namespace android { class audio_track_cblk_t; @@ -287,6 +289,8 @@ private: status_t dumpBase(int fd, const Vector<String16>& args); status_t dumpEffectChains(int fd, const Vector<String16>& args); + void clearPowerManager(); + // base for record and playback class TrackBase : public AudioBufferProvider, public RefBase { @@ -386,6 +390,21 @@ private: int mParam; }; + class PMDeathRecipient : public IBinder::DeathRecipient { + public: + PMDeathRecipient(const wp<ThreadBase>& thread) : mThread(thread) {} + virtual ~PMDeathRecipient() {} + + // IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>& who); + + private: + PMDeathRecipient(const PMDeathRecipient&); + PMDeathRecipient& operator = (const PMDeathRecipient&); + + wp<ThreadBase> mThread; + }; + virtual status_t initCheck() const = 0; int type() const { return mType; } uint32_t sampleRate() const; @@ -462,6 +481,11 @@ private: protected: + void acquireWakeLock(); + void acquireWakeLock_l(); + void releaseWakeLock(); + void releaseWakeLock_l(); + friend class Track; friend class TrackBase; friend class PlaybackThread; @@ -490,6 +514,11 @@ private: Vector< sp<EffectChain> > mEffectChains; uint32_t mDevice; // output device for PlaybackThread // input + output devices for RecordThread + static const int kNameLength = 32; + char mName[kNameLength]; + sp<IPowerManager> mPowerManager; + sp<IBinder> mWakeLockToken; + sp<PMDeathRecipient> mDeathRecipient; }; // --- PlaybackThread --- @@ -995,7 +1024,8 @@ private: STARTING, ACTIVE, STOPPING, - STOPPED + STOPPED, + DESTROYED }; int id() { return mId; } @@ -1040,6 +1070,7 @@ private: status_t setDevice(uint32_t device); status_t setVolume(uint32_t *left, uint32_t *right, bool controller); status_t setMode(uint32_t mode); + status_t stop(); status_t dump(int fd, const Vector<String16>& args); diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h index a583aad..09e88c4 100644 --- a/services/camera/libcameraservice/CameraHardwareInterface.h +++ b/services/camera/libcameraservice/CameraHardwareInterface.h @@ -386,7 +386,10 @@ public: if (mDevice->ops->get_parameters) { char *temp = mDevice->ops->get_parameters(mDevice); String8 str_parms(temp); - free(temp); + if (mDevice->ops->put_parameters) + mDevice->ops->put_parameters(mDevice, temp); + else + free(temp); parms.unflatten(str_parms); } return parms; diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index ca2540b..7ea3de2 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -460,6 +460,17 @@ void EventHub::setExcludedDevices(const Vector<String8>& devices) { mExcludedDevices = devices; } +bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device && scanCode >= 0 && scanCode <= KEY_MAX) { + if (test_bit(scanCode, device->keyBitmask)) { + return true; + } + } + return false; +} + bool EventHub::hasLed(int32_t deviceId, int32_t led) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); diff --git a/services/input/EventHub.h b/services/input/EventHub.h index 695dfdf..293a1a0 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -195,6 +195,7 @@ public: virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0; + virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0; virtual bool hasLed(int32_t deviceId, int32_t led) const = 0; virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0; @@ -246,6 +247,7 @@ public: virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize); + virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const; virtual bool hasLed(int32_t deviceId, int32_t led) const; virtual void setLedState(int32_t deviceId, int32_t led, bool on); diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 039b003..af13945 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -998,8 +998,7 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi void InputDispatcher::resetTargetsLocked() { mCurrentInputTargetsValid = false; mCurrentInputTargets.clear(); - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; - mInputTargetWaitApplicationHandle.clear(); + resetANRTimeoutsLocked(); } void InputDispatcher::commitTargetsLocked() { @@ -1110,6 +1109,7 @@ void InputDispatcher::resetANRTimeoutsLocked() { // Reset input target wait timeout. mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; + mInputTargetWaitApplicationHandle.clear(); } int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, @@ -3226,8 +3226,14 @@ void InputDispatcher::setFocusedApplication( AutoMutex _l(mLock); if (inputApplicationHandle != NULL && inputApplicationHandle->update()) { - mFocusedApplicationHandle = inputApplicationHandle; - } else { + if (mFocusedApplicationHandle != inputApplicationHandle) { + if (mFocusedApplicationHandle != NULL) { + resetTargetsLocked(); + } + mFocusedApplicationHandle = inputApplicationHandle; + } + } else if (mFocusedApplicationHandle != NULL) { + resetTargetsLocked(); mFocusedApplicationHandle.clear(); } diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index c9fac81..db312ad 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -151,30 +151,6 @@ static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0; } -static uint32_t getButtonStateForScanCode(int32_t scanCode) { - // Currently all buttons are mapped to the primary button. - switch (scanCode) { - case BTN_LEFT: - return AMOTION_EVENT_BUTTON_PRIMARY; - case BTN_RIGHT: - case BTN_STYLUS: - return AMOTION_EVENT_BUTTON_SECONDARY; - case BTN_MIDDLE: - case BTN_STYLUS2: - return AMOTION_EVENT_BUTTON_TERTIARY; - case BTN_SIDE: - return AMOTION_EVENT_BUTTON_BACK; - case BTN_FORWARD: - case BTN_EXTRA: - return AMOTION_EVENT_BUTTON_FORWARD; - case BTN_BACK: - return AMOTION_EVENT_BUTTON_BACK; - case BTN_TASK: - default: - return 0; - } -} - // Returns true if the pointer should be reported as being down given the specified // button states. This determines whether the event is reported as a touch event. static bool isPointerDown(int32_t buttonState) { @@ -1007,6 +983,378 @@ void InputDevice::fadePointer() { } +// --- CursorButtonAccumulator --- + +CursorButtonAccumulator::CursorButtonAccumulator() { + clearButtons(); +} + +void CursorButtonAccumulator::clearButtons() { + mBtnLeft = 0; + mBtnRight = 0; + mBtnMiddle = 0; + mBtnBack = 0; + mBtnSide = 0; + mBtnForward = 0; + mBtnExtra = 0; + mBtnTask = 0; +} + +void CursorButtonAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_KEY) { + switch (rawEvent->scanCode) { + case BTN_LEFT: + mBtnLeft = rawEvent->value; + break; + case BTN_RIGHT: + mBtnRight = rawEvent->value; + break; + case BTN_MIDDLE: + mBtnMiddle = rawEvent->value; + break; + case BTN_BACK: + mBtnBack = rawEvent->value; + break; + case BTN_SIDE: + mBtnSide = rawEvent->value; + break; + case BTN_FORWARD: + mBtnForward = rawEvent->value; + break; + case BTN_EXTRA: + mBtnExtra = rawEvent->value; + break; + case BTN_TASK: + mBtnTask = rawEvent->value; + break; + } + } +} + +uint32_t CursorButtonAccumulator::getButtonState() const { + uint32_t result = 0; + if (mBtnLeft) { + result |= AMOTION_EVENT_BUTTON_PRIMARY; + } + if (mBtnRight) { + result |= AMOTION_EVENT_BUTTON_SECONDARY; + } + if (mBtnMiddle) { + result |= AMOTION_EVENT_BUTTON_TERTIARY; + } + if (mBtnBack || mBtnSide) { + result |= AMOTION_EVENT_BUTTON_BACK; + } + if (mBtnForward || mBtnExtra) { + result |= AMOTION_EVENT_BUTTON_FORWARD; + } + return result; +} + + +// --- CursorMotionAccumulator --- + +CursorMotionAccumulator::CursorMotionAccumulator() : + mHaveRelWheel(false), mHaveRelHWheel(false) { + clearRelativeAxes(); +} + +void CursorMotionAccumulator::configure(InputDevice* device) { + mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL); + mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL); +} + +void CursorMotionAccumulator::clearRelativeAxes() { + mRelX = 0; + mRelY = 0; + mRelWheel = 0; + mRelHWheel = 0; +} + +void CursorMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_REL) { + switch (rawEvent->scanCode) { + case REL_X: + mRelX = rawEvent->value; + break; + case REL_Y: + mRelY = rawEvent->value; + break; + case REL_WHEEL: + mRelWheel = rawEvent->value; + break; + case REL_HWHEEL: + mRelHWheel = rawEvent->value; + break; + } + } +} + + +// --- TouchButtonAccumulator --- + +TouchButtonAccumulator::TouchButtonAccumulator() : + mHaveBtnTouch(false) { + clearButtons(); +} + +void TouchButtonAccumulator::configure(InputDevice* device) { + mHaveBtnTouch = device->getEventHub()->hasScanCode(device->getId(), BTN_TOUCH); +} + +void TouchButtonAccumulator::clearButtons() { + mBtnTouch = 0; + mBtnStylus = 0; + mBtnStylus2 = 0; + mBtnToolFinger = 0; + mBtnToolPen = 0; + mBtnToolRubber = 0; +} + +void TouchButtonAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_KEY) { + switch (rawEvent->scanCode) { + case BTN_TOUCH: + mBtnTouch = rawEvent->value; + break; + case BTN_STYLUS: + mBtnStylus = rawEvent->value; + break; + case BTN_STYLUS2: + mBtnStylus2 = rawEvent->value; + break; + case BTN_TOOL_FINGER: + mBtnToolFinger = rawEvent->value; + break; + case BTN_TOOL_PEN: + mBtnToolPen = rawEvent->value; + break; + case BTN_TOOL_RUBBER: + mBtnToolRubber = rawEvent->value; + break; + } + } +} + +uint32_t TouchButtonAccumulator::getButtonState() const { + uint32_t result = 0; + if (mBtnStylus) { + result |= AMOTION_EVENT_BUTTON_SECONDARY; + } + if (mBtnStylus2) { + result |= AMOTION_EVENT_BUTTON_TERTIARY; + } + return result; +} + +int32_t TouchButtonAccumulator::getToolType() const { + if (mBtnToolRubber) { + return AMOTION_EVENT_TOOL_TYPE_ERASER; + } + if (mBtnToolPen) { + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + if (mBtnToolFinger) { + return AMOTION_EVENT_TOOL_TYPE_FINGER; + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +bool TouchButtonAccumulator::isActive() const { + return mBtnTouch || mBtnToolFinger || mBtnToolPen + || mBtnToolRubber || mBtnStylus || mBtnStylus2; +} + +bool TouchButtonAccumulator::isHovering() const { + return mHaveBtnTouch && !mBtnTouch; +} + + +// --- SingleTouchMotionAccumulator --- + +SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() { + clearAbsoluteAxes(); +} + +void SingleTouchMotionAccumulator::clearAbsoluteAxes() { + mAbsX = 0; + mAbsY = 0; + mAbsPressure = 0; + mAbsToolWidth = 0; + mAbsDistance = 0; +} + +void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + switch (rawEvent->scanCode) { + case ABS_X: + mAbsX = rawEvent->value; + break; + case ABS_Y: + mAbsY = rawEvent->value; + break; + case ABS_PRESSURE: + mAbsPressure = rawEvent->value; + break; + case ABS_TOOL_WIDTH: + mAbsToolWidth = rawEvent->value; + break; + case ABS_DISTANCE: + mAbsDistance = rawEvent->value; + break; + } + } +} + + +// --- MultiTouchMotionAccumulator --- + +MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() : + mCurrentSlot(-1), mSlots(NULL), mSlotCount(0), mUsingSlotsProtocol(false) { +} + +MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { + delete[] mSlots; +} + +void MultiTouchMotionAccumulator::configure(size_t slotCount, bool usingSlotsProtocol) { + mSlotCount = slotCount; + mUsingSlotsProtocol = usingSlotsProtocol; + + delete[] mSlots; + mSlots = new Slot[slotCount]; +} + +void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { + for (size_t i = 0; i < mSlotCount; i++) { + mSlots[i].clearIfInUse(); + } + mCurrentSlot = initialSlot; +} + +void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + bool newSlot = false; + if (mUsingSlotsProtocol) { + if (rawEvent->scanCode == ABS_MT_SLOT) { + mCurrentSlot = rawEvent->value; + newSlot = true; + } + } else if (mCurrentSlot < 0) { + mCurrentSlot = 0; + } + + if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { +#if DEBUG_POINTERS + if (newSlot) { + LOGW("MultiTouch device emitted invalid slot index %d but it " + "should be between 0 and %d; ignoring this slot.", + mCurrentSlot, mSlotCount - 1); + } +#endif + } else { + Slot* slot = &mSlots[mCurrentSlot]; + + switch (rawEvent->scanCode) { + case ABS_MT_POSITION_X: + slot->mInUse = true; + slot->mAbsMTPositionX = rawEvent->value; + break; + case ABS_MT_POSITION_Y: + slot->mInUse = true; + slot->mAbsMTPositionY = rawEvent->value; + break; + case ABS_MT_TOUCH_MAJOR: + slot->mInUse = true; + slot->mAbsMTTouchMajor = rawEvent->value; + break; + case ABS_MT_TOUCH_MINOR: + slot->mInUse = true; + slot->mAbsMTTouchMinor = rawEvent->value; + slot->mHaveAbsMTTouchMinor = true; + break; + case ABS_MT_WIDTH_MAJOR: + slot->mInUse = true; + slot->mAbsMTWidthMajor = rawEvent->value; + break; + case ABS_MT_WIDTH_MINOR: + slot->mInUse = true; + slot->mAbsMTWidthMinor = rawEvent->value; + slot->mHaveAbsMTWidthMinor = true; + break; + case ABS_MT_ORIENTATION: + slot->mInUse = true; + slot->mAbsMTOrientation = rawEvent->value; + break; + case ABS_MT_TRACKING_ID: + if (mUsingSlotsProtocol && rawEvent->value < 0) { + slot->clearIfInUse(); + } else { + slot->mInUse = true; + slot->mAbsMTTrackingId = rawEvent->value; + } + break; + case ABS_MT_PRESSURE: + slot->mInUse = true; + slot->mAbsMTPressure = rawEvent->value; + break; + case ABS_MT_TOOL_TYPE: + slot->mInUse = true; + slot->mAbsMTToolType = rawEvent->value; + slot->mHaveAbsMTToolType = true; + break; + } + } + } else if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_MT_REPORT) { + // MultiTouch Sync: The driver has returned all data for *one* of the pointers. + mCurrentSlot += 1; + } +} + + +// --- MultiTouchMotionAccumulator::Slot --- + +MultiTouchMotionAccumulator::Slot::Slot() { + clear(); +} + +void MultiTouchMotionAccumulator::Slot::clearIfInUse() { + if (mInUse) { + clear(); + } +} + +void MultiTouchMotionAccumulator::Slot::clear() { + mInUse = false; + mHaveAbsMTTouchMinor = false; + mHaveAbsMTWidthMinor = false; + mHaveAbsMTToolType = false; + mAbsMTPositionX = 0; + mAbsMTPositionY = 0; + mAbsMTTouchMajor = 0; + mAbsMTTouchMinor = 0; + mAbsMTWidthMajor = 0; + mAbsMTWidthMinor = 0; + mAbsMTOrientation = 0; + mAbsMTTrackingId = -1; + mAbsMTPressure = 0; + mAbsMTToolType = 0; + mAbsMTDistance = 0; +} + +int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { + if (mHaveAbsMTToolType) { + switch (mAbsMTToolType) { + case MT_TOOL_FINGER: + return AMOTION_EVENT_TOOL_TYPE_FINGER; + case MT_TOOL_PEN: + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + + // --- InputMapper --- InputMapper::InputMapper(InputDevice* device) : @@ -1157,7 +1505,10 @@ void KeyboardInputMapper::configureParameters() { getDevice()->getConfiguration().tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); - mParameters.associatedDisplayId = mParameters.orientationAware ? 0 : -1; + mParameters.associatedDisplayId = -1; + if (mParameters.orientationAware) { + mParameters.associatedDisplayId = 0; + } } void KeyboardInputMapper::dumpParameters(String8& dump) { @@ -1229,7 +1580,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0) { int32_t orientation; if (!getPolicy()->getDisplayInfo(mParameters.associatedDisplayId, - NULL, NULL, & orientation)) { + false /*external*/, NULL, NULL, & orientation)) { orientation = DISPLAY_ORIENTATION_0; } @@ -1399,10 +1750,10 @@ void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { } info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f); - if (mHaveVWheel) { + if (mCursorMotionAccumulator.haveRelativeVWheel()) { info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f); } - if (mHaveHWheel) { + if (mCursorMotionAccumulator.haveRelativeHWheel()) { info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f); } } @@ -1416,8 +1767,10 @@ void CursorInputMapper::dump(String8& dump) { dump.appendFormat(INDENT3 "YScale: %0.3f\n", mYScale); dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision); dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision); - dump.appendFormat(INDENT3 "HaveVWheel: %s\n", toString(mHaveVWheel)); - dump.appendFormat(INDENT3 "HaveHWheel: %s\n", toString(mHaveHWheel)); + dump.appendFormat(INDENT3 "HaveVWheel: %s\n", + toString(mCursorMotionAccumulator.haveRelativeVWheel())); + dump.appendFormat(INDENT3 "HaveHWheel: %s\n", + toString(mCursorMotionAccumulator.haveRelativeHWheel())); dump.appendFormat(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); dump.appendFormat(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); dump.appendFormat(INDENT3 "ButtonState: 0x%08x\n", mLocked.buttonState); @@ -1430,6 +1783,8 @@ void CursorInputMapper::configure(const InputReaderConfiguration* config, uint32 InputMapper::configure(config, changes); if (!changes) { // first time only + mCursorMotionAccumulator.configure(getDevice()); + // Configure basic parameters. configureParameters(); @@ -1454,9 +1809,6 @@ void CursorInputMapper::configure(const InputReaderConfiguration* config, uint32 mVWheelScale = 1.0f; mHWheelScale = 1.0f; - - mHaveVWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_WHEEL); - mHaveHWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_HWHEEL); } if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { @@ -1481,8 +1833,10 @@ void CursorInputMapper::configureParameters() { getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"), mParameters.orientationAware); - mParameters.associatedDisplayId = mParameters.mode == Parameters::MODE_POINTER - || mParameters.orientationAware ? 0 : -1; + mParameters.associatedDisplayId = -1; + if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { + mParameters.associatedDisplayId = 0; + } } void CursorInputMapper::dumpParameters(String8& dump) { @@ -1506,7 +1860,8 @@ void CursorInputMapper::dumpParameters(String8& dump) { } void CursorInputMapper::initializeLocked() { - mAccumulator.clear(); + mCursorButtonAccumulator.clearButtons(); + mCursorMotionAccumulator.clearRelativeAxes(); mLocked.buttonState = 0; mLocked.downTime = 0; @@ -1532,10 +1887,8 @@ void CursorInputMapper::reset() { // Synthesize button up event on reset. nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); - mAccumulator.clear(); - mAccumulator.buttonDown = 0; - mAccumulator.buttonUp = buttonState; - mAccumulator.fields = Accumulator::FIELD_BUTTONS; + mCursorButtonAccumulator.clearButtons(); + mCursorMotionAccumulator.clearRelativeAxes(); sync(when); } @@ -1543,64 +1896,15 @@ void CursorInputMapper::reset() { } void CursorInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_KEY: { - int32_t buttonState = getButtonStateForScanCode(rawEvent->scanCode); - if (buttonState) { - if (rawEvent->value) { - mAccumulator.buttonDown = buttonState; - mAccumulator.buttonUp = 0; - } else { - mAccumulator.buttonDown = 0; - mAccumulator.buttonUp = buttonState; - } - mAccumulator.fields |= Accumulator::FIELD_BUTTONS; + mCursorButtonAccumulator.process(rawEvent); + mCursorMotionAccumulator.process(rawEvent); - // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and - // we need to ensure that we report the up/down promptly. - sync(rawEvent->when); - break; - } - break; - } - - case EV_REL: - switch (rawEvent->scanCode) { - case REL_X: - mAccumulator.fields |= Accumulator::FIELD_REL_X; - mAccumulator.relX = rawEvent->value; - break; - case REL_Y: - mAccumulator.fields |= Accumulator::FIELD_REL_Y; - mAccumulator.relY = rawEvent->value; - break; - case REL_WHEEL: - mAccumulator.fields |= Accumulator::FIELD_REL_WHEEL; - mAccumulator.relWheel = rawEvent->value; - break; - case REL_HWHEEL: - mAccumulator.fields |= Accumulator::FIELD_REL_HWHEEL; - mAccumulator.relHWheel = rawEvent->value; - break; - } - break; - - case EV_SYN: - switch (rawEvent->scanCode) { - case SYN_REPORT: - sync(rawEvent->when); - break; - } - break; + if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) { + sync(rawEvent->when); } } void CursorInputMapper::sync(nsecs_t when) { - uint32_t fields = mAccumulator.fields; - if (fields == 0) { - return; // no new state changes, so nothing to do - } - int32_t motionEventAction; int32_t lastButtonState, currentButtonState; PointerProperties pointerProperties; @@ -1611,34 +1915,21 @@ void CursorInputMapper::sync(nsecs_t when) { AutoMutex _l(mLock); lastButtonState = mLocked.buttonState; + currentButtonState = mCursorButtonAccumulator.getButtonState(); + mLocked.buttonState = currentButtonState; - bool down, downChanged; - bool wasDown = isPointerDown(mLocked.buttonState); - bool buttonsChanged = fields & Accumulator::FIELD_BUTTONS; - if (buttonsChanged) { - mLocked.buttonState = (mLocked.buttonState | mAccumulator.buttonDown) - & ~mAccumulator.buttonUp; - - down = isPointerDown(mLocked.buttonState); - - if (!wasDown && down) { - mLocked.downTime = when; - downChanged = true; - } else if (wasDown && !down) { - downChanged = true; - } else { - downChanged = false; - } + bool wasDown = isPointerDown(lastButtonState); + bool down = isPointerDown(currentButtonState); + bool downChanged; + if (!wasDown && down) { + mLocked.downTime = when; + downChanged = true; + } else if (wasDown && !down) { + downChanged = true; } else { - down = wasDown; downChanged = false; } - - currentButtonState = mLocked.buttonState; - downTime = mLocked.downTime; - float deltaX = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX * mXScale : 0.0f; - float deltaY = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY * mYScale : 0.0f; if (downChanged) { motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; @@ -1648,13 +1939,16 @@ void CursorInputMapper::sync(nsecs_t when) { motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE; } + float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale; + float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale; + if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0 && (deltaX != 0.0f || deltaY != 0.0f)) { // Rotate motion based on display orientation if needed. // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. int32_t orientation; if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId, - NULL, NULL, & orientation)) { + false /*external*/, NULL, NULL, & orientation)) { orientation = DISPLAY_ORIENTATION_0; } @@ -1667,25 +1961,17 @@ void CursorInputMapper::sync(nsecs_t when) { pointerCoords.clear(); - if (mHaveVWheel && (fields & Accumulator::FIELD_REL_WHEEL)) { - vscroll = mAccumulator.relWheel; - } else { - vscroll = 0; - } - mWheelYVelocityControl.move(when, NULL, &vscroll); + vscroll = mCursorMotionAccumulator.getRelativeVWheel(); + hscroll = mCursorMotionAccumulator.getRelativeHWheel(); - if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) { - hscroll = mAccumulator.relHWheel; - } else { - hscroll = 0; - } + mWheelYVelocityControl.move(when, NULL, &vscroll); mWheelXVelocityControl.move(when, &hscroll, NULL); mPointerVelocityControl.move(when, &deltaX, &deltaY); if (mPointerController != NULL) { if (deltaX != 0 || deltaY != 0 || vscroll != 0 || hscroll != 0 - || buttonsChanged) { + || currentButtonState != lastButtonState) { mPointerController->setPresentation( PointerControllerInterface::PRESENTATION_POINTER); @@ -1693,8 +1979,8 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerController->move(deltaX, deltaY); } - if (buttonsChanged) { - mPointerController->setButtonState(mLocked.buttonState); + if (currentButtonState != lastButtonState) { + mPointerController->setButtonState(currentButtonState); } mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); @@ -1755,7 +2041,7 @@ void CursorInputMapper::sync(nsecs_t when) { synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, policyFlags, lastButtonState, currentButtonState); - mAccumulator.clear(); + mCursorMotionAccumulator.clearRelativeAxes(); } int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { @@ -1881,10 +2167,11 @@ void TouchInputMapper::dump(String8& dump) { const PointerData& pointer = mLastTouch.pointers[i]; dump.appendFormat(INDENT5 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " - "orientation=%d, distance=%d, isStylus=%s\n", i, + "orientation=%d, distance=%d, toolType=%d, isHovering=%s\n", i, pointer.id, pointer.x, pointer.y, pointer.pressure, pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, pointer.toolMinor, - pointer.orientation, pointer.distance, toString(pointer.isStylus)); + pointer.orientation, pointer.distance, + pointer.toolType, toString(pointer.isHovering)); } if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { @@ -2026,10 +2313,16 @@ void TouchInputMapper::configureParameters() { getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), mParameters.orientationAware); - mParameters.associatedDisplayId = mParameters.orientationAware + mParameters.associatedDisplayId = -1; + mParameters.associatedDisplayIsExternal = false; + if (mParameters.orientationAware || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN - || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER - ? 0 : -1; + || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + mParameters.associatedDisplayIsExternal = + mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN + && getDevice()->isExternal(); + mParameters.associatedDisplayId = 0; + } } void TouchInputMapper::dumpParameters(String8& dump) { @@ -2111,6 +2404,7 @@ bool TouchInputMapper::configureSurfaceLocked() { if (mParameters.associatedDisplayId >= 0) { // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId, + mParameters.associatedDisplayIsExternal, &mLocked.associatedDisplayWidth, &mLocked.associatedDisplayHeight, &mLocked.associatedDisplayOrientation)) { return false; @@ -2924,6 +3218,7 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { calculatePointerIds(); } + // Handle initial down events. uint32_t policyFlags = 0; if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) { if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { @@ -3420,7 +3715,7 @@ void TouchInputMapper::prepareTouches(float* outXPrecision, float* outYPrecision PointerProperties& properties = mCurrentTouchProperties[i]; properties.clear(); properties.id = mCurrentTouch.pointers[i].id; - properties.toolType = getTouchToolType(mCurrentTouch.pointers[i].isStylus); + properties.toolType = mCurrentTouch.pointers[i].toolType; } *outXPrecision = mLocked.orientedXPrecision; @@ -3604,7 +3899,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag PointerProperties pointerProperties; pointerProperties.clear(); pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_INDIRECT_FINGER; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; PointerCoords pointerCoords; pointerCoords.clear(); @@ -3838,8 +4133,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; mPointerGesture.currentGestureProperties[0].clear(); mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_INDIRECT_FINGER; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; mPointerGesture.currentGestureCoords[0].clear(); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); @@ -3880,7 +4174,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_INDIRECT_FINGER; + AMOTION_EVENT_TOOL_TYPE_FINGER; mPointerGesture.currentGestureCoords[0].clear(); mPointerGesture.currentGestureCoords[0].setAxisValue( AMOTION_EVENT_AXIS_X, mPointerGesture.tapX); @@ -3993,7 +4287,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureProperties[0].clear(); mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_INDIRECT_FINGER; + AMOTION_EVENT_TOOL_TYPE_FINGER; mPointerGesture.currentGestureCoords[0].clear(); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); @@ -4240,7 +4534,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureProperties[0].clear(); mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_INDIRECT_FINGER; + AMOTION_EVENT_TOOL_TYPE_FINGER; mPointerGesture.currentGestureCoords[0].clear(); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, mPointerGesture.referenceGestureX); @@ -4331,7 +4625,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureProperties[i].clear(); mPointerGesture.currentGestureProperties[i].id = gestureId; mPointerGesture.currentGestureProperties[i].toolType = - AMOTION_EVENT_TOOL_TYPE_INDIRECT_FINGER; + AMOTION_EVENT_TOOL_TYPE_FINGER; mPointerGesture.currentGestureCoords[i].clear(); mPointerGesture.currentGestureCoords[i].setAxisValue( AMOTION_EVENT_AXIS_X, mPointerGesture.referenceGestureX + deltaX); @@ -4473,15 +4767,6 @@ void TouchInputMapper::fadePointer() { } // release lock } -int32_t TouchInputMapper::getTouchToolType(bool isStylus) const { - if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { - return isStylus ? AMOTION_EVENT_TOOL_TYPE_STYLUS : AMOTION_EVENT_TOOL_TYPE_FINGER; - } else { - return isStylus ? AMOTION_EVENT_TOOL_TYPE_INDIRECT_STYLUS - : AMOTION_EVENT_TOOL_TYPE_INDIRECT_FINGER; - } -} - bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) { return x >= mRawAxes.x.minValue && x <= mRawAxes.x.maxValue && y >= mRawAxes.y.minValue && y <= mRawAxes.y.maxValue; @@ -4760,14 +5045,9 @@ SingleTouchInputMapper::~SingleTouchInputMapper() { } void SingleTouchInputMapper::clearState() { - mAccumulator.clear(); - - mDown = false; - mX = 0; - mY = 0; - mPressure = 0; // default to 0 for devices that don't report pressure - mToolWidth = 0; // default to 0 for devices that don't report tool width - mButtonState = 0; + mCursorButtonAccumulator.clearButtons(); + mTouchButtonAccumulator.clearButtons(); + mSingleTouchMotionAccumulator.clearAbsoluteAxes(); } void SingleTouchInputMapper::reset() { @@ -4777,144 +5057,79 @@ void SingleTouchInputMapper::reset() { } void SingleTouchInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_KEY: - switch (rawEvent->scanCode) { - case BTN_TOUCH: - mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH; - mAccumulator.btnTouch = rawEvent->value != 0; - // Don't sync immediately. Wait until the next SYN_REPORT since we might - // not have received valid position information yet. This logic assumes that - // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet. - break; - default: - if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { - int32_t buttonState = getButtonStateForScanCode(rawEvent->scanCode); - if (buttonState) { - if (rawEvent->value) { - mAccumulator.buttonDown |= buttonState; - } else { - mAccumulator.buttonUp |= buttonState; - } - mAccumulator.fields |= Accumulator::FIELD_BUTTONS; - } - } - break; - } - break; - - case EV_ABS: - switch (rawEvent->scanCode) { - case ABS_X: - mAccumulator.fields |= Accumulator::FIELD_ABS_X; - mAccumulator.absX = rawEvent->value; - break; - case ABS_Y: - mAccumulator.fields |= Accumulator::FIELD_ABS_Y; - mAccumulator.absY = rawEvent->value; - break; - case ABS_PRESSURE: - mAccumulator.fields |= Accumulator::FIELD_ABS_PRESSURE; - mAccumulator.absPressure = rawEvent->value; - break; - case ABS_TOOL_WIDTH: - mAccumulator.fields |= Accumulator::FIELD_ABS_TOOL_WIDTH; - mAccumulator.absToolWidth = rawEvent->value; - break; - } - break; + mCursorButtonAccumulator.process(rawEvent); + mTouchButtonAccumulator.process(rawEvent); + mSingleTouchMotionAccumulator.process(rawEvent); - case EV_SYN: - switch (rawEvent->scanCode) { - case SYN_REPORT: - sync(rawEvent->when); - break; - } - break; + if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) { + sync(rawEvent->when); } } void SingleTouchInputMapper::sync(nsecs_t when) { - uint32_t fields = mAccumulator.fields; - if (fields == 0) { - return; // no new state changes, so nothing to do - } - - if (fields & Accumulator::FIELD_BTN_TOUCH) { - mDown = mAccumulator.btnTouch; - } - - if (fields & Accumulator::FIELD_ABS_X) { - mX = mAccumulator.absX; - } - - if (fields & Accumulator::FIELD_ABS_Y) { - mY = mAccumulator.absY; - } - - if (fields & Accumulator::FIELD_ABS_PRESSURE) { - mPressure = mAccumulator.absPressure; - } - - if (fields & Accumulator::FIELD_ABS_TOOL_WIDTH) { - mToolWidth = mAccumulator.absToolWidth; - } - - if (fields & Accumulator::FIELD_BUTTONS) { - mButtonState = (mButtonState | mAccumulator.buttonDown) & ~mAccumulator.buttonUp; - } - mCurrentTouch.clear(); - if (mDown) { + if (mTouchButtonAccumulator.isActive()) { + uint32_t buttonState = mTouchButtonAccumulator.getButtonState(); + bool isHovering = mTouchButtonAccumulator.isHovering(); + if (mSingleTouchMotionAccumulator.getAbsoluteDistance() > 0) { + isHovering = true; + } + mCurrentTouch.pointerCount = 1; - mCurrentTouch.pointers[0].id = 0; - mCurrentTouch.pointers[0].x = mX; - mCurrentTouch.pointers[0].y = mY; - mCurrentTouch.pointers[0].pressure = mPressure; - mCurrentTouch.pointers[0].touchMajor = 0; - mCurrentTouch.pointers[0].touchMinor = 0; - mCurrentTouch.pointers[0].toolMajor = mToolWidth; - mCurrentTouch.pointers[0].toolMinor = mToolWidth; - mCurrentTouch.pointers[0].orientation = 0; - mCurrentTouch.pointers[0].distance = 0; - mCurrentTouch.pointers[0].isStylus = false; // TODO: Set stylus mCurrentTouch.idToIndex[0] = 0; mCurrentTouch.idBits.markBit(0); - mCurrentTouch.buttonState = mButtonState; + mCurrentTouch.buttonState = buttonState; + + PointerData& outPointer = mCurrentTouch.pointers[0]; + outPointer.id = 0; + outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX(); + outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY(); + outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); + outPointer.touchMajor = 0; + outPointer.touchMinor = 0; + outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); + outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); + outPointer.orientation = 0; + outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance(); + outPointer.toolType = mTouchButtonAccumulator.getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + } + outPointer.isHovering = isHovering; } syncTouch(when, true); - - mAccumulator.clear(); } void SingleTouchInputMapper::configureRawAxes() { TouchInputMapper::configureRawAxes(); + mTouchButtonAccumulator.configure(getDevice()); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x); getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y); getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mRawAxes.pressure); getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mRawAxes.toolMajor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_DISTANCE, & mRawAxes.distance); } // --- MultiTouchInputMapper --- MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : - TouchInputMapper(device), mSlotCount(0), mUsingSlotsProtocol(false) { + TouchInputMapper(device) { } MultiTouchInputMapper::~MultiTouchInputMapper() { } void MultiTouchInputMapper::clearState() { - mAccumulator.clearSlots(mSlotCount); - mAccumulator.clearButtons(); - mButtonState = 0; + mCursorButtonAccumulator.clearButtons(); + mTouchButtonAccumulator.clearButtons(); mPointerIdBits.clear(); - if (mUsingSlotsProtocol) { + if (mMultiTouchMotionAccumulator.isUsingSlotsProtocol()) { // Query the driver for the current slot index and use it as the initial slot // before we start reading events from the device. It is possible that the // current slot index will not be the same as it was when the first event was @@ -4924,12 +5139,16 @@ void MultiTouchInputMapper::clearState() { // two slots will be confused until the next ABS_MT_SLOT event is received. // This can cause the touch point to "jump", but at least there will be // no stuck touches. + int32_t initialSlot; status_t status = getEventHub()->getAbsoluteAxisValue(getDeviceId(), ABS_MT_SLOT, - &mAccumulator.currentSlot); + &initialSlot); if (status) { LOGW("Could not retrieve current multitouch slot index. status=%d", status); - mAccumulator.currentSlot = -1; + initialSlot = -1; } + mMultiTouchMotionAccumulator.clearSlots(initialSlot); + } else { + mMultiTouchMotionAccumulator.clearSlots(-1); } } @@ -4940,124 +5159,26 @@ void MultiTouchInputMapper::reset() { } void MultiTouchInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_KEY: { - if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { - int32_t buttonState = getButtonStateForScanCode(rawEvent->scanCode); - if (buttonState) { - if (rawEvent->value) { - mAccumulator.buttonDown |= buttonState; - } else { - mAccumulator.buttonUp |= buttonState; - } - } - } - break; - } - - case EV_ABS: { - bool newSlot = false; - if (mUsingSlotsProtocol && rawEvent->scanCode == ABS_MT_SLOT) { - mAccumulator.currentSlot = rawEvent->value; - newSlot = true; - } - - if (mAccumulator.currentSlot < 0 || size_t(mAccumulator.currentSlot) >= mSlotCount) { -#if DEBUG_POINTERS - if (newSlot) { - LOGW("MultiTouch device %s emitted invalid slot index %d but it " - "should be between 0 and %d; ignoring this slot.", - getDeviceName().string(), mAccumulator.currentSlot, mSlotCount); - } -#endif - break; - } - - Accumulator::Slot* slot = &mAccumulator.slots[mAccumulator.currentSlot]; - - switch (rawEvent->scanCode) { - case ABS_MT_POSITION_X: - slot->fields |= Accumulator::FIELD_ABS_MT_POSITION_X; - slot->absMTPositionX = rawEvent->value; - break; - case ABS_MT_POSITION_Y: - slot->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y; - slot->absMTPositionY = rawEvent->value; - break; - case ABS_MT_TOUCH_MAJOR: - slot->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR; - slot->absMTTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - slot->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR; - slot->absMTTouchMinor = rawEvent->value; - break; - case ABS_MT_WIDTH_MAJOR: - slot->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; - slot->absMTWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - slot->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR; - slot->absMTWidthMinor = rawEvent->value; - break; - case ABS_MT_ORIENTATION: - slot->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION; - slot->absMTOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - if (mUsingSlotsProtocol && rawEvent->value < 0) { - slot->clear(); - } else { - slot->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID; - slot->absMTTrackingId = rawEvent->value; - } - break; - case ABS_MT_PRESSURE: - slot->fields |= Accumulator::FIELD_ABS_MT_PRESSURE; - slot->absMTPressure = rawEvent->value; - break; - case ABS_MT_TOOL_TYPE: - slot->fields |= Accumulator::FIELD_ABS_MT_TOOL_TYPE; - slot->absMTToolType = rawEvent->value; - break; - } - break; - } + mCursorButtonAccumulator.process(rawEvent); + mTouchButtonAccumulator.process(rawEvent); + mMultiTouchMotionAccumulator.process(rawEvent); - case EV_SYN: - switch (rawEvent->scanCode) { - case SYN_MT_REPORT: { - // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - mAccumulator.currentSlot += 1; - break; - } - - case SYN_REPORT: - sync(rawEvent->when); - break; - } - break; + if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) { + sync(rawEvent->when); } } void MultiTouchInputMapper::sync(nsecs_t when) { - static const uint32_t REQUIRED_FIELDS = - Accumulator::FIELD_ABS_MT_POSITION_X | Accumulator::FIELD_ABS_MT_POSITION_Y; - - size_t inCount = mSlotCount; + size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); size_t outCount = 0; bool havePointerIds = true; mCurrentTouch.clear(); for (size_t inIndex = 0; inIndex < inCount; inIndex++) { - const Accumulator::Slot& inSlot = mAccumulator.slots[inIndex]; - uint32_t fields = inSlot.fields; - - if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) { - // Some drivers send empty MT sync packets without X / Y to indicate a pointer up. - // This may also indicate an unused slot. - // Drop this finger. + const MultiTouchMotionAccumulator::Slot* inSlot = + mMultiTouchMotionAccumulator.getSlot(inIndex); + if (!inSlot->isInUse()) { continue; } @@ -5071,76 +5192,32 @@ void MultiTouchInputMapper::sync(nsecs_t when) { } PointerData& outPointer = mCurrentTouch.pointers[outCount]; - outPointer.x = inSlot.absMTPositionX; - outPointer.y = inSlot.absMTPositionY; - - if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) { - outPointer.pressure = inSlot.absMTPressure; - } else { - // Default pressure to 0 if absent. - outPointer.pressure = 0; - } - - if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) { - if (inSlot.absMTTouchMajor <= 0) { - // Some devices send sync packets with X / Y but with a 0 touch major to indicate - // a pointer going up. Drop this finger. - continue; + outPointer.x = inSlot->getX(); + outPointer.y = inSlot->getY(); + outPointer.pressure = inSlot->getPressure(); + outPointer.touchMajor = inSlot->getTouchMajor(); + outPointer.touchMinor = inSlot->getTouchMinor(); + outPointer.toolMajor = inSlot->getToolMajor(); + outPointer.toolMinor = inSlot->getToolMinor(); + outPointer.orientation = inSlot->getOrientation(); + outPointer.distance = inSlot->getDistance(); + + outPointer.toolType = inSlot->getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = mTouchButtonAccumulator.getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; } - outPointer.touchMajor = inSlot.absMTTouchMajor; - } else { - // Default touch area to 0 if absent. - outPointer.touchMajor = 0; } - if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) { - outPointer.touchMinor = inSlot.absMTTouchMinor; - } else { - // Assume touch area is circular. - outPointer.touchMinor = outPointer.touchMajor; - } - - if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) { - outPointer.toolMajor = inSlot.absMTWidthMajor; - } else { - // Default tool area to 0 if absent. - outPointer.toolMajor = 0; - } - - if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) { - outPointer.toolMinor = inSlot.absMTWidthMinor; - } else { - // Assume tool area is circular. - outPointer.toolMinor = outPointer.toolMajor; - } - - if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) { - outPointer.orientation = inSlot.absMTOrientation; - } else { - // Default orientation to vertical if absent. - outPointer.orientation = 0; - } - - if (fields & Accumulator::FIELD_ABS_MT_DISTANCE) { - outPointer.distance = inSlot.absMTDistance; - } else { - // Default distance is 0 (direct contact). - outPointer.distance = 0; - } - - if (fields & Accumulator::FIELD_ABS_MT_TOOL_TYPE) { - outPointer.isStylus = (inSlot.absMTToolType == MT_TOOL_PEN); - } else { - // Assume this is not a stylus. - outPointer.isStylus = false; - } + outPointer.isHovering = mTouchButtonAccumulator.isHovering() + || inSlot->getDistance() > 0; // Assign pointer id using tracking id if available. if (havePointerIds) { + int32_t trackingId = inSlot->getTrackingId(); int32_t id = -1; - if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) { - int32_t trackingId = inSlot.absMTTrackingId; - + if (trackingId >= 0) { for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) { uint32_t n = idBits.firstMarkedBit(); idBits.clearBit(n); @@ -5170,23 +5247,22 @@ void MultiTouchInputMapper::sync(nsecs_t when) { } mCurrentTouch.pointerCount = outCount; - - mButtonState = (mButtonState | mAccumulator.buttonDown) & ~mAccumulator.buttonUp; - mCurrentTouch.buttonState = mButtonState; + mCurrentTouch.buttonState = mTouchButtonAccumulator.getButtonState(); mPointerIdBits = mCurrentTouch.idBits; syncTouch(when, havePointerIds); - if (!mUsingSlotsProtocol) { - mAccumulator.clearSlots(mSlotCount); + if (!mMultiTouchMotionAccumulator.isUsingSlotsProtocol()) { + mMultiTouchMotionAccumulator.clearSlots(-1); } - mAccumulator.clearButtons(); } void MultiTouchInputMapper::configureRawAxes() { TouchInputMapper::configureRawAxes(); + mTouchButtonAccumulator.configure(getDevice()); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, &mRawAxes.x); getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, &mRawAxes.y); getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, &mRawAxes.touchMajor); @@ -5201,21 +5277,18 @@ void MultiTouchInputMapper::configureRawAxes() { if (mRawAxes.trackingId.valid && mRawAxes.slot.valid && mRawAxes.slot.minValue == 0 && mRawAxes.slot.maxValue > 0) { - mSlotCount = mRawAxes.slot.maxValue + 1; - if (mSlotCount > MAX_SLOTS) { + size_t slotCount = mRawAxes.slot.maxValue + 1; + if (slotCount > MAX_SLOTS) { LOGW("MultiTouch Device %s reported %d slots but the framework " "only supports a maximum of %d slots at this time.", - getDeviceName().string(), mSlotCount, MAX_SLOTS); - mSlotCount = MAX_SLOTS; + getDeviceName().string(), slotCount, MAX_SLOTS); + slotCount = MAX_SLOTS; } - mUsingSlotsProtocol = true; + mMultiTouchMotionAccumulator.configure(slotCount, true /*usingSlotsProtocol*/); } else { - mSlotCount = MAX_POINTERS; - mUsingSlotsProtocol = false; + mMultiTouchMotionAccumulator.configure(MAX_POINTERS, false /*usingSlotsProtocol*/); } - mAccumulator.allocateSlots(mSlotCount); - clearState(); } diff --git a/services/input/InputReader.h b/services/input/InputReader.h index b1fdcf2..ee6990b 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -180,9 +180,11 @@ public: }; /* Gets information about the display with the specified id. + * If external is true, returns the size of the external mirrored + * counterpart of the specified display. * Returns true if the display info is available, false otherwise. */ - virtual bool getDisplayInfo(int32_t displayId, + virtual bool getDisplayInfo(int32_t displayId, bool external, int32_t* width, int32_t* height, int32_t* orientation) = 0; /* Gets the input reader configuration. */ @@ -430,9 +432,8 @@ public: void fadePointer(); - inline const PropertyMap& getConfiguration() { - return mConfiguration; - } + inline const PropertyMap& getConfiguration() { return mConfiguration; } + inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } private: InputReaderContext* mContext; @@ -452,6 +453,171 @@ private: }; +/* Keeps track of the state of mouse or touch pad buttons. */ +class CursorButtonAccumulator { +public: + CursorButtonAccumulator(); + + void clearButtons(); + void process(const RawEvent* rawEvent); + + uint32_t getButtonState() const; + +private: + bool mBtnLeft; + bool mBtnRight; + bool mBtnMiddle; + bool mBtnBack; + bool mBtnSide; + bool mBtnForward; + bool mBtnExtra; + bool mBtnTask; +}; + + +/* Keeps track of cursor movements. */ + +class CursorMotionAccumulator { +public: + CursorMotionAccumulator(); + void configure(InputDevice* device); + + void clearRelativeAxes(); + void process(const RawEvent* rawEvent); + + inline bool haveRelativeVWheel() const { return mHaveRelWheel; } + inline bool haveRelativeHWheel() const { return mHaveRelHWheel; } + + inline int32_t getRelativeX() const { return mRelX; } + inline int32_t getRelativeY() const { return mRelY; } + inline int32_t getRelativeVWheel() const { return mRelWheel; } + inline int32_t getRelativeHWheel() const { return mRelHWheel; } + +private: + bool mHaveRelWheel; + bool mHaveRelHWheel; + + int32_t mRelX; + int32_t mRelY; + int32_t mRelWheel; + int32_t mRelHWheel; +}; + + +/* Keeps track of the state of touch, stylus and tool buttons. */ +class TouchButtonAccumulator { +public: + TouchButtonAccumulator(); + void configure(InputDevice* device); + + void clearButtons(); + void process(const RawEvent* rawEvent); + + uint32_t getButtonState() const; + int32_t getToolType() const; + bool isActive() const; + bool isHovering() const; + +private: + bool mHaveBtnTouch; + + bool mBtnTouch; + bool mBtnStylus; + bool mBtnStylus2; + bool mBtnToolFinger; + bool mBtnToolPen; + bool mBtnToolRubber; +}; + + +/* Keeps track of the state of single-touch protocol. */ +class SingleTouchMotionAccumulator { +public: + SingleTouchMotionAccumulator(); + + void clearAbsoluteAxes(); + void process(const RawEvent* rawEvent); + + inline int32_t getAbsoluteX() const { return mAbsX; } + inline int32_t getAbsoluteY() const { return mAbsY; } + inline int32_t getAbsolutePressure() const { return mAbsPressure; } + inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; } + inline int32_t getAbsoluteDistance() const { return mAbsDistance; } + +private: + int32_t mAbsX; + int32_t mAbsY; + int32_t mAbsPressure; + int32_t mAbsToolWidth; + int32_t mAbsDistance; +}; + + +/* Keeps track of the state of multi-touch protocol. */ +class MultiTouchMotionAccumulator { +public: + class Slot { + public: + inline bool isInUse() const { return mInUse; } + inline int32_t getX() const { return mAbsMTPositionX; } + inline int32_t getY() const { return mAbsMTPositionY; } + inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } + inline int32_t getTouchMinor() const { + return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; } + inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } + inline int32_t getToolMinor() const { + return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; } + inline int32_t getOrientation() const { return mAbsMTOrientation; } + inline int32_t getTrackingId() const { return mAbsMTTrackingId; } + inline int32_t getPressure() const { return mAbsMTPressure; } + inline int32_t getDistance() const { return mAbsMTDistance; } + inline int32_t getToolType() const; + + private: + friend class MultiTouchMotionAccumulator; + + bool mInUse; + bool mHaveAbsMTTouchMinor; + bool mHaveAbsMTWidthMinor; + bool mHaveAbsMTToolType; + + int32_t mAbsMTPositionX; + int32_t mAbsMTPositionY; + int32_t mAbsMTTouchMajor; + int32_t mAbsMTTouchMinor; + int32_t mAbsMTWidthMajor; + int32_t mAbsMTWidthMinor; + int32_t mAbsMTOrientation; + int32_t mAbsMTTrackingId; + int32_t mAbsMTPressure; + int32_t mAbsMTToolType; + int32_t mAbsMTDistance; + + Slot(); + void clearIfInUse(); + void clear(); + }; + + MultiTouchMotionAccumulator(); + ~MultiTouchMotionAccumulator(); + + void configure(size_t slotCount, bool usingSlotsProtocol); + void process(const RawEvent* rawEvent); + + void clearSlots(int32_t initialSlot); + + inline bool isUsingSlotsProtocol() const { return mUsingSlotsProtocol; } + inline size_t getSlotCount() const { return mSlotCount; } + inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } + +private: + int32_t mCurrentSlot; + Slot* mSlots; + size_t mSlotCount; + bool mUsingSlotsProtocol; +}; + + /* An input mapper transforms raw input events into cooked event data. * A single input device can have multiple associated input mappers in order to interpret * different classes of events. @@ -615,29 +781,8 @@ private: bool orientationAware; } mParameters; - struct Accumulator { - enum { - FIELD_BUTTONS = 1, - FIELD_REL_X = 2, - FIELD_REL_Y = 4, - FIELD_REL_WHEEL = 8, - FIELD_REL_HWHEEL = 16, - }; - - uint32_t fields; - - uint32_t buttonDown; - uint32_t buttonUp; - - int32_t relX; - int32_t relY; - int32_t relWheel; - int32_t relHWheel; - - inline void clear() { - fields = 0; - } - } mAccumulator; + CursorButtonAccumulator mCursorButtonAccumulator; + CursorMotionAccumulator mCursorMotionAccumulator; int32_t mSource; float mXScale; @@ -645,8 +790,6 @@ private: float mXPrecision; float mYPrecision; - bool mHaveVWheel; - bool mHaveHWheel; float mVWheelScale; float mHWheelScale; @@ -722,7 +865,8 @@ protected: int32_t toolMinor; int32_t orientation; int32_t distance; - bool isStylus; + int32_t toolType; // AMOTION_EVENT_TOOL_TYPE constant + bool isHovering; inline bool operator== (const PointerData& other) const { return id == other.id @@ -734,7 +878,9 @@ protected: && toolMajor == other.toolMajor && toolMinor == other.toolMinor && orientation == other.orientation - && distance == other.distance; + && distance == other.distance + && toolType == other.toolType + && isHovering == other.isHovering; } inline bool operator!= (const PointerData& other) const { return !(*this == other); @@ -800,6 +946,7 @@ protected: DeviceType deviceType; int32_t associatedDisplayId; + bool associatedDisplayIsExternal; bool orientationAware; enum GestureMode { @@ -1201,7 +1348,6 @@ private: void suppressSwipeOntoVirtualKeys(nsecs_t when); - int32_t getTouchToolType(bool isStylus) const; bool isPointInsideSurfaceLocked(int32_t x, int32_t y); const VirtualKey* findVirtualKeyHitLocked(int32_t x, int32_t y); @@ -1221,40 +1367,9 @@ protected: virtual void configureRawAxes(); private: - struct Accumulator { - enum { - FIELD_BTN_TOUCH = 1, - FIELD_ABS_X = 2, - FIELD_ABS_Y = 4, - FIELD_ABS_PRESSURE = 8, - FIELD_ABS_TOOL_WIDTH = 16, - FIELD_BUTTONS = 32, - }; - - uint32_t fields; - - bool btnTouch; - int32_t absX; - int32_t absY; - int32_t absPressure; - int32_t absToolWidth; - - uint32_t buttonDown; - uint32_t buttonUp; - - inline void clear() { - fields = 0; - buttonDown = 0; - buttonUp = 0; - } - } mAccumulator; - - bool mDown; - int32_t mX; - int32_t mY; - int32_t mPressure; - int32_t mToolWidth; - int32_t mButtonState; + CursorButtonAccumulator mCursorButtonAccumulator; + TouchButtonAccumulator mTouchButtonAccumulator; + SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; void clearState(); @@ -1274,83 +1389,9 @@ protected: virtual void configureRawAxes(); private: - struct Accumulator { - enum { - FIELD_ABS_MT_POSITION_X = 1 << 0, - FIELD_ABS_MT_POSITION_Y = 1 << 1, - FIELD_ABS_MT_TOUCH_MAJOR = 1 << 2, - FIELD_ABS_MT_TOUCH_MINOR = 1 << 3, - FIELD_ABS_MT_WIDTH_MAJOR = 1 << 4, - FIELD_ABS_MT_WIDTH_MINOR = 1 << 5, - FIELD_ABS_MT_ORIENTATION = 1 << 6, - FIELD_ABS_MT_TRACKING_ID = 1 << 7, - FIELD_ABS_MT_PRESSURE = 1 << 8, - FIELD_ABS_MT_TOOL_TYPE = 1 << 9, - FIELD_ABS_MT_DISTANCE = 1 << 10, - }; - - struct Slot { - uint32_t fields; // 0 if slot is unused - - int32_t absMTPositionX; - int32_t absMTPositionY; - int32_t absMTTouchMajor; - int32_t absMTTouchMinor; - int32_t absMTWidthMajor; - int32_t absMTWidthMinor; - int32_t absMTOrientation; - int32_t absMTTrackingId; - int32_t absMTPressure; - int32_t absMTToolType; - int32_t absMTDistance; - - inline Slot() { - clear(); - } - - inline void clear() { - fields = 0; - } - }; - - // Current slot index. - int32_t currentSlot; - - // Array of slots. - Slot* slots; - - // Bitfield of buttons that went down or up. - uint32_t buttonDown; - uint32_t buttonUp; - - Accumulator() : currentSlot(0), slots(NULL), buttonDown(0), buttonUp(0) { - } - - ~Accumulator() { - delete[] slots; - } - - void allocateSlots(size_t slotCount) { - slots = new Slot[slotCount]; - } - - void clearSlots(size_t slotCount) { - for (size_t i = 0; i < slotCount; i++) { - slots[i].clear(); - } - currentSlot = 0; - } - - void clearButtons() { - buttonDown = 0; - buttonUp = 0; - } - } mAccumulator; - - size_t mSlotCount; - bool mUsingSlotsProtocol; - - int32_t mButtonState; + CursorButtonAccumulator mCursorButtonAccumulator; + TouchButtonAccumulator mTouchButtonAccumulator; + MultiTouchMotionAccumulator mMultiTouchMotionAccumulator; // Specifies the pointer id bits that are in use, and their associated tracking id. BitSet32 mPointerIdBits; diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index 131894a..8533743 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -157,7 +157,7 @@ public: } private: - virtual bool getDisplayInfo(int32_t displayId, + virtual bool getDisplayInfo(int32_t displayId, bool external /*currently ignored*/, int32_t* width, int32_t* height, int32_t* orientation) { ssize_t index = mDisplayInfos.indexOfKey(displayId); if (index >= 0) { @@ -707,6 +707,15 @@ private: return result; } + virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const { + Device* device = getDevice(deviceId); + if (device) { + ssize_t index = device->keys.indexOfKey(scanCode); + return index >= 0; + } + return false; + } + virtual bool hasLed(int32_t deviceId, int32_t led) const { Device* device = getDevice(deviceId); return device && device->leds.indexOfKey(led) >= 0; @@ -2116,6 +2125,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaStat // Button press. // Mostly testing non x/y behavior here so we don't need to check again elsewhere. process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(ARBITRARY_TIME, args.eventTime); ASSERT_EQ(DEVICE_ID, args.deviceId); @@ -2137,6 +2147,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaStat // Button release. Should have same down time. process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); + process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); ASSERT_EQ(DEVICE_ID, args.deviceId); @@ -2190,6 +2201,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { // Button press without following sync. process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], @@ -2197,6 +2209,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { // Button release without following sync. process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], @@ -2233,6 +2246,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { // Release Button. process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], @@ -2248,10 +2262,12 @@ TEST_F(CursorInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeButto // Button press. process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); // Button release. process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); // Reset. Should not synthesize button up since button is not pressed. @@ -2269,6 +2285,7 @@ TEST_F(CursorInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp) { // Button press. process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); // Reset. Should synthesize button up. @@ -2445,6 +2462,7 @@ float TouchInputMapperTest::toDisplayY(int32_t rawY) { class SingleTouchInputMapperTest : public TouchInputMapperTest { protected: + void prepareButtons(); void prepareAxes(int axes); void processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y); @@ -2455,6 +2473,10 @@ protected: void processSync(SingleTouchInputMapper* mapper); }; +void SingleTouchInputMapperTest::prepareButtons() { + mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, AKEYCODE_UNKNOWN, 0); +} + void SingleTouchInputMapperTest::prepareAxes(int axes) { if (axes & POSITION) { mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_X, @@ -2504,6 +2526,7 @@ void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) { TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); + prepareButtons(); prepareAxes(POSITION); addMapperAndConfigure(mapper); @@ -2514,6 +2537,7 @@ TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsA SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X); mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y); + prepareButtons(); prepareAxes(POSITION); addMapperAndConfigure(mapper); @@ -2522,6 +2546,7 @@ TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsA TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchPad_ReturnsTouchPad) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); + prepareButtons(); prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchPad"); addMapperAndConfigure(mapper); @@ -2531,6 +2556,7 @@ TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchPad_ReturnsTo TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); + prepareButtons(); prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchScreen"); addMapperAndConfigure(mapper); @@ -2542,6 +2568,7 @@ TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); addMapperAndConfigure(mapper); @@ -2570,6 +2597,7 @@ TEST_F(SingleTouchInputMapperTest, GetScanCodeState) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); addMapperAndConfigure(mapper); @@ -2598,6 +2626,7 @@ TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); addMapperAndConfigure(mapper); @@ -2615,6 +2644,7 @@ TEST_F(SingleTouchInputMapperTest, Reset_WhenVirtualKeysAreDown_SendsUp) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); addMapperAndConfigure(mapper); @@ -2649,6 +2679,7 @@ TEST_F(SingleTouchInputMapperTest, Reset_WhenNothingIsPressed_NothingMuchHappens SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); addMapperAndConfigure(mapper); @@ -2676,6 +2707,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNor SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); addMapperAndConfigure(mapper); @@ -2726,6 +2758,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfB SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); addMapperAndConfigure(mapper); @@ -2847,6 +2880,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMoves SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); addMapperAndConfigure(mapper); @@ -2920,6 +2954,7 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); addMapperAndConfigure(mapper); @@ -3009,6 +3044,7 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) { TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareButtons(); prepareAxes(POSITION); addConfigurationProperty("touch.orientationAware", "0"); addMapperAndConfigure(mapper); @@ -3032,6 +3068,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotate TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareButtons(); prepareAxes(POSITION); addMapperAndConfigure(mapper); @@ -3094,6 +3131,7 @@ TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); prepareAxes(POSITION | PRESSURE | TOOL); addMapperAndConfigure(mapper); @@ -3815,6 +3853,7 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_SummedLinear FakeInputDispatcher::NotifyMotionArgs args; ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); + ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), args.action); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 0843948..9a9cc8f 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -1995,7 +1995,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } else { try { - mNetd.setDnsServersForInterface(Integer.toString(netType), + mNetd.setDnsServersForInterface(p.getInterfaceName(), NetworkUtils.makeStrings(dnses)); } catch (Exception e) { Slog.e(TAG, "exception setting dns servers: " + e); diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 17ad268..39d2b1c 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -125,10 +125,14 @@ class NetworkManagementService extends INetworkManagementService.Stub { private Thread mThread; private final CountDownLatch mConnectedSignal = new CountDownLatch(1); + // TODO: replace with RemoteCallbackList private ArrayList<INetworkManagementEventObserver> mObservers; + private Object mQuotaLock = new Object(); /** Set of interfaces with active quotas. */ - private HashSet<String> mInterfaceQuota = Sets.newHashSet(); + private HashSet<String> mActiveQuotaIfaces = Sets.newHashSet(); + /** Set of interfaces with active alerts. */ + private HashSet<String> mActiveAlertIfaces = Sets.newHashSet(); /** Set of UIDs with active reject rules. */ private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray(); @@ -1058,26 +1062,25 @@ class NetworkManagementService extends INetworkManagementService.Stub { } @Override - public void setInterfaceQuota(String iface, long quota) { + public void setInterfaceQuota(String iface, long quotaBytes) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); // silently discard when control disabled // TODO: eventually migrate to be always enabled if (!mBandwidthControlEnabled) return; - synchronized (mInterfaceQuota) { - if (mInterfaceQuota.contains(iface)) { - // TODO: eventually consider throwing - return; + synchronized (mQuotaLock) { + if (mActiveQuotaIfaces.contains(iface)) { + throw new IllegalStateException("iface " + iface + " already has quota"); } final StringBuilder command = new StringBuilder(); - command.append("bandwidth setiquota ").append(iface).append(" ").append(quota); + command.append("bandwidth setiquota ").append(iface).append(" ").append(quotaBytes); try { - // TODO: add support for quota shared across interfaces + // TODO: support quota shared across interfaces mConnector.doCommand(command.toString()); - mInterfaceQuota.add(iface); + mActiveQuotaIfaces.add(iface); } catch (NativeDaemonConnectorException e) { throw new IllegalStateException("Error communicating to native daemon", e); } @@ -1092,8 +1095,8 @@ class NetworkManagementService extends INetworkManagementService.Stub { // TODO: eventually migrate to be always enabled if (!mBandwidthControlEnabled) return; - synchronized (mInterfaceQuota) { - if (!mInterfaceQuota.contains(iface)) { + synchronized (mQuotaLock) { + if (!mActiveQuotaIfaces.contains(iface)) { // TODO: eventually consider throwing return; } @@ -1102,9 +1105,42 @@ class NetworkManagementService extends INetworkManagementService.Stub { command.append("bandwidth removeiquota ").append(iface); try { - // TODO: add support for quota shared across interfaces + // TODO: support quota shared across interfaces + mConnector.doCommand(command.toString()); + mActiveQuotaIfaces.remove(iface); + mActiveAlertIfaces.remove(iface); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon", e); + } + } + } + + @Override + public void setInterfaceAlert(String iface, long alertBytes) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + // silently discard when control disabled + // TODO: eventually migrate to be always enabled + if (!mBandwidthControlEnabled) return; + + // quick sanity check + if (!mActiveQuotaIfaces.contains(iface)) { + throw new IllegalStateException("setting alert requires existing quota on iface"); + } + + synchronized (mQuotaLock) { + if (mActiveAlertIfaces.contains(iface)) { + throw new IllegalStateException("iface " + iface + " already has alert"); + } + + final StringBuilder command = new StringBuilder(); + command.append("bandwidth setinterfacealert ").append(iface).append(" ").append( + alertBytes); + + try { + // TODO: support alert shared across interfaces mConnector.doCommand(command.toString()); - mInterfaceQuota.remove(iface); + mActiveAlertIfaces.add(iface); } catch (NativeDaemonConnectorException e) { throw new IllegalStateException("Error communicating to native daemon", e); } @@ -1112,6 +1148,51 @@ class NetworkManagementService extends INetworkManagementService.Stub { } @Override + public void removeInterfaceAlert(String iface) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + // silently discard when control disabled + // TODO: eventually migrate to be always enabled + if (!mBandwidthControlEnabled) return; + + synchronized (mQuotaLock) { + if (!mActiveAlertIfaces.contains(iface)) { + // TODO: eventually consider throwing + return; + } + + final StringBuilder command = new StringBuilder(); + command.append("bandwidth removeinterfacealert ").append(iface); + + try { + // TODO: support alert shared across interfaces + mConnector.doCommand(command.toString()); + mActiveAlertIfaces.remove(iface); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon", e); + } + } + } + + @Override + public void setGlobalAlert(long alertBytes) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + // silently discard when control disabled + // TODO: eventually migrate to be always enabled + if (!mBandwidthControlEnabled) return; + + final StringBuilder command = new StringBuilder(); + command.append("bandwidth setglobalalert ").append(alertBytes); + + try { + mConnector.doCommand(command.toString()); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon", e); + } + } + + @Override public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); @@ -1375,18 +1456,17 @@ class NetworkManagementService extends INetworkManagementService.Stub { try { String cmd = "resolver setifdns " + iface; for (String s : servers) { - if (s != null && !"0.0.0.0".equals(s) && - !"::".equals(s) && !"0:0:0:0:0:0:0:0".equals(s)) { - cmd += " " + InetAddress.getByName(s).getHostAddress(); + InetAddress a = NetworkUtils.numericToInetAddress(s); + if (a.isAnyLocalAddress() == false) { + cmd += " " + a.getHostAddress(); } } - mConnector.doCommand(cmd); - } catch (UnknownHostException e) { - throw new IllegalStateException("failed to resolve dns address.", e); + } catch (IllegalArgumentException e) { + throw new IllegalStateException("Error setting dnsn for interface", e); } catch (NativeDaemonConnectorException e) { throw new IllegalStateException( - "Error communicating with native deamon to set dns for interface", e); + "Error communicating with native daemon to set dns for interface", e); } } @@ -1412,7 +1492,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { mConnector.doCommand(cmd); } catch (NativeDaemonConnectorException e) { throw new IllegalStateException( - "Error communicating with native deamon to flush interface " + iface, e); + "Error communicating with native daemon to flush interface " + iface, e); } } } diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index 92647e6..d0f8843 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1205,6 +1205,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED | AccessibilityEvent.TYPE_VIEW_SCROLLED; + private static final int RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES = + AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER + | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT; + private int mRetrievalAlowingWindowId; private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) { @@ -1216,9 +1220,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) { final int windowId = event.getWindowId(); final int eventType = event.getEventType(); - if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) != 0) { + if ((eventType & RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES) != 0) { mRetrievalAlowingWindowId = windowId; - } else { + } + if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) { event.setSource(null); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 3389f33..66f88fc 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1821,7 +1821,7 @@ public final class ActivityManagerService extends ActivityManagerNative // An application record is attached to a previous process, // clean it up now. if (DEBUG_PROCESSES) Slog.v(TAG, "App died: " + app); - handleAppDiedLocked(app, true); + handleAppDiedLocked(app, true, true); } } @@ -2658,8 +2658,8 @@ public final class ActivityManagerService extends ActivityManagerNative * to the process. */ private final void handleAppDiedLocked(ProcessRecord app, - boolean restarting) { - cleanUpApplicationRecordLocked(app, restarting, -1); + boolean restarting, boolean allowRestart) { + cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1); if (!restarting) { mLruProcesses.remove(app); } @@ -2791,7 +2791,7 @@ public final class ActivityManagerService extends ActivityManagerNative TAG, "Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder()); boolean doLowMem = app.instrumentationClass == null; - handleAppDiedLocked(app, false); + handleAppDiedLocked(app, false, true); if (doLowMem) { // If there are no longer any background processes running, @@ -3195,7 +3195,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } killPackageProcessesLocked(packageName, pkgUid, - SECONDARY_SERVER_ADJ, false, true); + SECONDARY_SERVER_ADJ, false, true, true); } } finally { Binder.restoreCallingIdentity(callingId); @@ -3358,7 +3358,7 @@ public final class ActivityManagerService extends ActivityManagerNative } private final boolean killPackageProcessesLocked(String packageName, int uid, - int minOomAdj, boolean callerWillRestart, boolean doit) { + int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit) { ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); // Remove all processes this package may have touched: all with the @@ -3369,6 +3369,10 @@ public final class ActivityManagerService extends ActivityManagerNative final int NA = apps.size(); for (int ia=0; ia<NA; ia++) { ProcessRecord app = apps.valueAt(ia); + if (app.persistent) { + // we don't kill persistent processes + continue; + } if (app.removed) { if (doit) { procs.add(app); @@ -3389,7 +3393,7 @@ public final class ActivityManagerService extends ActivityManagerNative int N = procs.size(); for (int i=0; i<N; i++) { - removeProcessLocked(procs.get(i), callerWillRestart); + removeProcessLocked(procs.get(i), callerWillRestart, allowRestart); } return N > 0; } @@ -3419,11 +3423,12 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean didSomething = killPackageProcessesLocked(name, uid, -100, - callerWillRestart, doit); + callerWillRestart, false, doit); for (i=mMainStack.mHistory.size()-1; i>=0; i--) { ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); - if (r.packageName.equals(name)) { + if (r.packageName.equals(name) + && (r.app == null || !r.app.persistent)) { if (!doit) { return true; } @@ -3439,7 +3444,8 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); for (ServiceRecord service : mServices.values()) { - if (service.packageName.equals(name)) { + if (service.packageName.equals(name) + && (service.app == null || !service.app.persistent)) { if (!doit) { return true; } @@ -3471,7 +3477,8 @@ public final class ActivityManagerService extends ActivityManagerNative return didSomething; } - private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart) { + private final boolean removeProcessLocked(ProcessRecord app, + boolean callerWillRestart, boolean allowRestart) { final String name = app.processName; final int uid = app.info.uid; if (DEBUG_PROCESSES) Slog.d( @@ -3490,7 +3497,7 @@ public final class ActivityManagerService extends ActivityManagerNative mPidsSelfLocked.remove(pid); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } - handleAppDiedLocked(app, true); + handleAppDiedLocked(app, true, allowRestart); mLruProcesses.remove(app); Process.killProcess(pid); @@ -3600,7 +3607,7 @@ public final class ActivityManagerService extends ActivityManagerNative // If this application record is still attached to a previous // process, clean it up now. if (app.thread != null) { - handleAppDiedLocked(app, true); + handleAppDiedLocked(app, true, true); } // Tell the process all about itself. @@ -3783,7 +3790,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (badApp) { // todo: Also need to kill application to deal with all // kinds of exceptions. - handleAppDiedLocked(app, false); + handleAppDiedLocked(app, false, true); return false; } @@ -6643,7 +6650,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=procsToKill.size()-1; i>=0; i--) { ProcessRecord proc = procsToKill.get(i); Slog.i(TAG, "Removing system update proc: " + proc); - removeProcessLocked(proc, true); + removeProcessLocked(proc, true, false); } } @@ -6826,10 +6833,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } if (!app.persistent) { - // Don't let services in this process be restarted and potentially - // annoy the user repeatedly. Unless it is persistent, since those - // processes run critical code. - killServicesLocked(app, false); // We don't want to start this process again until the user // explicitly does so... but for persistent process, we really // need to keep it running. If a persistent process is actually @@ -6840,7 +6843,10 @@ public final class ActivityManagerService extends ActivityManagerNative app.bad = true; mProcessCrashTimes.remove(app.info.processName, app.info.uid); app.removed = true; - removeProcessLocked(app, false); + // Don't let services in this process be restarted and potentially + // annoy the user repeatedly. Unless it is persistent, since those + // processes run critical code. + removeProcessLocked(app, false, false); mMainStack.resumeTopActivityLocked(null); return false; } @@ -9120,7 +9126,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Should the service remain running? Note that in the // extreme case of so many attempts to deliver a command - // that it failed, that we also will stop it here. + // that it failed we also will stop it here. if (sr.startRequested && (sr.stopIfKilled || canceled)) { if (sr.pendingStarts.size() == 0) { sr.startRequested = false; @@ -9189,7 +9195,7 @@ public final class ActivityManagerService extends ActivityManagerNative * a process when running in single process mode. */ private final void cleanUpApplicationRecordLocked(ProcessRecord app, - boolean restarting, int index) { + boolean restarting, boolean allowRestart, int index) { if (index >= 0) { mLruProcesses.remove(index); } @@ -9221,7 +9227,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.foregroundActivities = false; app.hasShownUi = false; - killServicesLocked(app, true); + killServicesLocked(app, allowRestart); boolean restart = false; @@ -9238,7 +9244,7 @@ public final class ActivityManagerService extends ActivityManagerNative // See if someone is waiting for this provider... in which // case we don't remove it, but just let it restart. int i = 0; - if (!app.bad) { + if (!app.bad && allowRestart) { for (; i<NL; i++) { if (mLaunchingProviders.get(i) == cpr) { restart = true; @@ -9994,8 +10000,12 @@ public final class ActivityManagerService extends ActivityManagerNative while (it.hasNext()) { ArrayList<ConnectionRecord> c = it.next(); for (int i=0; i<c.size(); i++) { + ConnectionRecord cr = c.get(i); + // There is still a connection to the service that is + // being brought down. Mark it as dead. + cr.serviceDead = true; try { - c.get(i).conn.connected(r.name, null); + cr.conn.connected(r.name, null); } catch (Exception e) { Slog.w(TAG, "Failure disconnecting service " + r.name + " to connection " + c.get(i).conn.asBinder() + @@ -10526,26 +10536,28 @@ public final class ActivityManagerService extends ActivityManagerNative b.intent.apps.remove(b.client); } - if (DEBUG_SERVICE) Slog.v(TAG, "Disconnecting binding " + b.intent - + ": shouldUnbind=" + b.intent.hasBound); - if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0 - && b.intent.hasBound) { - try { - bumpServiceExecutingLocked(s, "unbind"); - updateOomAdjLocked(s.app); - b.intent.hasBound = false; - // Assume the client doesn't want to know about a rebind; - // we will deal with that later if it asks for one. - b.intent.doRebind = false; - s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent()); - } catch (Exception e) { - Slog.w(TAG, "Exception when unbinding service " + s.shortName, e); - serviceDoneExecutingLocked(s, true); + if (!c.serviceDead) { + if (DEBUG_SERVICE) Slog.v(TAG, "Disconnecting binding " + b.intent + + ": shouldUnbind=" + b.intent.hasBound); + if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0 + && b.intent.hasBound) { + try { + bumpServiceExecutingLocked(s, "unbind"); + updateOomAdjLocked(s.app); + b.intent.hasBound = false; + // Assume the client doesn't want to know about a rebind; + // we will deal with that later if it asks for one. + b.intent.doRebind = false; + s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent()); + } catch (Exception e) { + Slog.w(TAG, "Exception when unbinding service " + s.shortName, e); + serviceDoneExecutingLocked(s, true); + } + } + + if ((c.flags&Context.BIND_AUTO_CREATE) != 0) { + bringDownServiceLocked(s, false); } - } - - if ((c.flags&Context.BIND_AUTO_CREATE) != 0) { - bringDownServiceLocked(s, false); } } @@ -12774,6 +12786,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) { ProcessRecord client = cr.binding.client; + int clientAdj = adj; int myHiddenAdj = hiddenAdj; if (myHiddenAdj > client.hiddenAdj) { if (client.hiddenAdj >= VISIBLE_APP_ADJ) { @@ -12782,8 +12795,35 @@ public final class ActivityManagerService extends ActivityManagerNative myHiddenAdj = VISIBLE_APP_ADJ; } } - int clientAdj = computeOomAdjLocked( + clientAdj = computeOomAdjLocked( client, myHiddenAdj, TOP_APP, true); + String adjType = null; + if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) { + // Not doing bind OOM management, so treat + // this guy more like a started service. + if (app.hasShownUi) { + // If this process has shown some UI, let it immediately + // go to the LRU list because it may be pretty heavy with + // UI stuff. We'll tag it with a label just to help + // debug and understand what is going on. + if (adj > clientAdj) { + adjType = "bound-bg-ui-services"; + } + clientAdj = adj; + } else { + if (now >= (s.lastActivity+MAX_SERVICE_INACTIVITY)) { + // This service has not seen activity within + // recent memory, so allow it to drop to the + // LRU list if there is no other reason to keep + // it around. We'll also tag it with a label just + // to help debug and undertand what is going on. + if (adj > clientAdj) { + adjType = "bound-bg-services"; + } + clientAdj = adj; + } + } + } if (adj > clientAdj) { adj = clientAdj >= VISIBLE_APP_ADJ ? clientAdj : VISIBLE_APP_ADJ; @@ -12793,7 +12833,10 @@ public final class ActivityManagerService extends ActivityManagerNative if (client.keeping) { app.keeping = true; } - app.adjType = "service"; + adjType = "service"; + } + if (adjType != null) { + app.adjType = adjType; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_SERVICE_IN_USE; app.adjSource = cr.binding.client; @@ -13413,7 +13456,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Ignore exceptions. } } - cleanUpApplicationRecordLocked(app, false, -1); + cleanUpApplicationRecordLocked(app, false, true, -1); mRemovedProcesses.remove(i); if (app.persistent) { diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java index 22acda9..0106114 100644 --- a/services/java/com/android/server/am/ConnectionRecord.java +++ b/services/java/com/android/server/am/ConnectionRecord.java @@ -32,6 +32,7 @@ class ConnectionRecord { final int clientLabel; // String resource labeling this client. final PendingIntent clientIntent; // How to launch the client. String stringName; // Caching of toString. + boolean serviceDead; // Well is it? void dump(PrintWriter pw, String prefix) { pw.println(prefix + "binding=" + binding); @@ -61,6 +62,9 @@ class ConnectionRecord { sb.append("ConnectionRecord{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); + if (serviceDead) { + sb.append("DEAD "); + } sb.append(binding.service.shortName); sb.append(":@"); sb.append(Integer.toHexString(System.identityHashCode(conn.asBinder()))); diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index a5a6d8e..ce45998 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -202,7 +202,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub { sm.start(); } } else { - if (sm != null) { + if (isUsb(iface)) { + // ignore usb0 down after enabling RNDIS + // we will handle disconnect in interfaceRemoved instead + Log.d(TAG, "ignoring interface down for " + iface); + } else if (sm != null) { sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN); mIfaces.remove(iface); } @@ -237,6 +241,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } public void interfaceAdded(String iface) { + if (DEBUG) Log.d(TAG, "interfaceAdded " + iface); boolean found = false; boolean usb = false; if (isWifi(iface)) { @@ -268,6 +273,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } public void interfaceRemoved(String iface) { + if (DEBUG) Log.d(TAG, "interfaceRemoved " + iface); synchronized (mIfaces) { TetherInterfaceSM sm = mIfaces.get(iface); if (sm == null) { @@ -542,6 +548,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } public int setUsbTethering(boolean enable) { + if (DEBUG) Log.d(TAG, "setUsbTethering(" + enable + ")"); UsbManager usbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE); synchronized (this) { diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java index e3131fe..b38ea13 100644 --- a/services/java/com/android/server/location/GeocoderProxy.java +++ b/services/java/com/android/server/location/GeocoderProxy.java @@ -47,7 +47,9 @@ public class GeocoderProxy { public GeocoderProxy(Context context, String serviceName) { mContext = context; mIntent = new Intent(serviceName); - mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + mContext.bindService(mIntent, mServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND + | Context.BIND_ALLOW_OOM_MANAGEMENT); } /** @@ -58,7 +60,9 @@ public class GeocoderProxy { synchronized (mMutex) { mContext.unbindService(mServiceConnection); mServiceConnection = new Connection(); - mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + mContext.bindService(mIntent, mServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND + | Context.BIND_ALLOW_OOM_MANAGEMENT); } } @@ -67,14 +71,12 @@ public class GeocoderProxy { private IGeocodeProvider mProvider; public void onServiceConnected(ComponentName className, IBinder service) { - Log.d(TAG, "onServiceConnected " + className); synchronized (this) { mProvider = IGeocodeProvider.Stub.asInterface(service); } } public void onServiceDisconnected(ComponentName className) { - Log.d(TAG, "onServiceDisconnected " + className); synchronized (this) { mProvider = null; } diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java index 1a1a170..0bc1664 100644 --- a/services/java/com/android/server/location/LocationProviderProxy.java +++ b/services/java/com/android/server/location/LocationProviderProxy.java @@ -28,7 +28,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; -import android.os.SystemClock; import android.os.WorkSource; import android.util.Log; @@ -65,7 +64,9 @@ public class LocationProviderProxy implements LocationProviderInterface { mName = name; mIntent = new Intent(serviceName); mHandler = handler; - mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + mContext.bindService(mIntent, mServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND + | Context.BIND_ALLOW_OOM_MANAGEMENT); } /** @@ -76,7 +77,9 @@ public class LocationProviderProxy implements LocationProviderInterface { synchronized (mMutex) { mContext.unbindService(mServiceConnection); mServiceConnection = new Connection(); - mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + mContext.bindService(mIntent, mServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND + | Context.BIND_ALLOW_OOM_MANAGEMENT); } } @@ -88,7 +91,6 @@ public class LocationProviderProxy implements LocationProviderInterface { private DummyLocationProvider mCachedAttributes; // synchronized by mMutex public void onServiceConnected(ComponentName className, IBinder service) { - Log.d(TAG, "LocationProviderProxy.onServiceConnected " + className); synchronized (this) { mProvider = ILocationProvider.Stub.asInterface(service); if (mProvider != null) { @@ -98,7 +100,6 @@ public class LocationProviderProxy implements LocationProviderInterface { } public void onServiceDisconnected(ComponentName className) { - Log.d(TAG, "LocationProviderProxy.onServiceDisconnected " + className); synchronized (this) { mProvider = null; } diff --git a/services/java/com/android/server/net/NetworkAlertObserver.java b/services/java/com/android/server/net/NetworkAlertObserver.java new file mode 100644 index 0000000..0d1c3b2 --- /dev/null +++ b/services/java/com/android/server/net/NetworkAlertObserver.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.net; + +import android.net.INetworkManagementEventObserver; + +/** + * @hide + */ +public abstract class NetworkAlertObserver extends INetworkManagementEventObserver.Stub { + @Override + public void interfaceStatusChanged(String iface, boolean up) { + // ignored; interface changes come through ConnectivityService + } + + @Override + public void interfaceRemoved(String iface) { + // ignored; interface changes come through ConnectivityService + } + + @Override + public void interfaceLinkStateChanged(String iface, boolean up) { + // ignored; interface changes come through ConnectivityService + } + + @Override + public void interfaceAdded(String iface) { + // ignored; interface changes come through ConnectivityService + } +} diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 435c394..2e1e69b 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -29,9 +29,8 @@ import static android.net.ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHA import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkPolicy.LIMIT_DISABLED; +import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; -import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_LIMIT; -import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_WARNING; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; @@ -56,6 +55,7 @@ import android.app.IProcessObserver; import android.app.Notification; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -64,6 +64,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.IConnectivityManager; +import android.net.INetworkManagementEventObserver; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; @@ -131,14 +132,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final boolean LOGD = true; private static final boolean LOGV = false; - private static final int VERSION_CURRENT = 1; + private static final int VERSION_INIT = 1; + private static final int VERSION_ADDED_SNOOZE = 2; private static final long KB_IN_BYTES = 1024; private static final long MB_IN_BYTES = KB_IN_BYTES * 1024; private static final long GB_IN_BYTES = MB_IN_BYTES * 1024; - private static final int TYPE_WARNING = 0x1; - private static final int TYPE_LIMIT = 0x2; + // @VisibleForTesting + public static final int TYPE_WARNING = 0x1; + public static final int TYPE_LIMIT = 0x2; + public static final int TYPE_LIMIT_SNOOZED = 0x3; private static final String TAG_POLICY_LIST = "policy-list"; private static final String TAG_NETWORK_POLICY = "network-policy"; @@ -150,6 +154,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final String ATTR_CYCLE_DAY = "cycleDay"; private static final String ATTR_WARNING_BYTES = "warningBytes"; private static final String ATTR_LIMIT_BYTES = "limitBytes"; + private static final String ATTR_LAST_SNOOZE = "lastSnooze"; private static final String ATTR_UID = "uid"; private static final String ATTR_POLICY = "policy"; @@ -162,7 +167,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final IActivityManager mActivityManager; private final IPowerManager mPowerManager; private final INetworkStatsService mNetworkStats; - private final INetworkManagementService mNetworkManagement; + private final INetworkManagementService mNetworkManager; private final TrustedTime mTime; private IConnectivityManager mConnManager; @@ -173,18 +178,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private boolean mScreenOn; private boolean mBackgroundData; - /** Current policy for network templates. */ - private ArrayList<NetworkPolicy> mNetworkPolicy = Lists.newArrayList(); - /** Current derived network rules for ifaces. */ + /** Defined network policies. */ + private HashMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = Maps.newHashMap(); + /** Currently active network rules for ifaces. */ private HashMap<NetworkPolicy, String[]> mNetworkRules = Maps.newHashMap(); - /** Current policy for each UID. */ + /** Defined UID policies. */ private SparseIntArray mUidPolicy = new SparseIntArray(); - /** Current derived rules for each UID. */ + /** Currently derived rules for each UID. */ private SparseIntArray mUidRules = new SparseIntArray(); /** Set of ifaces that are metered. */ private HashSet<String> mMeteredIfaces = Sets.newHashSet(); + /** Set of over-limit templates that have been notified. */ + private HashSet<NetworkTemplate> mOverLimitNotified = Sets.newHashSet(); /** Foreground at both UID and PID granularity. */ private SparseBooleanArray mUidForeground = new SparseBooleanArray(); @@ -202,6 +209,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // TODO: keep whitelist of system-critical services that should never have // rules enforced, such as system, phone, and radio UIDs. + // TODO: migrate notifications to SystemUI + public NetworkPolicyManagerService(Context context, IActivityManager activityManager, IPowerManager powerManager, INetworkStatsService networkStats, INetworkManagementService networkManagement) { @@ -221,7 +230,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mActivityManager = checkNotNull(activityManager, "missing activityManager"); mPowerManager = checkNotNull(powerManager, "missing powerManager"); mNetworkStats = checkNotNull(networkStats, "missing networkStats"); - mNetworkManagement = checkNotNull(networkManagement, "missing networkManagement"); + mNetworkManager = checkNotNull(networkManagement, "missing networkManagement"); mTime = checkNotNull(time, "missing TrustedTime"); mHandlerThread = new HandlerThread(TAG); @@ -256,13 +265,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { Slog.e(TAG, "unable to register IProcessObserver", e); } + try { + mNetworkManager.registerObserver(mAlertObserver); + } catch (RemoteException e) { + // ouch, no alert updates means we fall back to + // ACTION_NETWORK_STATS_UPDATED broadcasts. + Slog.e(TAG, "unable to register INetworkManagementEventObserver", e); + } + // TODO: traverse existing processes to know foreground state, or have // activitymanager dispatch current state when new observer attached. final IntentFilter screenFilter = new IntentFilter(); screenFilter.addAction(Intent.ACTION_SCREEN_ON); screenFilter.addAction(Intent.ACTION_SCREEN_OFF); - mContext.registerReceiver(mScreenReceiver, screenFilter); + mContext.registerReceiver(mScreenReceiver, screenFilter, null, mHandler); // watch for network interfaces to be claimed final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION); @@ -272,7 +289,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(ACTION_PACKAGE_ADDED); packageFilter.addAction(ACTION_UID_REMOVED); - packageFilter.addDataScheme("package"); mContext.registerReceiver(mPackageReceiver, packageFilter, null, mHandler); // listen for stats update events @@ -393,6 +409,31 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { }; /** + * Observer that watches for {@link INetworkManagementService} alerts. + */ + private INetworkManagementEventObserver mAlertObserver = new NetworkAlertObserver() { + @Override + public void limitReached(String limitName, String iface) { + // only someone like NMS should be calling us + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + synchronized (mRulesLock) { + if (mMeteredIfaces.contains(iface)) { + try { + // force stats update to make sure we have numbers that + // caused alert to trigger. + mNetworkStats.forceUpdate(); + } catch (RemoteException e) { + Slog.w(TAG, "problem updating network stats"); + } + + updateNotificationsLocked(); + } + } + } + }; + + /** * Check {@link NetworkPolicy} against current {@link INetworkStatsService} * to show visible notifications as needed. */ @@ -415,42 +456,69 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long start = computeLastCycleBoundary(currentTime, policy); final long end = currentTime; - final long total; + final long totalBytes; try { final NetworkStats stats = mNetworkStats.getSummaryForNetwork( policy.template, start, end); final NetworkStats.Entry entry = stats.getValues(0, null); - total = entry.rxBytes + entry.txBytes; + totalBytes = entry.rxBytes + entry.txBytes; } catch (RemoteException e) { Slog.w(TAG, "problem reading summary for template " + policy.template); continue; } - if (policy.limitBytes != LIMIT_DISABLED && total >= policy.limitBytes) { + if (policy.limitBytes != LIMIT_DISABLED && totalBytes >= policy.limitBytes) { cancelNotification(policy, TYPE_WARNING); - enqueueNotification(policy, TYPE_LIMIT); + + if (policy.lastSnooze >= start) { + cancelNotification(policy, TYPE_LIMIT); + enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes); + } else { + cancelNotification(policy, TYPE_LIMIT_SNOOZED); + enqueueNotification(policy, TYPE_LIMIT, totalBytes); + notifyOverLimitLocked(policy.template); + } + } else { cancelNotification(policy, TYPE_LIMIT); + cancelNotification(policy, TYPE_LIMIT_SNOOZED); + notifyUnderLimitLocked(policy.template); - if (policy.warningBytes != WARNING_DISABLED && total >= policy.warningBytes) { - enqueueNotification(policy, TYPE_WARNING); + if (policy.warningBytes != WARNING_DISABLED && totalBytes >= policy.warningBytes) { + enqueueNotification(policy, TYPE_WARNING, totalBytes); } else { cancelNotification(policy, TYPE_WARNING); } } - } // clear notifications for non-active policies - for (NetworkPolicy policy : mNetworkPolicy) { + for (NetworkPolicy policy : mNetworkPolicy.values()) { if (!mNetworkRules.containsKey(policy)) { cancelNotification(policy, TYPE_WARNING); cancelNotification(policy, TYPE_LIMIT); + cancelNotification(policy, TYPE_LIMIT_SNOOZED); + notifyUnderLimitLocked(policy.template); } } } /** + * Notify that given {@link NetworkTemplate} is over + * {@link NetworkPolicy#limitBytes}, potentially showing dialog to user. + */ + private void notifyOverLimitLocked(NetworkTemplate template) { + if (!mOverLimitNotified.contains(template)) { + mContext.startActivity(buildNetworkOverLimitIntent(template)); + mOverLimitNotified.add(template); + } + } + + private void notifyUnderLimitLocked(NetworkTemplate template) { + mOverLimitNotified.remove(template); + } + + /** * Build unique tag that identifies an active {@link NetworkPolicy} * notification of a specific type, like {@link #TYPE_LIMIT}. */ @@ -462,7 +530,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Show notification for combined {@link NetworkPolicy} and specific type, * like {@link #TYPE_LIMIT}. Okay to call multiple times. */ - private void enqueueNotification(NetworkPolicy policy, int type) { + private void enqueueNotification(NetworkPolicy policy, int type, long totalBytes) { final String tag = buildNotificationTag(policy, type); final Notification.Builder builder = new Notification.Builder(mContext); builder.setOnlyAlertOnce(true); @@ -471,8 +539,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final Resources res = mContext.getResources(); switch (type) { case TYPE_WARNING: { - final String title = res.getString(R.string.data_usage_warning_title); - final String body = res.getString(R.string.data_usage_warning_body, + final CharSequence title = res.getText(R.string.data_usage_warning_title); + final CharSequence body = res.getString(R.string.data_usage_warning_body, Formatter.formatFileSize(mContext, policy.warningBytes)); builder.setSmallIcon(R.drawable.ic_menu_info_details); @@ -480,25 +548,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { builder.setContentTitle(title); builder.setContentText(body); - final Intent intent = new Intent(ACTION_DATA_USAGE_WARNING); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.putExtra(EXTRA_NETWORK_TEMPLATE, policy.template.getMatchRule()); + final Intent intent = buildViewDataUsageIntent(policy.template); builder.setContentIntent(PendingIntent.getActivity( mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); break; } case TYPE_LIMIT: { - final String title; - final String body = res.getString(R.string.data_usage_limit_body); + final CharSequence body = res.getText(R.string.data_usage_limit_body); + + final CharSequence title; switch (policy.template.getMatchRule()) { case MATCH_MOBILE_3G_LOWER: - title = res.getString(R.string.data_usage_3g_limit_title); + title = res.getText(R.string.data_usage_3g_limit_title); break; case MATCH_MOBILE_4G: - title = res.getString(R.string.data_usage_4g_limit_title); + title = res.getText(R.string.data_usage_4g_limit_title); break; default: - title = res.getString(R.string.data_usage_mobile_limit_title); + title = res.getText(R.string.data_usage_mobile_limit_title); break; } @@ -507,9 +574,35 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { builder.setContentTitle(title); builder.setContentText(body); - final Intent intent = new Intent(ACTION_DATA_USAGE_LIMIT); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.putExtra(EXTRA_NETWORK_TEMPLATE, policy.template.getMatchRule()); + final Intent intent = buildNetworkOverLimitIntent(policy.template); + builder.setContentIntent(PendingIntent.getActivity( + mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } + case TYPE_LIMIT_SNOOZED: { + final long overBytes = totalBytes - policy.limitBytes; + final CharSequence body = res.getString(R.string.data_usage_limit_snoozed_body, + Formatter.formatFileSize(mContext, overBytes)); + + final CharSequence title; + switch (policy.template.getMatchRule()) { + case MATCH_MOBILE_3G_LOWER: + title = res.getText(R.string.data_usage_3g_limit_snoozed_title); + break; + case MATCH_MOBILE_4G: + title = res.getText(R.string.data_usage_4g_limit_snoozed_title); + break; + default: + title = res.getText(R.string.data_usage_mobile_limit_snoozed_title); + break; + } + + builder.setSmallIcon(R.drawable.ic_menu_info_details); + builder.setTicker(title); + builder.setContentTitle(title); + builder.setContentText(body); + + final Intent intent = buildViewDataUsageIntent(policy.template); builder.setContentIntent(PendingIntent.getActivity( mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); break; @@ -591,7 +684,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // build list of rules and ifaces to enforce them against mNetworkRules.clear(); final ArrayList<String> ifaceList = Lists.newArrayList(); - for (NetworkPolicy policy : mNetworkPolicy) { + for (NetworkPolicy policy : mNetworkPolicy.values()) { // collect all active ifaces that match this template ifaceList.clear(); @@ -642,11 +735,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + Arrays.toString(ifaces)); } - // TODO: register for warning notification trigger through NMS + final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED; + final boolean hasWarning = policy.warningBytes != WARNING_DISABLED; - if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { - // remaining "quota" is based on usage in current cycle - final long quota = Math.max(0, policy.limitBytes - total); + if (hasLimit || hasWarning) { + final long quotaBytes; + if (hasLimit) { + // remaining "quota" is based on usage in current cycle + quotaBytes = Math.max(0, policy.limitBytes - total); + } else { + // to track warning alert later, use a high quota + quotaBytes = Long.MAX_VALUE; + } if (ifaces.length > 1) { // TODO: switch to shared quota once NMS supports @@ -655,8 +755,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { for (String iface : ifaces) { removeInterfaceQuota(iface); - setInterfaceQuota(iface, quota); - newMeteredIfaces.add(iface); + if (quotaBytes > 0) { + setInterfaceQuota(iface, quotaBytes); + newMeteredIfaces.add(iface); + } + } + } + + if (hasWarning) { + final long alertBytes = Math.max(0, policy.warningBytes - total); + for (String iface : ifaces) { + removeInterfaceAlert(iface); + if (alertBytes > 0) { + setInterfaceAlert(iface, alertBytes); + } } } } @@ -685,7 +797,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // examine to see if any policy is defined for active mobile boolean mobileDefined = false; - for (NetworkPolicy policy : mNetworkPolicy) { + for (NetworkPolicy policy : mNetworkPolicy.values()) { if (policy.template.matches(probeIdent)) { mobileDefined = true; } @@ -704,7 +816,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int cycleDay = time.monthDay; final NetworkTemplate template = buildTemplateMobileAll(subscriberId); - mNetworkPolicy.add(new NetworkPolicy(template, cycleDay, warningBytes, LIMIT_DISABLED)); + mNetworkPolicy.put(template, new NetworkPolicy( + template, cycleDay, warningBytes, LIMIT_DISABLED, SNOOZE_NEVER)); writePolicyLocked(); } } @@ -723,7 +836,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { in.setInput(fis, null); int type; - int version = VERSION_CURRENT; + int version = VERSION_INIT; while ((type = in.next()) != END_DOCUMENT) { final String tag = in.getName(); if (type == START_TAG) { @@ -736,11 +849,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int cycleDay = readIntAttribute(in, ATTR_CYCLE_DAY); final long warningBytes = readLongAttribute(in, ATTR_WARNING_BYTES); final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES); + final long lastSnooze; + if (version >= VERSION_ADDED_SNOOZE) { + lastSnooze = readLongAttribute(in, ATTR_LAST_SNOOZE); + } else { + lastSnooze = SNOOZE_NEVER; + } final NetworkTemplate template = new NetworkTemplate( networkTemplate, subscriberId); - mNetworkPolicy.add( - new NetworkPolicy(template, cycleDay, warningBytes, limitBytes)); + mNetworkPolicy.put(template, new NetworkPolicy( + template, cycleDay, warningBytes, limitBytes, lastSnooze)); } else if (TAG_UID_POLICY.equals(tag)) { final int uid = readIntAttribute(in, ATTR_UID); @@ -778,10 +897,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { out.startDocument(null, true); out.startTag(null, TAG_POLICY_LIST); - writeIntAttribute(out, ATTR_VERSION, VERSION_CURRENT); + writeIntAttribute(out, ATTR_VERSION, VERSION_ADDED_SNOOZE); // write all known network policies - for (NetworkPolicy policy : mNetworkPolicy) { + for (NetworkPolicy policy : mNetworkPolicy.values()) { final NetworkTemplate template = policy.template; out.startTag(null, TAG_NETWORK_POLICY); @@ -793,6 +912,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { writeIntAttribute(out, ATTR_CYCLE_DAY, policy.cycleDay); writeLongAttribute(out, ATTR_WARNING_BYTES, policy.warningBytes); writeLongAttribute(out, ATTR_LIMIT_BYTES, policy.limitBytes); + writeLongAttribute(out, ATTR_LAST_SNOOZE, policy.lastSnooze); out.endTag(null, TAG_NETWORK_POLICY); } @@ -880,7 +1000,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { synchronized (mRulesLock) { mNetworkPolicy.clear(); for (NetworkPolicy policy : policies) { - mNetworkPolicy.add(policy); + mNetworkPolicy.put(policy.template, policy); } updateNetworkRulesLocked(); @@ -895,7 +1015,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, TAG); synchronized (mRulesLock) { - return mNetworkPolicy.toArray(new NetworkPolicy[mNetworkPolicy.size()]); + return mNetworkPolicy.values().toArray(new NetworkPolicy[mNetworkPolicy.size()]); + } + } + + @Override + public void snoozePolicy(NetworkTemplate template) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + // try refreshing time source when stale + if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) { + mTime.forceRefresh(); + } + + final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis() + : System.currentTimeMillis(); + + synchronized (mRulesLock) { + // find and snooze local policy that matches + final NetworkPolicy policy = mNetworkPolicy.get(template); + if (policy == null) { + throw new IllegalArgumentException("unable to find policy for " + template); + } + + policy.lastSnooze = currentTime; + + updateNetworkRulesLocked(); + updateNotificationsLocked(); + writePolicyLocked(); } } @@ -903,9 +1050,23 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { mContext.enforceCallingOrSelfPermission(DUMP, TAG); + final HashSet<String> argSet = new HashSet<String>(); + for (String arg : args) { + argSet.add(arg); + } + synchronized (mRulesLock) { + if (argSet.contains("unsnooze")) { + for (NetworkPolicy policy : mNetworkPolicy.values()) { + policy.lastSnooze = SNOOZE_NEVER; + } + writePolicyLocked(); + fout.println("Wiped snooze timestamps"); + return; + } + fout.println("Network policies:"); - for (NetworkPolicy policy : mNetworkPolicy) { + for (NetworkPolicy policy : mNetworkPolicy.values()) { fout.print(" "); fout.println(policy.toString()); } @@ -1124,9 +1285,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } }; - private void setInterfaceQuota(String iface, long quota) { + private void setInterfaceQuota(String iface, long quotaBytes) { try { - mNetworkManagement.setInterfaceQuota(iface, quota); + mNetworkManager.setInterfaceQuota(iface, quotaBytes); } catch (IllegalStateException e) { Slog.e(TAG, "problem setting interface quota", e); } catch (RemoteException e) { @@ -1136,7 +1297,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private void removeInterfaceQuota(String iface) { try { - mNetworkManagement.removeInterfaceQuota(iface); + mNetworkManager.removeInterfaceQuota(iface); } catch (IllegalStateException e) { Slog.e(TAG, "problem removing interface quota", e); } catch (RemoteException e) { @@ -1144,9 +1305,29 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private void setInterfaceAlert(String iface, long alertBytes) { + try { + mNetworkManager.setInterfaceAlert(iface, alertBytes); + } catch (IllegalStateException e) { + Slog.e(TAG, "problem setting interface alert", e); + } catch (RemoteException e) { + Slog.e(TAG, "problem setting interface alert", e); + } + } + + private void removeInterfaceAlert(String iface) { + try { + mNetworkManager.removeInterfaceAlert(iface); + } catch (IllegalStateException e) { + Slog.e(TAG, "problem removing interface alert", e); + } catch (RemoteException e) { + Slog.e(TAG, "problem removing interface alert", e); + } + } + private void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) { try { - mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces); + mNetworkManager.setUidNetworkRules(uid, rejectOnQuotaInterfaces); } catch (IllegalStateException e) { Slog.e(TAG, "problem setting uid rules", e); } catch (RemoteException e) { @@ -1160,6 +1341,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return telephony.getSubscriberId(); } + private static Intent buildNetworkOverLimitIntent(NetworkTemplate template) { + final Intent intent = new Intent(); + intent.setComponent(new ComponentName( + "com.android.systemui", "com.android.systemui.net.NetworkOverLimitActivity")); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRA_NETWORK_TEMPLATE, template); + return intent; + } + + private static Intent buildViewDataUsageIntent(NetworkTemplate template) { + final Intent intent = new Intent(); + intent.setComponent(new ComponentName( + "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity")); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRA_NETWORK_TEMPLATE, template); + return intent; + } + private static void collectKeys(SparseIntArray source, SparseBooleanArray target) { final int size = source.size(); for (int i = 0; i < size; i++) { @@ -1198,7 +1397,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { return Long.parseLong(value); } catch (NumberFormatException e) { - throw new ProtocolException("problem parsing " + name + "=" + value + " as int"); + throw new ProtocolException("problem parsing " + name + "=" + value + " as long"); } } diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 9eb1179..88e0fa8 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static libcore.io.OsConstants.S_ISLNK; import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; @@ -76,6 +77,7 @@ import android.os.Environment; import android.os.FileObserver; import android.os.FileUtils; import android.os.FileUtils.FileStatus; +import android.os.Debug; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -128,6 +130,9 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; +import libcore.io.ErrnoException; +import libcore.io.Libcore; + /** * Keep track of all those .apks everywhere. * @@ -3338,16 +3343,27 @@ public class PackageManagerService extends IPackageManager.Stub { } else if (nativeLibraryDir.getCanonicalFile().getParent() .equals(dataPathString)) { /* + * Make sure the native library dir isn't a symlink to + * something. If it is, ask installd to remove it and create + * a directory so we can copy to it afterwards. + */ + boolean isSymLink; + try { + isSymLink = S_ISLNK(Libcore.os.lstat(nativeLibraryDir.getPath()).st_mode); + } catch (ErrnoException e) { + // This shouldn't happen, but we'll fail-safe. + isSymLink = true; + } + if (isSymLink) { + mInstaller.unlinkNativeLibraryDirectory(dataPathString); + } + + /* * If this is an internal application or our * nativeLibraryPath points to our data directory, unpack - * the libraries. The native library path pointing to the - * data directory for an application in an ASEC container - * can happen for older apps that existed before an OTA to - * Gingerbread. + * the libraries if necessary. */ - Slog.i(TAG, "Unpacking native libraries for " + path); - mInstaller.unlinkNativeLibraryDirectory(dataPathString); - NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir); + NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir); } else { Slog.i(TAG, "Linking native library dir for " + path); mInstaller.linkNativeLibraryDirectory(dataPathString, @@ -7930,7 +7946,7 @@ public class PackageManagerService extends IPackageManager.Stub { .unlinkNativeLibraryDirectory(pkg.applicationInfo.dataDir) < 0) { returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; } else { - NativeLibraryHelper.copyNativeBinariesLI(new File( + NativeLibraryHelper.copyNativeBinariesIfNeededLI(new File( newCodePath), new File(newNativePath)); } } else { diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index f183f83..11cb555 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -528,10 +528,10 @@ public class UsbDeviceManager { private void updateUsbNotification() { if (mNotificationManager == null || !mUseUsbNotification) return; + int id = NOTIFICATION_NONE; + Resources r = mContext.getResources(); + CharSequence title = null; if (mConnected) { - Resources r = mContext.getResources(); - CharSequence title = null; - int id = NOTIFICATION_NONE; if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) { title = r.getText( com.android.internal.R.string.usb_mtp_notification_title); @@ -550,16 +550,19 @@ public class UsbDeviceManager { com.android.internal.R.string.usb_accessory_notification_title); id = NOTIFICATION_ACCESSORY; } else { - Slog.e(TAG, "No known USB function in updateUsbNotification"); - } - if (id != mUsbNotificationId) { - // clear notification if title needs changing - if (mUsbNotificationId != NOTIFICATION_NONE) { - mNotificationManager.cancel(mUsbNotificationId); - mUsbNotificationId = NOTIFICATION_NONE; + // There is a different notification for USB tethering so we don't need one here + if (!containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_RNDIS)) { + Slog.e(TAG, "No known USB function in updateUsbNotification"); } } - if (mUsbNotificationId == NOTIFICATION_NONE) { + } + if (id != mUsbNotificationId) { + // clear notification if title needs changing + if (mUsbNotificationId != NOTIFICATION_NONE) { + mNotificationManager.cancel(mUsbNotificationId); + mUsbNotificationId = NOTIFICATION_NONE; + } + if (id != NOTIFICATION_NONE) { CharSequence message = r.getText( com.android.internal.R.string.usb_notification_message); @@ -584,10 +587,6 @@ public class UsbDeviceManager { mNotificationManager.notify(id, notification); mUsbNotificationId = id; } - - } else if (mUsbNotificationId != NOTIFICATION_NONE) { - mNotificationManager.cancel(mUsbNotificationId); - mUsbNotificationId = NOTIFICATION_NONE; } } diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java index 8146fca..b37d1c2 100644 --- a/services/java/com/android/server/wm/DragState.java +++ b/services/java/com/android/server/wm/DragState.java @@ -123,8 +123,8 @@ class DragState { // The drag window covers the entire display mDragWindowHandle.frameLeft = 0; mDragWindowHandle.frameTop = 0; - mDragWindowHandle.frameRight = mService.mDisplay.getRealWidth(); - mDragWindowHandle.frameBottom = mService.mDisplay.getRealHeight(); + mDragWindowHandle.frameRight = mService.mCurDisplayWidth; + mDragWindowHandle.frameBottom = mService.mCurDisplayHeight; } } diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java index 3133a19..1d0857b 100644 --- a/services/java/com/android/server/wm/InputManager.java +++ b/services/java/com/android/server/wm/InputManager.java @@ -64,7 +64,8 @@ public class InputManager { private static native void nativeInit(Context context, Callbacks callbacks, MessageQueue messageQueue); private static native void nativeStart(); - private static native void nativeSetDisplaySize(int displayId, int width, int height); + private static native void nativeSetDisplaySize(int displayId, int width, int height, + int externalWidth, int externalHeight); private static native void nativeSetDisplayOrientation(int displayId, int rotation); private static native int nativeGetScanCodeState(int deviceId, int sourceMask, @@ -144,15 +145,17 @@ public class InputManager { updatePointerSpeedFromSettings(); } - public void setDisplaySize(int displayId, int width, int height) { - if (width <= 0 || height <= 0) { + public void setDisplaySize(int displayId, int width, int height, + int externalWidth, int externalHeight) { + if (width <= 0 || height <= 0 || externalWidth <= 0 || externalHeight <= 0) { throw new IllegalArgumentException("Invalid display id or dimensions."); } if (DEBUG) { - Slog.d(TAG, "Setting display #" + displayId + " size to " + width + "x" + height); + Slog.d(TAG, "Setting display #" + displayId + " size to " + width + "x" + height + + " external size " + externalWidth + "x" + externalHeight); } - nativeSetDisplaySize(displayId, width, height); + nativeSetDisplaySize(displayId, width, height, externalWidth, externalHeight); } public void setDisplayOrientation(int displayId, int rotation) { diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java index 8470918..16af151 100644 --- a/services/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java @@ -25,9 +25,7 @@ import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.util.DisplayMetrics; import android.util.Slog; -import android.view.Display; import android.view.Surface; import android.view.SurfaceSession; import android.view.animation.Animation; @@ -41,7 +39,6 @@ class ScreenRotationAnimation { static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200; final Context mContext; - final Display mDisplay; Surface mSurface; BlackFrame mBlackFrame; int mWidth, mHeight; @@ -58,18 +55,14 @@ class ScreenRotationAnimation { final Transformation mEnterTransformation = new Transformation(); boolean mStarted; - final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); final Matrix mSnapshotInitialMatrix = new Matrix(); final Matrix mSnapshotFinalMatrix = new Matrix(); final Matrix mTmpMatrix = new Matrix(); final float[] mTmpFloats = new float[9]; - public ScreenRotationAnimation(Context context, Display display, SurfaceSession session, - boolean inTransaction) { + public ScreenRotationAnimation(Context context, SurfaceSession session, + boolean inTransaction, int originalWidth, int originalHeight, int originalRotation) { mContext = context; - mDisplay = display; - - display.getRealMetrics(mDisplayMetrics); Bitmap screenshot = Surface.screenshot(0, 0); @@ -83,9 +76,9 @@ class ScreenRotationAnimation { mWidth = screenshot.getWidth(); mHeight = screenshot.getHeight(); - mOriginalRotation = display.getRotation(); - mOriginalWidth = mDisplayMetrics.widthPixels; - mOriginalHeight = mDisplayMetrics.heightPixels; + mOriginalRotation = originalRotation; + mOriginalWidth = originalWidth; + mOriginalHeight = originalHeight; if (!inTransaction) { if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, @@ -106,7 +99,7 @@ class ScreenRotationAnimation { WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG, " FREEZE " + mSurface + ": CREATE"); - setRotation(display.getRotation()); + setRotation(originalRotation); if (mSurface != null) { Rect dirty = new Rect(0, 0, mWidth, mHeight); @@ -212,7 +205,7 @@ class ScreenRotationAnimation { * Returns true if animating. */ public boolean dismiss(SurfaceSession session, long maxAnimationDuration, - float animationScale) { + float animationScale, int finalWidth, int finalHeight) { if (mSurface == null) { // Can't do animation. return false; @@ -248,16 +241,12 @@ class ScreenRotationAnimation { break; } - mDisplay.getRealMetrics(mDisplayMetrics); - // Initialize the animations. This is a hack, redefining what "parent" // means to allow supplying the last and next size. In this definition // "%p" is the original (let's call it "previous") size, and "%" is the // screen's current/new size. - mEnterAnimation.initialize(mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, - mOriginalWidth, mOriginalHeight); - mExitAnimation.initialize(mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, - mOriginalWidth, mOriginalHeight); + mEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight); + mExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight); mStarted = false; mExitAnimation.restrictDuration(maxAnimationDuration); @@ -270,10 +259,8 @@ class ScreenRotationAnimation { Surface.openTransaction(); try { - final int w = mDisplayMetrics.widthPixels; - final int h = mDisplayMetrics.heightPixels; - Rect outer = new Rect(-w, -h, w*2, h*2); - Rect inner = new Rect(0, 0, w, h); + Rect outer = new Rect(-finalWidth, -finalHeight, finalWidth * 2, finalHeight * 2); + Rect inner = new Rect(0, 0, finalWidth, finalHeight); mBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER); } catch (Surface.OutOfResourcesException e) { Slog.w(TAG, "Unable to allocate black surface", e); diff --git a/services/java/com/android/server/wm/StrictModeFlash.java b/services/java/com/android/server/wm/StrictModeFlash.java index 71b5952..768d2db 100644 --- a/services/java/com/android/server/wm/StrictModeFlash.java +++ b/services/java/com/android/server/wm/StrictModeFlash.java @@ -38,9 +38,6 @@ class StrictModeFlash { final int mThickness = 20; public StrictModeFlash(Display display, SurfaceSession session) { - final DisplayMetrics dm = new DisplayMetrics(); - display.getRealMetrics(dm); - try { mSurface = new Surface(session, 0, "StrictModeFlash", -1, 1, 1, PixelFormat.TRANSLUCENT, 0); } catch (Surface.OutOfResourcesException e) { diff --git a/services/java/com/android/server/wm/Watermark.java b/services/java/com/android/server/wm/Watermark.java index 375abe5..5497eb4 100644 --- a/services/java/com/android/server/wm/Watermark.java +++ b/services/java/com/android/server/wm/Watermark.java @@ -50,10 +50,7 @@ class Watermark { int mLastDH; boolean mDrawNeeded; - Watermark(Display display, SurfaceSession session, String[] tokens) { - final DisplayMetrics dm = new DisplayMetrics(); - display.getRealMetrics(dm); - + Watermark(DisplayMetrics dm, SurfaceSession session, String[] tokens) { if (false) { Log.i(WindowManagerService.TAG, "*********************** WATERMARK"); for (int i=0; i<tokens.length; i++) { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index be21ac0..f8059f5 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -465,6 +465,7 @@ public class WindowManagerService extends IWindowManager.Stub Display mDisplay; final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics(); final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics(); final DisplayMetrics mCompatDisplayMetrics = new DisplayMetrics(); @@ -5642,15 +5643,14 @@ public class WindowManagerService extends IWindowManager.Stub } config.orientation = orientation; - DisplayMetrics dm = mDisplayMetrics; - mDisplay.getRealMetrics(dm); + // Update real display metrics. + mDisplay.getMetricsWithSize(mRealDisplayMetrics, mCurDisplayWidth, mCurDisplayHeight); - // Override display width and height with what we are computing, - // to be sure they remain consistent. - dm.widthPixels = dm.noncompatWidthPixels = mAppDisplayWidth - = mPolicy.getNonDecorDisplayWidth(mRotation, dw); - dm.heightPixels = dm.noncompatHeightPixels = mAppDisplayHeight - = mPolicy.getNonDecorDisplayHeight(mRotation, dh); + // Update application display metrics. + final DisplayMetrics dm = mDisplayMetrics; + mAppDisplayWidth = mPolicy.getNonDecorDisplayWidth(mRotation, dw); + mAppDisplayHeight = mPolicy.getNonDecorDisplayHeight(mRotation, dh); + mDisplay.getMetricsWithSize(dm, mAppDisplayWidth, mAppDisplayHeight); mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(dm, mCompatDisplayMetrics); @@ -6086,8 +6086,8 @@ public class WindowManagerService extends IWindowManager.Stub } WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); mDisplay = wm.getDefaultDisplay(); - mInitialDisplayWidth = mDisplay.getRealWidth(); - mInitialDisplayHeight = mDisplay.getRealHeight(); + mInitialDisplayWidth = mDisplay.getRawWidth(); + mInitialDisplayHeight = mDisplay.getRawHeight(); int rot = mDisplay.getRotation(); if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) { // If the screen is currently rotated, we need to swap the @@ -6098,7 +6098,9 @@ public class WindowManagerService extends IWindowManager.Stub } mBaseDisplayWidth = mCurDisplayWidth = mAppDisplayWidth = mInitialDisplayWidth; mBaseDisplayHeight = mCurDisplayHeight = mAppDisplayHeight = mInitialDisplayHeight; - mInputManager.setDisplaySize(0, mDisplay.getRawWidth(), mDisplay.getRawHeight()); + mInputManager.setDisplaySize(Display.DEFAULT_DISPLAY, + mDisplay.getRawWidth(), mDisplay.getRawHeight(), + mDisplay.getRawExternalWidth(), mDisplay.getRawExternalHeight()); mPolicy.setInitialDisplaySize(mInitialDisplayWidth, mInitialDisplayHeight); } @@ -6602,6 +6604,13 @@ public class WindowManagerService extends IWindowManager.Stub } } + public void getRealDisplaySize(Point size) { + synchronized(mWindowMap) { + size.x = mCurDisplayWidth; + size.y = mCurDisplayHeight; + } + } + public int getMaximumSizeDimension() { synchronized(mWindowMap) { // Do this based on the raw screen size, until we are smarter. @@ -8687,7 +8696,8 @@ public class WindowManagerService extends IWindowManager.Stub } if (mScreenRotationAnimation == null) { mScreenRotationAnimation = new ScreenRotationAnimation(mContext, - mDisplay, mFxSession, inTransaction); + mFxSession, inTransaction, mCurDisplayWidth, mCurDisplayHeight, + mDisplay.getRotation()); } if (!mScreenRotationAnimation.hasScreenshot()) { Surface.freezeDisplay(0); @@ -8717,7 +8727,7 @@ public class WindowManagerService extends IWindowManager.Stub if (CUSTOM_SCREEN_ROTATION && mScreenRotationAnimation != null && mScreenRotationAnimation.hasScreenshot()) { if (mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION, - mTransitionAnimationScale)) { + mTransitionAnimationScale, mCurDisplayWidth, mCurDisplayHeight)) { requestAnimationLocked(0); } else { mScreenRotationAnimation = null; @@ -8797,7 +8807,7 @@ public class WindowManagerService extends IWindowManager.Stub if (line != null) { String[] toks = line.split("%"); if (toks != null && toks.length > 0) { - mWatermark = new Watermark(mDisplay, mFxSession, toks); + mWatermark = new Watermark(mRealDisplayMetrics, mFxSession, toks); } } } catch (FileNotFoundException e) { @@ -9063,8 +9073,6 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(mCurDisplayWidth); pw.print("x"); pw.print(mCurDisplayHeight); pw.print(" app="); pw.print(mAppDisplayWidth); pw.print("x"); pw.print(mAppDisplayHeight); - pw.print(" real="); pw.print(mDisplay.getRealWidth()); - pw.print("x"); pw.print(mDisplay.getRealHeight()); pw.print(" raw="); pw.print(mDisplay.getRawWidth()); pw.print("x"); pw.println(mDisplay.getRawHeight()); } else { diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index d298ff7..cacb3e7 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -497,8 +497,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { } if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) { - mService.updateWallpaperOffsetLocked(this, mService.mDisplay.getRealWidth(), - mService.mDisplay.getRealHeight(), false); + mService.updateWallpaperOffsetLocked(this, + mService.mAppDisplayWidth, mService.mAppDisplayHeight, false); } if (WindowManagerService.localLOGV) { diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index de9c9d0..3414eea 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -166,7 +166,8 @@ public: void dump(String8& dump); - void setDisplaySize(int32_t displayId, int32_t width, int32_t height); + void setDisplaySize(int32_t displayId, int32_t width, int32_t height, + int32_t externalWidth, int32_t externalHeight); void setDisplayOrientation(int32_t displayId, int32_t orientation); status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, @@ -181,7 +182,7 @@ public: /* --- InputReaderPolicyInterface implementation --- */ - virtual bool getDisplayInfo(int32_t displayId, + virtual bool getDisplayInfo(int32_t displayId, bool external, int32_t* width, int32_t* height, int32_t* orientation); virtual void getReaderConfiguration(InputReaderConfiguration* outConfig); virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId); @@ -221,7 +222,8 @@ private: Mutex mLock; struct Locked { // Display size information. - int32_t displayWidth, displayHeight; // -1 when initialized + int32_t displayWidth, displayHeight; // -1 when not initialized + int32_t displayExternalWidth, displayExternalHeight; // -1 when not initialized int32_t displayOrientation; // System UI visibility. @@ -269,6 +271,8 @@ NativeInputManager::NativeInputManager(jobject contextObj, AutoMutex _l(mLock); mLocked.displayWidth = -1; mLocked.displayHeight = -1; + mLocked.displayExternalWidth = -1; + mLocked.displayExternalHeight = -1; mLocked.displayOrientation = ROTATION_0; mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; @@ -305,22 +309,24 @@ bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const c return false; } -void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_t height) { +void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_t height, + int32_t externalWidth, int32_t externalHeight) { if (displayId == 0) { { // acquire lock AutoMutex _l(mLock); - if (mLocked.displayWidth == width && mLocked.displayHeight == height) { - return; - } - - mLocked.displayWidth = width; - mLocked.displayHeight = height; + if (mLocked.displayWidth != width || mLocked.displayHeight != height) { + mLocked.displayWidth = width; + mLocked.displayHeight = height; - sp<PointerController> controller = mLocked.pointerController.promote(); - if (controller != NULL) { - controller->setDisplaySize(width, height); + sp<PointerController> controller = mLocked.pointerController.promote(); + if (controller != NULL) { + controller->setDisplaySize(width, height); + } } + + mLocked.displayExternalWidth = externalWidth; + mLocked.displayExternalHeight = externalHeight; } // release lock } } @@ -352,7 +358,7 @@ status_t NativeInputManager::unregisterInputChannel(JNIEnv* env, return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel); } -bool NativeInputManager::getDisplayInfo(int32_t displayId, +bool NativeInputManager::getDisplayInfo(int32_t displayId, bool external, int32_t* width, int32_t* height, int32_t* orientation) { bool result = false; if (displayId == 0) { @@ -360,10 +366,10 @@ bool NativeInputManager::getDisplayInfo(int32_t displayId, if (mLocked.displayWidth > 0 && mLocked.displayHeight > 0) { if (width) { - *width = mLocked.displayWidth; + *width = external ? mLocked.displayExternalWidth : mLocked.displayWidth; } if (height) { - *height = mLocked.displayHeight; + *height = external ? mLocked.displayExternalHeight : mLocked.displayHeight; } if (orientation) { *orientation = mLocked.displayOrientation; @@ -952,7 +958,7 @@ static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) { } static void android_server_InputManager_nativeSetDisplaySize(JNIEnv* env, jclass clazz, - jint displayId, jint width, jint height) { + jint displayId, jint width, jint height, jint externalWidth, jint externalHeight) { if (checkInputManagerUnitialized(env)) { return; } @@ -961,7 +967,7 @@ static void android_server_InputManager_nativeSetDisplaySize(JNIEnv* env, jclass // to be passed in like this, not sure which is better but leaving it like this // keeps the window manager in direct control of when display transitions propagate down // to the input dispatcher - gNativeInputManager->setDisplaySize(displayId, width, height); + gNativeInputManager->setDisplaySize(displayId, width, height, externalWidth, externalHeight); } static void android_server_InputManager_nativeSetDisplayOrientation(JNIEnv* env, jclass clazz, @@ -1291,7 +1297,7 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) android_server_InputManager_nativeInit }, { "nativeStart", "()V", (void*) android_server_InputManager_nativeStart }, - { "nativeSetDisplaySize", "(III)V", + { "nativeSetDisplaySize", "(IIIII)V", (void*) android_server_InputManager_nativeSetDisplaySize }, { "nativeSetDisplayOrientation", "(II)V", (void*) android_server_InputManager_nativeSetDisplayOrientation }, diff --git a/services/powermanager/Android.mk b/services/powermanager/Android.mk new file mode 100644 index 0000000..d98b2da --- /dev/null +++ b/services/powermanager/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + IPowerManager.cpp + +LOCAL_SHARED_LIBRARIES := \ + libutils \ + libbinder + +LOCAL_MODULE:= libpowermanager + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp new file mode 100644 index 0000000..a0f19d4 --- /dev/null +++ b/services/powermanager/IPowerManager.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "IPowerManager" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include <stdint.h> +#include <sys/types.h> + +#include <binder/Parcel.h> + +#include <powermanager/IPowerManager.h> + +namespace android { + +// must be kept in sync with IPowerManager.aidl +enum { + ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION, + RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 4, +}; + +class BpPowerManager : public BpInterface<IPowerManager> +{ +public: + BpPowerManager(const sp<IBinder>& impl) + : BpInterface<IPowerManager>(impl) + { + } + + virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag) + { + Parcel data, reply; + data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); + + data.writeInt32(flags); + data.writeStrongBinder(lock); + data.writeString16(tag); + // no WorkSource passed + data.writeInt32(0); + return remote()->transact(ACQUIRE_WAKE_LOCK, data, &reply); + } + + virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags) + { + Parcel data, reply; + data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); + data.writeStrongBinder(lock); + data.writeInt32(flags); + return remote()->transact(RELEASE_WAKE_LOCK, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(PowerManager, "android.os.IPowerManager"); + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index e0268fa..7d6a14d 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -352,12 +352,13 @@ uint32_t Layer::doTransaction(uint32_t flags) if (sizeChanged) { // the size changed, we need to ask our client to request a new buffer LOGD_IF(DEBUG_RESIZE, + "doTransaction: " "resize (layer=%p), requested (%dx%d), drawing (%d,%d), " - "fixedSize=%d", + "scalingMode=%d", this, int(temp.requested_w), int(temp.requested_h), int(front.requested_w), int(front.requested_h), - isFixedSize()); + mCurrentScalingMode); if (!isFixedSize()) { // we're being resized and there is a freeze display request, @@ -492,6 +493,14 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) // we now have the correct size, unfreeze the screen mFreezeLock.clear(); } + + LOGD_IF(DEBUG_RESIZE, + "lockPageFlip : " + " (layer=%p), buffer (%ux%u, tr=%02x), " + "requested (%dx%d)", + this, + bufWidth, bufHeight, mCurrentTransform, + front.requested_w, front.requested_h); } } } diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index ee5f3f5..1620405 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -29,6 +29,7 @@ <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /> <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 504ba42..aab09ca 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -20,6 +20,7 @@ import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; @@ -27,6 +28,10 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import static android.net.NetworkPolicyManager.computeLastCycleBoundary; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; +import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT; +import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED; +import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING; import static org.easymock.EasyMock.anyInt; import static org.easymock.EasyMock.aryEq; import static org.easymock.EasyMock.capture; @@ -39,12 +44,14 @@ import static org.easymock.EasyMock.isA; import android.app.IActivityManager; import android.app.INotificationManager; import android.app.IProcessObserver; +import android.app.Notification; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; import android.net.ConnectivityManager; import android.net.IConnectivityManager; +import android.net.INetworkManagementEventObserver; import android.net.INetworkPolicyListener; import android.net.INetworkStatsService; import android.net.LinkProperties; @@ -95,7 +102,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { private IActivityManager mActivityManager; private IPowerManager mPowerManager; private INetworkStatsService mStatsService; - private INetworkManagementService mNetworkManagement; + private INetworkManagementService mNetworkManager; private INetworkPolicyListener mPolicyListener; private TrustedTime mTime; private IConnectivityManager mConnManager; @@ -103,6 +110,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { private NetworkPolicyManagerService mService; private IProcessObserver mProcessObserver; + private INetworkManagementEventObserver mNetworkObserver; private Binder mStubBinder = new Binder(); @@ -141,6 +149,11 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { } }; } + + @Override + public void startActivity(Intent intent) { + // ignored + } }; mPolicyDir = getContext().getFilesDir(); @@ -151,7 +164,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { mActivityManager = createMock(IActivityManager.class); mPowerManager = createMock(IPowerManager.class); mStatsService = createMock(INetworkStatsService.class); - mNetworkManagement = createMock(INetworkManagementService.class); + mNetworkManager = createMock(INetworkManagementService.class); mPolicyListener = createMock(INetworkPolicyListener.class); mTime = createMock(TrustedTime.class); mConnManager = createMock(IConnectivityManager.class); @@ -159,7 +172,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { mService = new NetworkPolicyManagerService( mServiceContext, mActivityManager, mPowerManager, mStatsService, - mNetworkManagement, mTime, mPolicyDir); + mNetworkManager, mTime, mPolicyDir); mService.bindConnectivityManager(mConnManager); mService.bindNotificationManager(mNotifManager); @@ -169,11 +182,17 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { mService.registerListener(mPolicyListener); verifyAndReset(); - // catch the registered IProcessObserver during systemReady() + // catch IProcessObserver during systemReady() final Capture<IProcessObserver> processObserver = new Capture<IProcessObserver>(); mActivityManager.registerProcessObserver(capture(processObserver)); expectLastCall().atLeastOnce(); + // catch INetworkManagementEventObserver during systemReady() + final Capture<INetworkManagementEventObserver> networkObserver = new Capture< + INetworkManagementEventObserver>(); + mNetworkManager.registerObserver(capture(networkObserver)); + expectLastCall().atLeastOnce(); + // expect to answer screen status during systemReady() expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce(); expectTime(System.currentTimeMillis()); @@ -186,6 +205,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { verifyAndReset(); mProcessObserver = processObserver.getValue(); + mNetworkObserver = networkObserver.getValue(); } @@ -382,7 +402,8 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { final long currentTime = parseTime("2007-11-14T00:00:00.000Z"); final long expectedCycle = parseTime("2007-11-05T00:00:00.000Z"); - final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 5, 1024L, 1024L); + final NetworkPolicy policy = new NetworkPolicy( + sTemplateWifi, 5, 1024L, 1024L, SNOOZE_NEVER); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -392,7 +413,8 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { final long currentTime = parseTime("2007-11-14T00:00:00.000Z"); final long expectedCycle = parseTime("2007-10-20T00:00:00.000Z"); - final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 20, 1024L, 1024L); + final NetworkPolicy policy = new NetworkPolicy( + sTemplateWifi, 20, 1024L, 1024L, SNOOZE_NEVER); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -402,7 +424,8 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { final long currentTime = parseTime("2007-02-14T00:00:00.000Z"); final long expectedCycle = parseTime("2007-01-30T00:00:00.000Z"); - final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 30, 1024L, 1024L); + final NetworkPolicy policy = new NetworkPolicy( + sTemplateWifi, 30, 1024L, 1024L, SNOOZE_NEVER); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -412,7 +435,8 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { final long currentTime = parseTime("2007-03-14T00:00:00.000Z"); final long expectedCycle = parseTime("2007-03-01T00:00:00.000Z"); - final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 30, 1024L, 1024L); + final NetworkPolicy policy = new NetworkPolicy( + sTemplateWifi, 30, 1024L, 1024L, SNOOZE_NEVER); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -432,6 +456,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { state = new NetworkState[] { buildWifi() }; expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); expectTime(TIME_MAR_10 + elapsedRealtime); + expectClearNotifications(); future = expectMeteredIfacesChanged(); replay(); @@ -453,12 +478,14 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { // TODO: consider making strongly ordered mock expectRemoveInterfaceQuota(TEST_IFACE); expectSetInterfaceQuota(TEST_IFACE, 1536L); + expectRemoveInterfaceAlert(TEST_IFACE); + expectSetInterfaceAlert(TEST_IFACE, 512L); expectClearNotifications(); future = expectMeteredIfacesChanged(TEST_IFACE); replay(); - setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L)); + setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L, SNOOZE_NEVER)); future.get(); verifyAndReset(); } @@ -485,6 +512,131 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { verifyAndReset(); } + public void testOverWarningLimitNotification() throws Exception { + long elapsedRealtime = 0; + long currentTime = 0; + NetworkState[] state = null; + NetworkStats stats = null; + Future<Void> future; + Capture<String> tag; + + final long TIME_FEB_15 = 1171497600000L; + final long TIME_MAR_10 = 1173484800000L; + final int CYCLE_DAY = 15; + + // assign wifi policy + elapsedRealtime = 0; + currentTime = TIME_MAR_10 + elapsedRealtime; + state = new NetworkState[] {}; + + { + expectTime(currentTime); + expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); + + expectClearNotifications(); + future = expectMeteredIfacesChanged(); + + replay(); + setNetworkPolicies( + new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L, SNOOZE_NEVER)); + future.get(); + verifyAndReset(); + } + + // bring up wifi network + elapsedRealtime += MINUTE_IN_MILLIS; + currentTime = TIME_MAR_10 + elapsedRealtime; + stats = new NetworkStats(elapsedRealtime, 1) + .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L); + state = new NetworkState[] { buildWifi() }; + + { + expectTime(currentTime); + expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); + expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime)) + .andReturn(stats).atLeastOnce(); + + expectRemoveInterfaceQuota(TEST_IFACE); + expectSetInterfaceQuota(TEST_IFACE, 2048L); + expectRemoveInterfaceAlert(TEST_IFACE); + expectSetInterfaceAlert(TEST_IFACE, 1024L); + + expectClearNotifications(); + future = expectMeteredIfacesChanged(TEST_IFACE); + + replay(); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + future.get(); + verifyAndReset(); + } + + // go over warning, which should kick notification + elapsedRealtime += MINUTE_IN_MILLIS; + currentTime = TIME_MAR_10 + elapsedRealtime; + stats = new NetworkStats(elapsedRealtime, 1) + .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 1536L, 15L, 0L, 0L); + + { + expectTime(currentTime); + expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime)) + .andReturn(stats).atLeastOnce(); + + expectForceUpdate(); + expectClearNotifications(); + tag = expectEnqueueNotification(); + + replay(); + mNetworkObserver.limitReached(null, TEST_IFACE); + assertNotificationType(TYPE_WARNING, tag.getValue()); + verifyAndReset(); + } + + // go over limit, which should kick notification and dialog + elapsedRealtime += MINUTE_IN_MILLIS; + currentTime = TIME_MAR_10 + elapsedRealtime; + stats = new NetworkStats(elapsedRealtime, 1) + .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 5120L, 512L, 0L, 0L); + + { + expectTime(currentTime); + expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime)) + .andReturn(stats).atLeastOnce(); + + expectForceUpdate(); + expectClearNotifications(); + tag = expectEnqueueNotification(); + + replay(); + mNetworkObserver.limitReached(null, TEST_IFACE); + assertNotificationType(TYPE_LIMIT, tag.getValue()); + verifyAndReset(); + } + + // now snooze policy, which should remove quota + elapsedRealtime += MINUTE_IN_MILLIS; + currentTime = TIME_MAR_10 + elapsedRealtime; + + { + expectTime(currentTime); + expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); + expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime)) + .andReturn(stats).atLeastOnce(); + + expectRemoveInterfaceQuota(TEST_IFACE); + expectRemoveInterfaceAlert(TEST_IFACE); + + expectClearNotifications(); + tag = expectEnqueueNotification(); + future = expectMeteredIfacesChanged(); + + replay(); + mService.snoozePolicy(sTemplateWifi); + future.get(); + assertNotificationType(TYPE_LIMIT_SNOOZED, tag.getValue()); + verifyAndReset(); + } + } + private static long parseTime(String time) { final Time result = new Time(); result.parse3339(time); @@ -511,24 +663,46 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes(); } + private void expectForceUpdate() throws Exception { + mStatsService.forceUpdate(); + expectLastCall().atLeastOnce(); + } + private void expectClearNotifications() throws Exception { mNotifManager.cancelNotificationWithTag(isA(String.class), isA(String.class), anyInt()); expectLastCall().anyTimes(); } - private void expectSetInterfaceQuota(String iface, long quota) throws Exception { - mNetworkManagement.setInterfaceQuota(iface, quota); + private Capture<String> expectEnqueueNotification() throws Exception { + final Capture<String> tag = new Capture<String>(); + mNotifManager.enqueueNotificationWithTag(isA(String.class), capture(tag), anyInt(), + isA(Notification.class), isA(int[].class)); + return tag; + } + + private void expectSetInterfaceQuota(String iface, long quotaBytes) throws Exception { + mNetworkManager.setInterfaceQuota(iface, quotaBytes); expectLastCall().atLeastOnce(); } private void expectRemoveInterfaceQuota(String iface) throws Exception { - mNetworkManagement.removeInterfaceQuota(iface); + mNetworkManager.removeInterfaceQuota(iface); + expectLastCall().atLeastOnce(); + } + + private void expectSetInterfaceAlert(String iface, long alertBytes) throws Exception { + mNetworkManager.setInterfaceAlert(iface, alertBytes); + expectLastCall().atLeastOnce(); + } + + private void expectRemoveInterfaceAlert(String iface) throws Exception { + mNetworkManager.removeInterfaceAlert(iface); expectLastCall().atLeastOnce(); } private void expectSetUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) throws Exception { - mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces); + mNetworkManager.setUidNetworkRules(uid, rejectOnQuotaInterfaces); expectLastCall().atLeastOnce(); } @@ -563,15 +737,20 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { } } + private static void assertNotificationType(int expected, String actualTag) { + assertEquals( + Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1)); + } + private void replay() { EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener, - mNetworkManagement, mTime, mConnManager, mNotifManager); + mNetworkManager, mTime, mConnManager, mNotifManager); } private void verifyAndReset() { EasyMock.verify(mActivityManager, mPowerManager, mStatsService, mPolicyListener, - mNetworkManagement, mTime, mConnManager, mNotifManager); + mNetworkManager, mTime, mConnManager, mNotifManager); EasyMock.reset(mActivityManager, mPowerManager, mStatsService, mPolicyListener, - mNetworkManagement, mTime, mConnManager, mNotifManager); + mNetworkManager, mTime, mConnManager, mNotifManager); } } |