diff options
Diffstat (limited to 'services')
78 files changed, 7811 insertions, 4811 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 22ecc54..69a4adc 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -120,12 +120,4 @@ ifeq ($(TARGET_SIMULATOR),true) endif endif -ifeq ($(BOARD_USE_LVMX),true) - LOCAL_CFLAGS += -DLVMX - LOCAL_C_INCLUDES += vendor/nxp - LOCAL_STATIC_LIBRARIES += liblifevibes - LOCAL_SHARED_LIBRARIES += liblvmxservice -# LOCAL_SHARED_LIBRARIES += liblvmxipc -endif - include $(BUILD_SHARED_LIBRARY) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 4ec16c1..a07ebfc 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -47,10 +47,6 @@ #include "A2dpAudioInterface.h" #endif -#ifdef LVMX -#include "lifevibes.h" -#endif - #include <media/EffectsFactoryApi.h> #include <media/EffectVisualizerApi.h> @@ -149,10 +145,6 @@ AudioFlinger::AudioFlinger() } else { LOGE("Couldn't even initialize the stubbed audio hardware!"); } -#ifdef LVMX - LifeVibes::init(); - mLifeVibesClientPid = -1; -#endif } AudioFlinger::~AudioFlinger() @@ -485,9 +477,6 @@ status_t AudioFlinger::setMode(int mode) mMode = mode; for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) mPlaybackThreads.valueAt(i)->setMode(mode); -#ifdef LVMX - LifeVibes::setMode(mode); -#endif } return ret; @@ -635,39 +624,11 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) return PERMISSION_DENIED; } -#ifdef LVMX - AudioParameter param = AudioParameter(keyValuePairs); - LifeVibes::setParameters(ioHandle,keyValuePairs); - String8 key = String8(AudioParameter::keyRouting); - int device; - if (NO_ERROR != param.getInt(key, device)) { - device = -1; - } - - key = String8(LifevibesTag); - String8 value; - int musicEnabled = -1; - if (NO_ERROR == param.get(key, value)) { - if (value == LifevibesEnable) { - mLifeVibesClientPid = IPCThreadState::self()->getCallingPid(); - musicEnabled = 1; - } else if (value == LifevibesDisable) { - mLifeVibesClientPid = -1; - musicEnabled = 0; - } - } -#endif - // ioHandle == 0 means the parameters are global to the audio hardware interface if (ioHandle == 0) { AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_SET_PARAMETER; result = mAudioHardware->setParameters(keyValuePairs); -#ifdef LVMX - if (musicEnabled != -1) { - LifeVibes::enableMusic((bool) musicEnabled); - } -#endif mHardwareStatus = AUDIO_HW_IDLE; return result; } @@ -684,11 +645,6 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) } if (thread != NULL) { result = thread->setParameters(keyValuePairs); -#ifdef LVMX - if ((NO_ERROR == result) && (device != -1)) { - LifeVibes::setDevice(LifeVibes::threadIdToAudioOutputType(thread->id()), device); - } -#endif return result; } return BAD_VALUE; @@ -802,13 +758,6 @@ void AudioFlinger::removeNotificationClient(pid_t pid) if (index >= 0) { sp <NotificationClient> client = mNotificationClients.valueFor(pid); LOGV("removeNotificationClient() %p, pid %d", client.get(), pid); -#ifdef LVMX - if (pid == mLifeVibesClientPid) { - LOGV("Disabling lifevibes"); - LifeVibes::enableMusic(false); - mLifeVibesClientPid = -1; - } -#endif mNotificationClients.removeItem(pid); } } @@ -1214,24 +1163,12 @@ uint32_t AudioFlinger::PlaybackThread::latency() const status_t AudioFlinger::PlaybackThread::setMasterVolume(float value) { -#ifdef LVMX - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { - LifeVibes::setMasterVolume(audioOutputType, value); - } -#endif mMasterVolume = value; return NO_ERROR; } status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted) { -#ifdef LVMX - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { - LifeVibes::setMasterMute(audioOutputType, muted); - } -#endif mMasterMute = muted; return NO_ERROR; } @@ -1248,24 +1185,12 @@ bool AudioFlinger::PlaybackThread::masterMute() const status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value) { -#ifdef LVMX - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { - LifeVibes::setStreamVolume(audioOutputType, stream, value); - } -#endif mStreamTypes[stream].volume = value; return NO_ERROR; } status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted) { -#ifdef LVMX - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { - LifeVibes::setStreamMute(audioOutputType, stream, muted); - } -#endif mStreamTypes[stream].mute = muted; return NO_ERROR; } @@ -1593,12 +1518,6 @@ bool AudioFlinger::MixerThread::threadLoop() } // enable changes in effect chain unlockEffectChains(effectChains); -#ifdef LVMX - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { - LifeVibes::process(audioOutputType, mMixBuffer, mixBufferSize); - } -#endif mLastWriteTime = systemTime(); mInWrite = true; mBytesWritten += mixBufferSize; @@ -1661,24 +1580,6 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track if (masterMute) { masterVolume = 0; } -#ifdef LVMX - bool tracksConnectedChanged = false; - bool stateChanged = false; - - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) - { - int activeTypes = 0; - for (size_t i=0 ; i<count ; i++) { - sp<Track> t = activeTracks[i].promote(); - if (t == 0) continue; - Track* const track = t.get(); - int iTracktype=track->type(); - activeTypes |= 1<<track->type(); - } - LifeVibes::computeVolumes(audioOutputType, activeTypes, tracksConnectedChanged, stateChanged, masterVolume, masterMute); - } -#endif // Delegate master volume control to effect in output mix effect chain if needed sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX); if (chain != 0) { @@ -1728,6 +1629,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track track->mState = TrackBase::ACTIVE; param = AudioMixer::RAMP_VOLUME; } + mAudioMixer->setParameter(AudioMixer::RESAMPLE, AudioMixer::RESET, NULL); } else if (cblk->server != 0) { // If the track is stopped before the first frame was mixed, // do not apply ramp @@ -1746,17 +1648,6 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track // read original volumes with volume control float typeVolume = mStreamTypes[track->type()].volume; -#ifdef LVMX - bool streamMute=false; - // read the volume from the LivesVibes audio engine. - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) - { - LifeVibes::getStreamVolumes(audioOutputType, track->type(), &typeVolume, &streamMute); - if (streamMute) { - typeVolume = 0; - } - } -#endif float v = masterVolume * typeVolume; vl = (uint32_t)(v * cblk->volume[0]) << 12; vr = (uint32_t)(v * cblk->volume[1]) << 12; @@ -1789,14 +1680,6 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track if (va > MAX_GAIN_INT) va = MAX_GAIN_INT; aux = int16_t(va); -#ifdef LVMX - if ( tracksConnectedChanged || stateChanged ) - { - // only do the ramp when the volume is changed by the user / application - param = AudioMixer::VOLUME; - } -#endif - // XXX: these things DON'T need to be done each time mAudioMixer->setBufferProvider(track); mAudioMixer->enable(AudioMixer::MIXING); @@ -3973,9 +3856,12 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac mActiveTrack.clear(); return status; } - mActiveTrack->mState = TrackBase::RESUMING; mRsmpInIndex = mFrameCount; mBytesRead = 0; + if (mResampler != NULL) { + mResampler->reset(); + } + mActiveTrack->mState = TrackBase::RESUMING; // signal thread to start LOGV("Signal record thread"); mWaitWorkCV.signal(); @@ -4292,18 +4178,6 @@ int AudioFlinger::openOutput(uint32_t *pDevices, } else { thread = new MixerThread(this, output, id, *pDevices); LOGV("openOutput() created mixer output: ID %d thread %p", id, thread); - -#ifdef LVMX - unsigned bitsPerSample = - (format == AudioSystem::PCM_16_BIT) ? 16 : - ((format == AudioSystem::PCM_8_BIT) ? 8 : 0); - unsigned channelCount = (channels == AudioSystem::CHANNEL_OUT_STEREO) ? 2 : 1; - int audioOutputType = LifeVibes::threadIdToAudioOutputType(thread->id()); - - LifeVibes::init_aot(audioOutputType, samplingRate, bitsPerSample, channelCount); - LifeVibes::setDevice(audioOutputType, *pDevices); -#endif - } mPlaybackThreads.add(id, thread); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 81f2eb4..ec3d202 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -1181,9 +1181,6 @@ private: DefaultKeyedVector< pid_t, sp<NotificationClient> > mNotificationClients; volatile int32_t mNextUniqueId; -#ifdef LVMX - int mLifeVibesClientPid; -#endif uint32_t mMode; }; diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 433f1f7..50dcda7 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -220,6 +220,12 @@ status_t AudioMixer::setParameter(int target, int name, void *value) return NO_ERROR; } } + if (name == RESET) { + track_t& track = mState.tracks[ mActiveTrack ]; + track.resetResampler(); + invalidateState(1<<mActiveTrack); + return NO_ERROR; + } break; case RAMP_VOLUME: case VOLUME: @@ -289,6 +295,13 @@ bool AudioMixer::track_t::doesResample() const return resampler != 0; } +void AudioMixer::track_t::resetResampler() +{ + if (resampler != 0) { + resampler->reset(); + } +} + inline void AudioMixer::track_t::adjustVolumeRamp(bool aux) { diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index aee3e17..88408a7 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -67,6 +67,7 @@ public: AUX_BUFFER = 0x4003, // for TARGET RESAMPLE SAMPLE_RATE = 0x4100, + RESET = 0x4101, // for TARGET VOLUME (8 channels max) VOLUME0 = 0x4200, VOLUME1 = 0x4201, @@ -163,6 +164,7 @@ private: bool setResampler(uint32_t sampleRate, uint32_t devSampleRate); bool doesResample() const; + void resetResampler(); void adjustVolumeRamp(bool aux); }; diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp index afa9acc..bfc80db 100644 --- a/services/audioflinger/AudioPolicyManagerBase.cpp +++ b/services/audioflinger/AudioPolicyManagerBase.cpp @@ -19,6 +19,7 @@ #include <utils/Log.h> #include <hardware_legacy/AudioPolicyManagerBase.h> #include <media/mediarecorder.h> +#include <math.h> namespace android { @@ -609,7 +610,7 @@ status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output, // store time at which the stream was stopped - see isStreamActive() outputDesc->mStopTime[stream] = systemTime(); - setOutputDevice(output, getNewDevice(output)); + setOutputDevice(output, getNewDevice(output), false, outputDesc->mLatency*2); #ifdef WITH_A2DP if (mA2dpOutput != 0 && !a2dpUsedForSonification() && @@ -1030,6 +1031,8 @@ AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clien mForceUse[i] = AudioSystem::FORCE_NONE; } + initializeVolumeCurves(); + // devices available by default are speaker, ear piece and microphone mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE | AudioSystem::DEVICE_OUT_SPEAKER; @@ -1540,6 +1543,20 @@ uint32_t AudioPolicyManagerBase::getStrategyForStream(AudioSystem::stream_type s return (uint32_t)getStrategy(stream); } +uint32_t AudioPolicyManagerBase::getDevicesForStream(AudioSystem::stream_type stream) { + uint32_t devices; + // By checking the range of stream before calling getStrategy, we avoid + // getStrategy's behavior for invalid streams. getStrategy would do a LOGE + // and then return STRATEGY_MEDIA, but we want to return the empty set. + if (stream < (AudioSystem::stream_type) 0 || stream >= AudioSystem::NUM_STREAM_TYPES) { + devices = 0; + } else { + AudioPolicyManagerBase::routing_strategy strategy = getStrategy(stream); + devices = getDeviceForStrategy(strategy, true); + } + return devices; +} + AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy( AudioSystem::stream_type stream) { // stream to strategy mapping @@ -1605,12 +1622,6 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, if (device) break; device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; if (device) break; - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; - if (device) break; - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; - if (device) break; - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; - if (device) break; #ifdef WITH_A2DP // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP if (!isInCall()) { @@ -1620,6 +1631,12 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, if (device) break; } #endif + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE; if (device == 0) { LOGE("getDeviceForStrategy() earpiece device not found"); @@ -1627,12 +1644,6 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, break; case AudioSystem::FORCE_SPEAKER: - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; - if (device) break; - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; - if (device) break; - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; - if (device) break; #ifdef WITH_A2DP // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to // A2DP speaker when forcing to speaker output @@ -1641,6 +1652,12 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, if (device) break; } #endif + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; if (device == 0) { LOGE("getDeviceForStrategy() speaker device not found"); @@ -1669,20 +1686,9 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, if (device2 == 0) { device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; } - if (device2 == 0) { - device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; - } - if (device2 == 0) { - device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; - } - if (device2 == 0) { - device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; - } #ifdef WITH_A2DP - if (mA2dpOutput != 0) { - if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) { - break; - } + if ((mA2dpOutput != 0) && + (strategy != STRATEGY_SONIFICATION || a2dpUsedForSonification())) { if (device2 == 0) { device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP; } @@ -1695,6 +1701,15 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, } #endif if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; + } + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; + } + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; + } + if (device2 == 0) { device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; } @@ -1821,6 +1836,70 @@ audio_io_handle_t AudioPolicyManagerBase::getActiveInput() return 0; } +float AudioPolicyManagerBase::volIndexToAmpl(uint32_t device, const StreamDescriptor& streamDesc, + int indexInUi) { + // the volume index in the UI is relative to the min and max volume indices for this stream type + int nbSteps = 1 + streamDesc.mVolIndex[StreamDescriptor::VOLMAX] - + streamDesc.mVolIndex[StreamDescriptor::VOLMIN]; + int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) / + (streamDesc.mIndexMax - streamDesc.mIndexMin); + + // find what part of the curve this index volume belongs to, or if it's out of bounds + int segment = 0; + if (volIdx < streamDesc.mVolIndex[StreamDescriptor::VOLMIN]) { // out of bounds + return 0.0f; + } else if (volIdx < streamDesc.mVolIndex[StreamDescriptor::VOLKNEE1]) { + segment = 0; + } else if (volIdx < streamDesc.mVolIndex[StreamDescriptor::VOLKNEE2]) { + segment = 1; + } else if (volIdx <= streamDesc.mVolIndex[StreamDescriptor::VOLMAX]) { + segment = 2; + } else { // out of bounds + return 1.0f; + } + + // linear interpolation in the attenuation table in dB + float decibels = streamDesc.mVolDbAtt[segment] + + ((float)(volIdx - streamDesc.mVolIndex[segment])) * + ( (streamDesc.mVolDbAtt[segment+1] - streamDesc.mVolDbAtt[segment]) / + ((float)(streamDesc.mVolIndex[segment+1] - streamDesc.mVolIndex[segment])) ); + + float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) + + LOGV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f", + streamDesc.mVolIndex[segment], volIdx, streamDesc.mVolIndex[segment+1], + streamDesc.mVolDbAtt[segment], decibels, streamDesc.mVolDbAtt[segment+1], + amplification); + + return amplification; +} + +void AudioPolicyManagerBase::initializeVolumeCurves() { + // initialize the volume curves to a (-49.5 - 0 dB) attenuation in 0.5dB steps + for (int i=0 ; i< AudioSystem::NUM_STREAM_TYPES ; i++) { + mStreams[i].mVolIndex[StreamDescriptor::VOLMIN] = 1; + mStreams[i].mVolDbAtt[StreamDescriptor::VOLMIN] = -49.5f; + mStreams[i].mVolIndex[StreamDescriptor::VOLKNEE1] = 33; + mStreams[i].mVolDbAtt[StreamDescriptor::VOLKNEE1] = -33.5f; + mStreams[i].mVolIndex[StreamDescriptor::VOLKNEE2] = 66; + mStreams[i].mVolDbAtt[StreamDescriptor::VOLKNEE2] = -17.0f; + // here we use 100 steps to avoid rounding errors + // when computing the volume in volIndexToAmpl() + mStreams[i].mVolIndex[StreamDescriptor::VOLMAX] = 100; + mStreams[i].mVolDbAtt[StreamDescriptor::VOLMAX] = 0.0f; + } + + // Modification for music: more attenuation for lower volumes, finer steps at high volumes + mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLMIN] = 1; + mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLMIN] = -58.0f; + mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLKNEE1] = 20; + mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLKNEE1] = -40.0f; + mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLKNEE2] = 60; + mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLKNEE2] = -17.0f; + mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLMAX] = 100; + mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLMAX] = 0.0f; +} + float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device) { float volume = 1.0; @@ -1831,8 +1910,7 @@ float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_hand device = outputDesc->device(); } - int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin); - volume = AudioSystem::linearToLog(volInt); + volume = volIndexToAmpl(device, streamDesc, index); // if a headset is connected, apply the following rules to ring tones and notifications // to avoid sound level bursts in user's ears: @@ -1843,9 +1921,7 @@ float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_hand (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP | AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AudioSystem::DEVICE_OUT_WIRED_HEADSET | - AudioSystem::DEVICE_OUT_WIRED_HEADPHONE | - AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET | - AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET)) && + AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) && ((getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) || (stream == AudioSystem::SYSTEM)) && streamDesc.mCanBeMuted) { diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp index b04672d..b614c48 100644 --- a/services/audioflinger/AudioPolicyService.cpp +++ b/services/audioflinger/AudioPolicyService.cpp @@ -365,6 +365,14 @@ uint32_t AudioPolicyService::getStrategyForStream(AudioSystem::stream_type strea return mpPolicyManager->getStrategyForStream(stream); } +uint32_t AudioPolicyService::getDevicesForStream(AudioSystem::stream_type stream) +{ + if (mpPolicyManager == NULL) { + return 0; + } + return mpPolicyManager->getDevicesForStream(stream); +} + audio_io_handle_t AudioPolicyService::getOutputForEffect(effect_descriptor_t *desc) { if (mpPolicyManager == NULL) { @@ -488,13 +496,6 @@ status_t AudioPolicyService::onTransact( // ---------------------------------------------------------------------------- -void AudioPolicyService::instantiate() { - defaultServiceManager()->addService( - String16("media.audio_policy"), new AudioPolicyService()); -} - - -// ---------------------------------------------------------------------------- // AudioPolicyClientInterface implementation // ---------------------------------------------------------------------------- diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h index 54af1f1..faad893 100644 --- a/services/audioflinger/AudioPolicyService.h +++ b/services/audioflinger/AudioPolicyService.h @@ -21,6 +21,7 @@ #include <hardware_legacy/AudioPolicyInterface.h> #include <media/ToneGenerator.h> #include <utils/Vector.h> +#include <binder/BinderService.h> namespace android { @@ -28,12 +29,17 @@ class String8; // ---------------------------------------------------------------------------- -class AudioPolicyService: public BnAudioPolicyService, public AudioPolicyClientInterface, +class AudioPolicyService : + public BinderService<AudioPolicyService>, + public BnAudioPolicyService, + public AudioPolicyClientInterface, public IBinder::DeathRecipient { + friend class BinderService<AudioPolicyService>; public: - static void instantiate(); + // for BinderService + static const char *getServiceName() { return "media.audio_policy"; } virtual status_t dump(int fd, const Vector<String16>& args); @@ -80,6 +86,7 @@ public: virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index); virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream); + virtual uint32_t getDevicesForStream(AudioSystem::stream_type stream); virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc); virtual status_t registerEffect(effect_descriptor_t *desc, @@ -241,11 +248,3 @@ private: }; // namespace android #endif // ANDROID_AUDIOPOLICYSERVICE_H - - - - - - - - diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp index 5dabacb..5c3b43fc 100644 --- a/services/audioflinger/AudioResampler.cpp +++ b/services/audioflinger/AudioResampler.cpp @@ -148,6 +148,12 @@ void AudioResampler::setVolume(int16_t left, int16_t right) { mVolume[1] = right; } +void AudioResampler::reset() { + mInputIndex = 0; + mPhaseFraction = 0; + mBuffer.frameCount = 0; +} + // ---------------------------------------------------------------------------- void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount, diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h index 2dfac76..9f06c1c 100644 --- a/services/audioflinger/AudioResampler.h +++ b/services/audioflinger/AudioResampler.h @@ -53,6 +53,8 @@ public: virtual void resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider) = 0; + virtual void reset(); + protected: // number of bits for phase fraction - 30 bits allows nearly 2x downsampling static const int kNumPhaseBits = 30; diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 3d8ca7a..a09e16b 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -727,17 +727,30 @@ status_t CameraService::Client::cancelAutoFocus() { } // take a picture - image is returned in callback -status_t CameraService::Client::takePicture() { - LOG1("takePicture (pid %d)", getCallingPid()); +status_t CameraService::Client::takePicture(int msgType) { + LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType); Mutex::Autolock lock(mLock); status_t result = checkPidAndHardware(); if (result != NO_ERROR) return result; - enableMsgType(CAMERA_MSG_SHUTTER | - CAMERA_MSG_POSTVIEW_FRAME | - CAMERA_MSG_RAW_IMAGE | - CAMERA_MSG_COMPRESSED_IMAGE); + if ((msgType & CAMERA_MSG_RAW_IMAGE) && + (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) { + LOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY" + " cannot be both enabled"); + return BAD_VALUE; + } + + // We only accept picture related message types + // and ignore other types of messages for takePicture(). + int picMsgType = msgType + & (CAMERA_MSG_SHUTTER | + CAMERA_MSG_POSTVIEW_FRAME | + CAMERA_MSG_RAW_IMAGE | + CAMERA_MSG_RAW_IMAGE_NOTIFY | + CAMERA_MSG_COMPRESSED_IMAGE); + + enableMsgType(picMsgType); return mHardware->takePicture(); } diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index ccb9cf7..1c43b00 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -108,7 +108,7 @@ private: virtual void releaseRecordingFrame(const sp<IMemory>& mem); virtual status_t autoFocus(); virtual status_t cancelAutoFocus(); - virtual status_t takePicture(); + virtual status_t takePicture(int msgType); virtual status_t setParameters(const String8& params); virtual String8 getParameters() const; virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2); diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 41dbe2f..25db25e 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -101,12 +101,14 @@ EventHub::Device::Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier) : next(NULL), fd(fd), id(id), path(path), identifier(identifier), - classes(0), keyBitmask(NULL), configuration(NULL), virtualKeyMap(NULL) { + classes(0), keyBitmask(NULL), relBitmask(NULL), + configuration(NULL), virtualKeyMap(NULL) { } EventHub::Device::~Device() { close(); delete[] keyBitmask; + delete[] relBitmask; delete configuration; delete virtualKeyMap; } @@ -127,9 +129,7 @@ EventHub::EventHub(void) : mOpened(false), mNeedToSendFinishedDeviceScan(false), mInputBufferIndex(0), mInputBufferCount(0), mInputFdIndex(0) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); -#ifdef EV_SW memset(mSwitches, 0, sizeof(mSwitches)); -#endif } EventHub::~EventHub(void) { @@ -191,6 +191,18 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, return OK; } +bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const { + if (axis >= 0 && axis <= REL_MAX) { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device && device->relBitmask) { + return test_bit(axis, device->relBitmask); + } + } + return false; +} + int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { if (scanCode >= 0 && scanCode <= KEY_MAX) { AutoMutex _l(mLock); @@ -229,7 +241,7 @@ int32_t EventHub::getKeyCodeStateLocked(Device* device, int32_t keyCode) const { } Vector<int32_t> scanCodes; - device->keyMap.keyLayoutMap->findScanCodes(keyCode, &scanCodes); + device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes); uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; memset(key_bitmask, 0, sizeof(key_bitmask)); @@ -253,7 +265,6 @@ int32_t EventHub::getKeyCodeStateLocked(Device* device, int32_t keyCode) const { } int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { -#ifdef EV_SW if (sw >= 0 && sw <= SW_MAX) { AutoMutex _l(mLock); @@ -262,7 +273,6 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { return getSwitchStateLocked(device, sw); } } -#endif return AKEY_STATE_UNKNOWN; } @@ -297,7 +307,8 @@ bool EventHub::markSupportedKeyCodesLocked(Device* device, size_t numCodes, for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { scanCodes.clear(); - status_t err = device->keyMap.keyLayoutMap->findScanCodes(keyCodes[codeIndex], &scanCodes); + status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey( + keyCodes[codeIndex], &scanCodes); if (! err) { // check the possible scan codes identified by the layout map against the // map of codes actually emitted by the driver @@ -312,14 +323,14 @@ bool EventHub::markSupportedKeyCodesLocked(Device* device, size_t numCodes, return true; } -status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode, +status_t EventHub::mapKey(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); if (device && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->map(scancode, outKeycode, outFlags); + status_t err = device->keyMap.keyLayoutMap->mapKey(scancode, outKeycode, outFlags); if (err == NO_ERROR) { return NO_ERROR; } @@ -329,7 +340,7 @@ status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode, device = getDeviceLocked(mBuiltInKeyboardId); if (device && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->map(scancode, outKeycode, outFlags); + status_t err = device->keyMap.keyLayoutMap->mapKey(scancode, outKeycode, outFlags); if (err == NO_ERROR) { return NO_ERROR; } @@ -341,6 +352,34 @@ status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode, return NAME_NOT_FOUND; } +status_t EventHub::mapAxis(int32_t deviceId, int scancode, + int32_t* outAxis) const +{ + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + + if (device && device->keyMap.haveKeyLayout()) { + status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxis); + if (err == NO_ERROR) { + return NO_ERROR; + } + } + + if (mBuiltInKeyboardId != -1) { + device = getDeviceLocked(mBuiltInKeyboardId); + + if (device && device->keyMap.haveKeyLayout()) { + status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxis); + if (err == NO_ERROR) { + return NO_ERROR; + } + } + } + + *outAxis = -1; + return NAME_NOT_FOUND; +} + void EventHub::addExcludedDevice(const char* deviceName) { AutoMutex _l(mLock); @@ -469,6 +508,7 @@ bool EventHub::getEvent(RawEvent* outEvent) { } // Grab the next input event. + bool deviceWasRemoved = false; for (;;) { // Consume buffered input events, if any. if (mInputBufferIndex < mInputBufferCount) { @@ -488,7 +528,7 @@ bool EventHub::getEvent(RawEvent* outEvent) { if (iev.type == EV_KEY) { outEvent->keyCode = AKEYCODE_UNKNOWN; if (device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->map(iev.code, + status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code, &outEvent->keyCode, &outEvent->flags); LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", iev.code, outEvent->keyCode, outEvent->flags, err); @@ -519,6 +559,10 @@ bool EventHub::getEvent(RawEvent* outEvent) { int32_t readSize = read(pfd.fd, mInputBufferData, sizeof(struct input_event) * INPUT_BUFFER_SIZE); if (readSize < 0) { + if (errno == ENODEV) { + deviceWasRemoved = true; + break; + } if (errno != EAGAIN && errno != EINTR) { LOGW("could not get event (errno=%d)", errno); } @@ -531,6 +575,13 @@ bool EventHub::getEvent(RawEvent* outEvent) { } } + // Handle the case where a device has been removed but INotify has not yet noticed. + if (deviceWasRemoved) { + AutoMutex _l(mLock); + closeDeviceAtIndexLocked(mInputFdIndex); + continue; // report added or removed devices immediately + } + #if HAVE_INOTIFY // readNotify() will modify mFDs and mFDCount, so this must be done after // processing all other events. @@ -541,8 +592,6 @@ bool EventHub::getEvent(RawEvent* outEvent) { } #endif - mInputFdIndex = 0; - // Poll for events. Mind the wake lock dance! // We hold a wake lock at all times except during poll(). This works due to some // subtle choreography. When a device driver has pending (unread) events, it acquires @@ -563,6 +612,9 @@ bool EventHub::getEvent(RawEvent* outEvent) { usleep(100000); } } + + // Prepare to process all of the FDs we just polled. + mInputFdIndex = 0; } } @@ -624,7 +676,11 @@ static const int32_t GAMEPAD_KEYCODES[] = { AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, - AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE + AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE, + AKEYCODE_BUTTON_1, AKEYCODE_BUTTON_2, AKEYCODE_BUTTON_3, AKEYCODE_BUTTON_4, + AKEYCODE_BUTTON_5, AKEYCODE_BUTTON_6, AKEYCODE_BUTTON_7, AKEYCODE_BUTTON_8, + AKEYCODE_BUTTON_9, AKEYCODE_BUTTON_10, AKEYCODE_BUTTON_11, AKEYCODE_BUTTON_12, + AKEYCODE_BUTTON_13, AKEYCODE_BUTTON_14, AKEYCODE_BUTTON_15, AKEYCODE_BUTTON_16, }; int EventHub::openDevice(const char *devicePath) { @@ -727,86 +783,89 @@ int EventHub::openDevice(const char *devicePath) { loadConfiguration(device); // Figure out the kinds of events the device reports. - uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; memset(key_bitmask, 0, sizeof(key_bitmask)); + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask); - LOGV("Getting keys..."); - if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) { - //LOGI("MAP\n"); - //for (int i = 0; i < sizeof(key_bitmask); i++) { - // LOGI("%d: 0x%02x\n", i, key_bitmask[i]); - //} - - // See if this is a keyboard. Ignore everything in the button range except for - // gamepads which are also considered keyboards. - if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC)) - || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD), - sizeof_bit_array(BTN_DIGI)) - || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK), - sizeof_bit_array(KEY_MAX + 1))) { - device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; + uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)]; + memset(abs_bitmask, 0, sizeof(abs_bitmask)); + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask); - device->keyBitmask = new uint8_t[sizeof(key_bitmask)]; - if (device->keyBitmask != NULL) { - memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask)); - } else { - delete device; - LOGE("out of memory allocating key bitmask"); - return -1; - } - } + uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)]; + memset(rel_bitmask, 0, sizeof(rel_bitmask)); + ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask); + + uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)]; + memset(sw_bitmask, 0, sizeof(sw_bitmask)); + ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask); + + device->keyBitmask = new uint8_t[sizeof(key_bitmask)]; + if (device->keyBitmask != NULL) { + memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask)); + } else { + delete device; + LOGE("out of memory allocating key bitmask"); + return -1; } - + + device->relBitmask = new uint8_t[sizeof(rel_bitmask)]; + if (device->relBitmask != NULL) { + memcpy(device->relBitmask, rel_bitmask, sizeof(rel_bitmask)); + } else { + delete device; + LOGE("out of memory allocating rel bitmask"); + return -1; + } + + // See if this is a keyboard. Ignore everything in the button range except for + // joystick and gamepad buttons which are handled like keyboards for the most part. + bool haveKeyboardKeys = containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC)) + || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK), + sizeof_bit_array(KEY_MAX + 1)); + bool haveGamepadButtons =containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_JOYSTICK), + sizeof_bit_array(BTN_DIGI)); + if (haveKeyboardKeys || haveGamepadButtons) { + device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; + } + // See if this is a cursor device such as a trackball or mouse. - if (test_bit(BTN_MOUSE, key_bitmask)) { - uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)]; - memset(rel_bitmask, 0, sizeof(rel_bitmask)); - LOGV("Getting relative controllers..."); - if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) { - if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) { - device->classes |= INPUT_DEVICE_CLASS_CURSOR; - } - } + if (test_bit(BTN_MOUSE, key_bitmask) + && test_bit(REL_X, rel_bitmask) + && test_bit(REL_Y, rel_bitmask)) { + device->classes |= INPUT_DEVICE_CLASS_CURSOR; } // See if this is a touch pad. - uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)]; - memset(abs_bitmask, 0, sizeof(abs_bitmask)); - LOGV("Getting absolute controllers..."); - if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) { - // Is this a new modern multi-touch driver? - if (test_bit(ABS_MT_POSITION_X, abs_bitmask) - && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) { + // Is this a new modern multi-touch driver? + if (test_bit(ABS_MT_POSITION_X, abs_bitmask) + && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) { + // Some joysticks such as the PS3 controller report axes that conflict + // with the ABS_MT range. Try to confirm that the device really is + // a touch screen. + if (test_bit(BTN_TOUCH, key_bitmask) || !haveGamepadButtons) { device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT; - - // Is this an old style single-touch driver? - } else if (test_bit(BTN_TOUCH, key_bitmask) - && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) { - device->classes |= INPUT_DEVICE_CLASS_TOUCH; } + // Is this an old style single-touch driver? + } else if (test_bit(BTN_TOUCH, key_bitmask) + && test_bit(ABS_X, abs_bitmask) + && test_bit(ABS_Y, abs_bitmask)) { + device->classes |= INPUT_DEVICE_CLASS_TOUCH; } -#ifdef EV_SW // figure out the switches this device reports - uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)]; - memset(sw_bitmask, 0, sizeof(sw_bitmask)); - bool hasSwitches = false; - if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) { - for (int i=0; i<EV_SW; i++) { - //LOGI("Device %d sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask)); - if (test_bit(i, sw_bitmask)) { - hasSwitches = true; - if (mSwitches[i] == 0) { - mSwitches[i] = device->id; - } + bool haveSwitches = false; + for (int i=0; i<EV_SW; i++) { + //LOGI("Device %d sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask)); + if (test_bit(i, sw_bitmask)) { + haveSwitches = true; + if (mSwitches[i] == 0) { + mSwitches[i] = device->id; } } } - if (hasSwitches) { + if (haveSwitches) { device->classes |= INPUT_DEVICE_CLASS_SWITCH; } -#endif if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) { // Load the virtual keys for the touch screen, if any. @@ -856,6 +915,16 @@ int EventHub::openDevice(const char *devicePath) { } } + // See if this device is a joystick. + // Ignore touchscreens because they use the same absolute axes for other purposes. + // Assumes that joysticks always have buttons and the keymap has been loaded. + if (device->classes & INPUT_DEVICE_CLASS_GAMEPAD + && !(device->classes & INPUT_DEVICE_CLASS_TOUCH)) { + if (containsNonZeroByte(abs_bitmask, 0, sizeof_bit_array(ABS_MAX + 1))) { + device->classes |= INPUT_DEVICE_CLASS_JOYSTICK; + } + } + // If the device isn't recognized as something we handle, don't monitor it. if (device->classes == 0) { LOGV("Dropping device: id=%d, path='%s', name='%s'", @@ -934,7 +1003,7 @@ bool EventHub::hasKeycodeLocked(Device* device, int keycode) const { } Vector<int32_t> scanCodes; - device->keyMap.keyLayoutMap->findScanCodes(keycode, &scanCodes); + device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes); const size_t N = scanCodes.size(); for (size_t i=0; i<N && i<=KEY_MAX; i++) { int32_t sc = scanCodes.itemAt(i); @@ -952,37 +1021,40 @@ int EventHub::closeDevice(const char *devicePath) { for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < mDevices.size(); i++) { Device* device = mDevices[i]; if (device->path == devicePath) { - LOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n", - device->path.string(), device->identifier.name.string(), device->id, - device->fd, device->classes); - -#ifdef EV_SW - for (int j=0; j<EV_SW; j++) { - if (mSwitches[j] == device->id) { - mSwitches[j] = 0; - } - } -#endif - - if (device->id == mBuiltInKeyboardId) { - LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", - device->path.string(), mBuiltInKeyboardId); - mBuiltInKeyboardId = -1; - clearKeyboardProperties(device, true); - } - clearKeyboardProperties(device, false); + return closeDeviceAtIndexLocked(i); + } + } + LOGV("Remove device: %s not found, device may already have been removed.", devicePath); + return -1; +} - mFds.removeAt(i); - mDevices.removeAt(i); - device->close(); +int EventHub::closeDeviceAtIndexLocked(int index) { + Device* device = mDevices[index]; + LOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n", + device->path.string(), device->identifier.name.string(), device->id, + device->fd, device->classes); - device->next = mClosingDevices; - mClosingDevices = device; - return 0; + for (int j=0; j<EV_SW; j++) { + if (mSwitches[j] == device->id) { + mSwitches[j] = 0; } } - LOGE("remove device: %s not found\n", devicePath); - return -1; + + if (device->id == mBuiltInKeyboardId) { + LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", + device->path.string(), mBuiltInKeyboardId); + mBuiltInKeyboardId = -1; + clearKeyboardProperties(device, true); + } + clearKeyboardProperties(device, false); + + mFds.removeAt(index); + mDevices.removeAt(index); + device->close(); + + device->next = mClosingDevices; + mClosingDevices = device; + return 0; } int EventHub::readNotify(int nfd) { diff --git a/services/input/EventHub.h b/services/input/EventHub.h index 0ee0b9b..f7936d2 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -85,7 +85,7 @@ struct RawAbsoluteAxisInfo { int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8 int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise - inline int32_t getRange() { return maxValue - minValue; } + inline int32_t getRange() const { return maxValue - minValue; } inline void clear() { valid = false; @@ -100,7 +100,7 @@ struct RawAbsoluteAxisInfo { * Input device classes. */ enum { - /* The input device is a keyboard. */ + /* The input device is a keyboard or has buttons. */ INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001, /* The input device is an alpha-numeric keyboard (not just a dial pad). */ @@ -123,6 +123,9 @@ enum { /* The input device has switches. */ INPUT_DEVICE_CLASS_SWITCH = 0x00000080, + + /* The input device is a joystick (implies gamepad, has joystick absolute axes). */ + INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100, }; /* @@ -164,9 +167,14 @@ public: virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const = 0; - virtual status_t scancodeToKeycode(int32_t deviceId, int scancode, + virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0; + + virtual status_t mapKey(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const = 0; + virtual status_t mapAxis(int32_t deviceId, int scancode, + int32_t* outAxis) const = 0; + // exclude a particular device from opening // this can be used to ignore input devices for sensors virtual void addExcludedDevice(const char* deviceName) = 0; @@ -218,9 +226,14 @@ public: virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const; - virtual status_t scancodeToKeycode(int32_t deviceId, int scancode, + virtual bool hasRelativeAxis(int32_t deviceId, int axis) const; + + virtual status_t mapKey(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const; + virtual status_t mapAxis(int32_t deviceId, int scancode, + int32_t* outAxis) const; + virtual void addExcludedDevice(const char* deviceName); virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const; @@ -248,6 +261,7 @@ private: int openDevice(const char *devicePath); int closeDevice(const char *devicePath); + int closeDeviceAtIndexLocked(int index); int scanDir(const char *dirname); int readNotify(int nfd); @@ -263,6 +277,7 @@ private: uint32_t classes; uint8_t* keyBitmask; + uint8_t* relBitmask; String8 configurationFile; PropertyMap* configuration; VirtualKeyMap* virtualKeyMap; @@ -311,9 +326,7 @@ private: List<String8> mExcludedDevices; // device ids that report particular switches. -#ifdef EV_SW int32_t mSwitches[SW_MAX + 1]; -#endif static const int INPUT_BUFFER_SIZE = 64; struct input_event mInputBufferData[INPUT_BUFFER_SIZE]; diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index cbfdd75..c064a9c 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -115,6 +115,8 @@ static bool isValidMotionAction(int32_t action, size_t pointerCount) { case AMOTION_EVENT_ACTION_CANCEL: case AMOTION_EVENT_ACTION_MOVE: case AMOTION_EVENT_ACTION_OUTSIDE: + case AMOTION_EVENT_ACTION_HOVER_MOVE: + case AMOTION_EVENT_ACTION_SCROLL: return true; case AMOTION_EVENT_ACTION_POINTER_DOWN: case AMOTION_EVENT_ACTION_POINTER_UP: { @@ -318,7 +320,8 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, uint32_t source = motionEntry->source; if (! isAppSwitchDue && motionEntry->next == & mInboundQueue.tailSentinel // exactly one event - && motionEntry->action == AMOTION_EVENT_ACTION_MOVE + && (motionEntry->action == AMOTION_EVENT_ACTION_MOVE + || motionEntry->action == AMOTION_EVENT_ACTION_HOVER_MOVE) && deviceId == mThrottleState.lastDeviceId && source == mThrottleState.lastSource) { nsecs_t nextTime = mThrottleState.lastEventTime @@ -482,8 +485,10 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && mInputTargetWaitApplication != NULL) { - int32_t x = int32_t(motionEntry->firstSample.pointerCoords[0].x); - int32_t y = int32_t(motionEntry->firstSample.pointerCoords[0].y); + int32_t x = int32_t(motionEntry->firstSample.pointerCoords[0]. + getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = int32_t(motionEntry->firstSample.pointerCoords[0]. + getAxisValue(AMOTION_EVENT_AXIS_Y)); const InputWindow* touchedWindow = findTouchedWindowAtLocked(x, y); if (touchedWindow && touchedWindow->inputWindowHandle != NULL @@ -836,12 +841,13 @@ bool InputDispatcher::dispatchMotionLocked( bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; // Identify targets. + bool conflictingPointerActions = false; if (! mCurrentInputTargetsValid) { int32_t injectionResult; if (isPointerEvent) { // Pointer event. (eg. touchscreen) injectionResult = findTouchedWindowTargetsLocked(currentTime, - entry, nextWakeupTime); + entry, nextWakeupTime, &conflictingPointerActions); } else { // Non touch event. (eg. trackball) injectionResult = findFocusedWindowTargetsLocked(currentTime, @@ -861,6 +867,10 @@ bool InputDispatcher::dispatchMotionLocked( } // Dispatch the motion. + if (conflictingPointerActions) { + synthesizeCancelationEventsForAllConnectionsLocked( + InputState::CANCEL_POINTER_EVENTS, "Conflicting pointer actions."); + } dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); return true; } @@ -888,11 +898,15 @@ void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const M "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " "orientation=%f", i, entry->pointerIds[i], - sample->pointerCoords[i].x, sample->pointerCoords[i].y, - sample->pointerCoords[i].pressure, sample->pointerCoords[i].size, - sample->pointerCoords[i].touchMajor, sample->pointerCoords[i].touchMinor, - sample->pointerCoords[i].toolMajor, sample->pointerCoords[i].toolMinor, - sample->pointerCoords[i].orientation); + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } // Keep in mind that due to batching, it is possible for the number of samples actually @@ -1117,7 +1131,7 @@ Unresponsive: } int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, - const MotionEntry* entry, nsecs_t* nextWakeupTime) { + const MotionEntry* entry, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { enum InjectionPermission { INJECTION_PERMISSION_UNKNOWN, INJECTION_PERMISSION_GRANTED, @@ -1160,36 +1174,47 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Update the touch state as needed based on the properties of the touch event. int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING; InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; - bool isSplit, wrongDevice; - if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - mTempTouchState.reset(); - mTempTouchState.down = true; - mTempTouchState.deviceId = entry->deviceId; - mTempTouchState.source = entry->source; - isSplit = false; - wrongDevice = false; + + bool isSplit = mTouchState.split; + bool wrongDevice = mTouchState.down + && (mTouchState.deviceId != entry->deviceId + || mTouchState.source != entry->source); + if (maskedAction == AMOTION_EVENT_ACTION_DOWN + || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE + || maskedAction == AMOTION_EVENT_ACTION_SCROLL) { + bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; + if (wrongDevice && !down) { + mTempTouchState.copyFrom(mTouchState); + } else { + mTempTouchState.reset(); + mTempTouchState.down = down; + mTempTouchState.deviceId = entry->deviceId; + mTempTouchState.source = entry->source; + isSplit = false; + wrongDevice = false; + } } else { mTempTouchState.copyFrom(mTouchState); - isSplit = mTempTouchState.split; - wrongDevice = mTempTouchState.down - && (mTempTouchState.deviceId != entry->deviceId - || mTempTouchState.source != entry->source); - if (wrongDevice) { + } + if (wrongDevice) { #if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("Dropping event because a pointer for a different device is already down."); + LOGD("Dropping event because a pointer for a different device is already down."); #endif - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } + injectionResult = INPUT_EVENT_INJECTION_FAILED; + goto Failed; } if (maskedAction == AMOTION_EVENT_ACTION_DOWN - || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { - /* Case 1: New splittable pointer going down. */ + || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) + || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE + || maskedAction == AMOTION_EVENT_ACTION_SCROLL) { + /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ int32_t pointerIndex = getMotionEventActionPointerIndex(action); - int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].x); - int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].y); + int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex]. + getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex]. + getAxisValue(AMOTION_EVENT_AXIS_Y)); const InputWindow* newTouchedWindow = NULL; const InputWindow* topErrorWindow = NULL; @@ -1357,6 +1382,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // If this is the first pointer going down and the touched window has a wallpaper // then also add the touched wallpaper windows so they are locked in for the duration // of the touch gesture. + // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper + // engine only supports touch events. We would need to add a mechanism similar + // to View.onGenericMotionEvent to enable wallpapers to handle these events. if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow(); if (foregroundWindow->hasWallpaper) { @@ -1396,16 +1424,19 @@ Failed: if (injectionPermission == INJECTION_PERMISSION_GRANTED) { if (!wrongDevice) { if (maskedAction == AMOTION_EVENT_ACTION_UP - || maskedAction == AMOTION_EVENT_ACTION_CANCEL) { + || maskedAction == AMOTION_EVENT_ACTION_CANCEL + || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { // All pointers up or canceled. - mTempTouchState.reset(); + mTouchState.reset(); } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { // First pointer went down. if (mTouchState.down) { + *outConflictingPointerActions = true; #if DEBUG_FOCUS LOGD("Pointer down received while already down."); #endif } + mTouchState.copyFrom(mTempTouchState); } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { // One pointer went up. if (isSplit) { @@ -1424,10 +1455,13 @@ Failed: i += 1; } } + mTouchState.copyFrom(mTempTouchState); + } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) { + // Discard temporary touch state since it was only valid for this action. + } else { + // Save changes to touch state as-is for all other actions. + mTouchState.copyFrom(mTempTouchState); } - - // Save changes to touch state. - mTouchState.copyFrom(mTempTouchState); } } else { #if DEBUG_FOCUS @@ -1742,29 +1776,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // Update the connection's input state. EventEntry* eventEntry = dispatchEntry->eventEntry; - InputState::Consistency consistency = connection->inputState.trackEvent(eventEntry); - -#if FILTER_INPUT_EVENTS - // Filter out inconsistent sequences of input events. - // The input system may drop or inject events in a way that could violate implicit - // invariants on input state and potentially cause an application to crash - // or think that a key or pointer is stuck down. Technically we make no guarantees - // of consistency but it would be nice to improve on this where possible. - // XXX: This code is a proof of concept only. Not ready for prime time. - if (consistency == InputState::TOLERABLE) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ Sending an event that is inconsistent with the connection's " - "current input state but that is likely to be tolerated by the application.", - connection->getInputChannelName()); -#endif - } else if (consistency == InputState::BROKEN) { - LOGI("channel '%s' ~ Dropping an event that is inconsistent with the connection's " - "current input state and that is likely to cause the application to crash.", - connection->getInputChannelName()); - startNextDispatchCycleLocked(currentTime, connection); - return; - } -#endif + connection->inputState.trackEvent(eventEntry); // Publish the event. status_t status; @@ -2275,11 +2287,16 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, " "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " "orientation=%f", - i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y, - pointerCoords[i].pressure, pointerCoords[i].size, - pointerCoords[i].touchMajor, pointerCoords[i].touchMinor, - pointerCoords[i].toolMajor, pointerCoords[i].toolMinor, - pointerCoords[i].orientation); + i, pointerIds[i], + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } #endif if (! validateMotionEvent(action, pointerCount, pointerIds)) { @@ -2294,7 +2311,8 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t AutoMutex _l(mLock); // Attempt batching and streaming of move events. - if (action == AMOTION_EVENT_ACTION_MOVE) { + if (action == AMOTION_EVENT_ACTION_MOVE + || action == AMOTION_EVENT_ACTION_HOVER_MOVE) { // BATCHING CASE // // Try to append a move sample to the tail of the inbound queue for this device. @@ -2313,7 +2331,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t continue; } - if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE + if (motionEntry->action != action || motionEntry->source != source || motionEntry->pointerCount != pointerCount || motionEntry->isInjected()) { @@ -2372,7 +2390,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t MotionEntry* motionEntry = static_cast<MotionEntry*>( dispatchEntry->eventEntry); - if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE + if (motionEntry->action != action || motionEntry->deviceId != deviceId || motionEntry->source != source || motionEntry->pointerCount != pointerCount @@ -3201,14 +3219,52 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( // be used as modifiers) but it will ensure that fallback keys do not // get stuck. This takes care of the case where the application does not handle // the original DOWN so we generate a fallback DOWN but it does handle - // the original UP in which case we would not generate the fallback UP. + // the original UP in which case we want to send a fallback CANCEL. synthesizeCancelationEventsForConnectionLocked(connection, InputState::CANCEL_FALLBACK_EVENTS, - "application handled a non-fallback event, canceling all fallback events"); + "application handled a non-fallback event, " + "canceling all fallback events"); + connection->originalKeyCodeForFallback = -1; } else { - // If the application did not handle a non-fallback key, then ask - // the policy what to do with it. We might generate a fallback key - // event here. + // If the application did not handle a non-fallback key, first check + // that we are in a good state to handle the fallback key. Then ask + // the policy what to do with it. + if (connection->originalKeyCodeForFallback < 0) { + if (keyEntry->action != AKEY_EVENT_ACTION_DOWN + || keyEntry->repeatCount != 0) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Unhandled key event: Skipping fallback since this " + "is not an initial down. " + "keyCode=%d, action=%d, repeatCount=%d", + keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount); +#endif + goto SkipFallback; + } + + // Start handling the fallback key on DOWN. + connection->originalKeyCodeForFallback = keyEntry->keyCode; + } else { + if (keyEntry->keyCode != connection->originalKeyCodeForFallback) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Unhandled key event: Skipping fallback since there is " + "already a different fallback in progress. " + "keyCode=%d, originalKeyCodeForFallback=%d", + keyEntry->keyCode, connection->originalKeyCodeForFallback); +#endif + goto SkipFallback; + } + + // Finish handling the fallback key on UP. + if (keyEntry->action == AKEY_EVENT_ACTION_UP) { + connection->originalKeyCodeForFallback = -1; + } + } + +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Unhandled key event: Asking policy to perform fallback action. " + "keyCode=%d, action=%d, repeatCount=%d", + keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount); +#endif KeyEvent event; initializeKeyEvent(&event, keyEntry); @@ -3238,6 +3294,12 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( keyEntry->downTime = event.getDownTime(); keyEntry->syntheticRepeat = false; +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Unhandled key event: Dispatching fallback key. " + "fallbackKeyCode=%d, fallbackMetaState=%08x", + keyEntry->keyCode, keyEntry->metaState); +#endif + dispatchEntry->inProgress = false; startDispatchCycleLocked(now(), connection); return; @@ -3247,6 +3309,7 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( } } +SkipFallback: startNextDispatchCycleLocked(now(), connection); } @@ -3516,21 +3579,20 @@ bool InputDispatcher::InputState::isNeutral() const { return mKeyMementos.isEmpty() && mMotionMementos.isEmpty(); } -InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackEvent( +void InputDispatcher::InputState::trackEvent( const EventEntry* entry) { switch (entry->type) { case EventEntry::TYPE_KEY: - return trackKey(static_cast<const KeyEntry*>(entry)); + trackKey(static_cast<const KeyEntry*>(entry)); + break; case EventEntry::TYPE_MOTION: - return trackMotion(static_cast<const MotionEntry*>(entry)); - - default: - return CONSISTENT; + trackMotion(static_cast<const MotionEntry*>(entry)); + break; } } -InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey( +void InputDispatcher::InputState::trackKey( const KeyEntry* entry) { int32_t action = entry->action; for (size_t i = 0; i < mKeyMementos.size(); i++) { @@ -3542,19 +3604,20 @@ InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey( switch (action) { case AKEY_EVENT_ACTION_UP: mKeyMementos.removeAt(i); - return CONSISTENT; + return; case AKEY_EVENT_ACTION_DOWN: - return TOLERABLE; + mKeyMementos.removeAt(i); + goto Found; default: - return BROKEN; + return; } } } - switch (action) { - case AKEY_EVENT_ACTION_DOWN: { +Found: + if (action == AKEY_EVENT_ACTION_DOWN) { mKeyMementos.push(); KeyMemento& memento = mKeyMementos.editTop(); memento.deviceId = entry->deviceId; @@ -3563,15 +3626,10 @@ InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey( memento.scanCode = entry->scanCode; memento.flags = entry->flags; memento.downTime = entry->downTime; - return CONSISTENT; - } - - default: - return BROKEN; } } -InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotion( +void InputDispatcher::InputState::trackMotion( const MotionEntry* entry) { int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK; for (size_t i = 0; i < mMotionMementos.size(); i++) { @@ -3581,40 +3639,28 @@ InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotio switch (action) { case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_HOVER_MOVE: mMotionMementos.removeAt(i); - return CONSISTENT; + return; case AMOTION_EVENT_ACTION_DOWN: - return TOLERABLE; - - case AMOTION_EVENT_ACTION_POINTER_DOWN: - if (entry->pointerCount == memento.pointerCount + 1) { - memento.setPointers(entry); - return CONSISTENT; - } - return BROKEN; + mMotionMementos.removeAt(i); + goto Found; case AMOTION_EVENT_ACTION_POINTER_UP: - if (entry->pointerCount == memento.pointerCount - 1) { - memento.setPointers(entry); - return CONSISTENT; - } - return BROKEN; - + case AMOTION_EVENT_ACTION_POINTER_DOWN: case AMOTION_EVENT_ACTION_MOVE: - if (entry->pointerCount == memento.pointerCount) { - return CONSISTENT; - } - return BROKEN; + memento.setPointers(entry); + return; default: - return BROKEN; + return; } } } - switch (action) { - case AMOTION_EVENT_ACTION_DOWN: { +Found: + if (action == AMOTION_EVENT_ACTION_DOWN) { mMotionMementos.push(); MotionMemento& memento = mMotionMementos.editTop(); memento.deviceId = entry->deviceId; @@ -3623,11 +3669,6 @@ InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotio memento.yPrecision = entry->yPrecision; memento.downTime = entry->downTime; memento.setPointers(entry); - return CONSISTENT; - } - - default: - return BROKEN; } } @@ -3727,7 +3768,8 @@ InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle) : status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle), inputPublisher(inputChannel), - lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) { + lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX), + originalKeyCodeForFallback(-1) { } InputDispatcher::Connection::~Connection() { diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h index 006c6b8..304b1bb 100644 --- a/services/input/InputDispatcher.h +++ b/services/input/InputDispatcher.h @@ -593,19 +593,6 @@ private: * synthesized when events are dropped. */ class InputState { public: - // Specifies whether a given event will violate input state consistency. - enum Consistency { - // The event is consistent with the current input state. - CONSISTENT, - // The event is inconsistent with the current input state but applications - // will tolerate it. eg. Down followed by another down. - TOLERABLE, - // The event is inconsistent with the current input state and will probably - // cause applications to crash. eg. Up without prior down, move with - // unexpected number of pointers. - BROKEN - }; - // Specifies the sources to cancel. enum CancelationOptions { CANCEL_ALL_EVENTS = 0, @@ -621,16 +608,13 @@ private: bool isNeutral() const; // Records tracking information for an event that has just been published. - // Returns whether the event is consistent with the current input state. - Consistency trackEvent(const EventEntry* entry); + void trackEvent(const EventEntry* entry); // Records tracking information for a key event that has just been published. - // Returns whether the event is consistent with the current input state. - Consistency trackKey(const KeyEntry* entry); + void trackKey(const KeyEntry* entry); // Records tracking information for a motion event that has just been published. - // Returns whether the event is consistent with the current input state. - Consistency trackMotion(const MotionEntry* entry); + void trackMotion(const MotionEntry* entry); // Synthesizes cancelation events for the current state and resets the tracked state. void synthesizeCancelationEvents(nsecs_t currentTime, Allocator* allocator, @@ -698,6 +682,7 @@ private: nsecs_t lastEventTime; // the time when the event was originally captured nsecs_t lastDispatchTime; // the time when the last event was dispatched + int32_t originalKeyCodeForFallback; // original keycode for fallback in progress, -1 if none explicit Connection(const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle); @@ -911,7 +896,7 @@ private: int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, nsecs_t* nextWakeupTime); int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, - nsecs_t* nextWakeupTime); + nsecs_t* nextWakeupTime, bool* outConflictingPointerActions); void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, BitSet32 pointerIds); diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 46d374d..a865d9f 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -250,6 +250,9 @@ InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, ui if (classes & INPUT_DEVICE_CLASS_DPAD) { keyboardSources |= AINPUT_SOURCE_DPAD; } + if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { + keyboardSources |= AINPUT_SOURCE_GAMEPAD; + } if (keyboardSources != 0) { device->addMapper(new KeyboardInputMapper(device, keyboardSources, keyboardType)); @@ -267,6 +270,11 @@ InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, ui device->addMapper(new SingleTouchInputMapper(device)); } + // Joystick-like devices. + if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { + device->addMapper(new JoystickInputMapper(device)); + } + return device; } @@ -557,15 +565,6 @@ InputDevice::~InputDevice() { mMappers.clear(); } -static void dumpMotionRange(String8& dump, const InputDeviceInfo& deviceInfo, - int32_t rangeType, const char* name) { - const InputDeviceInfo::MotionRange* range = deviceInfo.getMotionRange(rangeType); - if (range) { - dump.appendFormat(INDENT3 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n", - name, range->min, range->max, range->flat, range->fuzz); - } -} - void InputDevice::dump(String8& dump) { InputDeviceInfo deviceInfo; getDeviceInfo(& deviceInfo); @@ -574,17 +573,24 @@ void InputDevice::dump(String8& dump) { deviceInfo.getName().string()); dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); - if (!deviceInfo.getMotionRanges().isEmpty()) { + + const KeyedVector<int32_t, InputDeviceInfo::MotionRange> ranges = deviceInfo.getMotionRanges(); + if (!ranges.isEmpty()) { dump.append(INDENT2 "Motion Ranges:\n"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_X, "X"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_Y, "Y"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_PRESSURE, "Pressure"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_SIZE, "Size"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MAJOR, "TouchMajor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MINOR, "TouchMinor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MAJOR, "ToolMajor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MINOR, "ToolMinor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_ORIENTATION, "Orientation"); + for (size_t i = 0; i < ranges.size(); i++) { + int32_t axis = ranges.keyAt(i); + const char* label = getAxisLabel(axis); + char name[32]; + if (label) { + strncpy(name, label, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + } else { + snprintf(name, sizeof(name), "%d", axis); + } + const InputDeviceInfo::MotionRange& range = ranges.valueAt(i); + dump.appendFormat(INDENT3 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n", + name, range.min, range.max, range.flat, range.fuzz); + } } size_t numMappers = mMappers.size(); @@ -605,19 +611,11 @@ void InputDevice::configure() { mSources = 0; - for (size_t i = 0; i < mMappers.size(); i++) { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; mapper->configure(); - - uint32_t sources = mapper->getSources(); - if (sources) { - mSources |= sources; - } else { - // The input mapper does not provide any sources. Remove it from the list. - mMappers.removeAt(i); - delete mapper; - i -= 1; - } + mSources |= mapper->getSources(); } } @@ -741,6 +739,16 @@ int32_t InputMapper::getMetaState() { return 0; } +void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump, + const RawAbsoluteAxisInfo& axis, const char* name) { + if (axis.valid) { + dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n", + name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); + } else { + dump.appendFormat(INDENT4 "%s: unknown range\n", name); + } +} + // --- SwitchInputMapper --- @@ -883,7 +891,7 @@ void KeyboardInputMapper::process(const RawEvent* rawEvent) { bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { return scanCode < BTN_MOUSE || scanCode >= KEY_OK - || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI); + || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); } void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, @@ -1051,14 +1059,21 @@ void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { if (mParameters.mode == Parameters::MODE_POINTER) { float minX, minY, maxX, maxY; if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { - info->addMotionRange(AINPUT_MOTION_RANGE_X, minX, maxX, 0.0f, 0.0f); - info->addMotionRange(AINPUT_MOTION_RANGE_Y, minY, maxY, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_X, minX, maxX, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, minY, maxY, 0.0f, 0.0f); } } else { - info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale); - info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale); + info->addMotionRange(AMOTION_EVENT_AXIS_X, -1.0f, 1.0f, 0.0f, mXScale); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, -1.0f, 1.0f, 0.0f, mYScale); + } + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, 0.0f, 1.0f, 0.0f, 0.0f); + + if (mHaveVWheel) { + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, -1.0f, 1.0f, 0.0f, 0.0f); + } + if (mHaveHWheel) { + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, -1.0f, 1.0f, 0.0f, 0.0f); } - info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, 0.0f, 1.0f, 0.0f, 0.0f); } void CursorInputMapper::dump(String8& dump) { @@ -1066,8 +1081,14 @@ void CursorInputMapper::dump(String8& dump) { AutoMutex _l(mLock); dump.append(INDENT2 "Cursor Input Mapper:\n"); dumpParameters(dump); + dump.appendFormat(INDENT3 "XScale: %0.3f\n", mXScale); + 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 "VWheelScale: %0.3f\n", mVWheelScale); + dump.appendFormat(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down)); dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime); } // release lock @@ -1082,7 +1103,7 @@ void CursorInputMapper::configure() { // Configure device mode. switch (mParameters.mode) { case Parameters::MODE_POINTER: - mSources = 0; // AINPUT_SOURCE_MOUSE; disable mouse support + mSources = AINPUT_SOURCE_MOUSE; mXPrecision = 1.0f; mYPrecision = 1.0f; mXScale = 1.0f; @@ -1097,6 +1118,12 @@ void CursorInputMapper::configure() { mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; break; } + + mVWheelScale = 1.0f; + mHWheelScale = 1.0f; + + mHaveVWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_WHEEL); + mHaveHWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_HWHEEL); } void CursorInputMapper::configureParameters() { @@ -1170,7 +1197,14 @@ void CursorInputMapper::process(const RawEvent* rawEvent) { switch (rawEvent->type) { case EV_KEY: switch (rawEvent->scanCode) { - case BTN_MOUSE: + case BTN_LEFT: + case BTN_RIGHT: + case BTN_MIDDLE: + case BTN_SIDE: + case BTN_EXTRA: + case BTN_FORWARD: + case BTN_BACK: + case BTN_TASK: mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE; mAccumulator.btnMouse = rawEvent->value != 0; // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and @@ -1190,6 +1224,14 @@ void CursorInputMapper::process(const RawEvent* rawEvent) { 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; @@ -1212,6 +1254,7 @@ void CursorInputMapper::sync(nsecs_t when) { int motionEventAction; PointerCoords pointerCoords; nsecs_t downTime; + float vscroll, hscroll; { // acquire lock AutoMutex _l(mLock); @@ -1240,8 +1283,10 @@ void CursorInputMapper::sync(nsecs_t when) { if (downChanged) { motionEventAction = mLocked.down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else { + } else if (mLocked.down || mPointerController == NULL) { motionEventAction = AMOTION_EVENT_ACTION_MOVE; + } else { + motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE; } if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0 @@ -1275,24 +1320,34 @@ void CursorInputMapper::sync(nsecs_t when) { } } + pointerCoords.clear(); + if (mPointerController != NULL) { mPointerController->move(deltaX, deltaY); if (downChanged) { mPointerController->setButtonState(mLocked.down ? POINTER_BUTTON_1 : 0); } - mPointerController->getPosition(&pointerCoords.x, &pointerCoords.y); + float x, y; + mPointerController->getPosition(&x, &y); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); } else { - pointerCoords.x = deltaX; - pointerCoords.y = deltaY; + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); } - pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f; - pointerCoords.size = 0; - pointerCoords.touchMajor = 0; - pointerCoords.touchMinor = 0; - pointerCoords.toolMajor = 0; - pointerCoords.toolMinor = 0; - pointerCoords.orientation = 0; + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, mLocked.down ? 1.0f : 0.0f); + + if (mHaveVWheel && (fields & Accumulator::FIELD_REL_WHEEL)) { + vscroll = mAccumulator.relWheel; + } else { + vscroll = 0; + } + if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) { + hscroll = mAccumulator.relHWheel; + } else { + hscroll = 0; + } } // release lock int32_t metaState = mContext->getGlobalMetaState(); @@ -1302,6 +1357,15 @@ void CursorInputMapper::sync(nsecs_t when) { 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime); mAccumulator.clear(); + + if (vscroll != 0 || hscroll != 0) { + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); + + getDispatcher()->notifyMotion(when, getDeviceId(), mSources, 0, + AMOTION_EVENT_ACTION_SCROLL, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime); + } } int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { @@ -1341,35 +1405,35 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { // noticed immediately. configureSurfaceLocked(); - info->addMotionRange(AINPUT_MOTION_RANGE_X, mLocked.orientedRanges.x); - info->addMotionRange(AINPUT_MOTION_RANGE_Y, mLocked.orientedRanges.y); + info->addMotionRange(AMOTION_EVENT_AXIS_X, mLocked.orientedRanges.x); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mLocked.orientedRanges.y); if (mLocked.orientedRanges.havePressure) { - info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mLocked.orientedRanges.pressure); } if (mLocked.orientedRanges.haveSize) { - info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, + info->addMotionRange(AMOTION_EVENT_AXIS_SIZE, mLocked.orientedRanges.size); } if (mLocked.orientedRanges.haveTouchSize) { - info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, + info->addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, mLocked.orientedRanges.touchMajor); - info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, + info->addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MINOR, mLocked.orientedRanges.touchMinor); } if (mLocked.orientedRanges.haveToolSize) { - info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, + info->addMotionRange(AMOTION_EVENT_AXIS_TOOL_MAJOR, mLocked.orientedRanges.toolMajor); - info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, + info->addMotionRange(AMOTION_EVENT_AXIS_TOOL_MINOR, mLocked.orientedRanges.toolMinor); } if (mLocked.orientedRanges.haveOrientation) { - info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, + info->addMotionRange(AMOTION_EVENT_AXIS_ORIENTATION, mLocked.orientedRanges.orientation); } } // release lock @@ -1519,25 +1583,16 @@ void TouchInputMapper::configureRawAxes() { mRawAxes.orientation.clear(); } -static void dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) { - if (axis.valid) { - dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n", - name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); - } else { - dump.appendFormat(INDENT4 "%s: unknown range\n", name); - } -} - void TouchInputMapper::dumpRawAxes(String8& dump) { dump.append(INDENT3 "Raw Axes:\n"); - dumpAxisInfo(dump, mRawAxes.x, "X"); - dumpAxisInfo(dump, mRawAxes.y, "Y"); - dumpAxisInfo(dump, mRawAxes.pressure, "Pressure"); - dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor"); - dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor"); - dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor"); - dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor"); - dumpAxisInfo(dump, mRawAxes.orientation, "Orientation"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.pressure, "Pressure"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.orientation, "Orientation"); } bool TouchInputMapper::configureSurfaceLocked() { @@ -1803,7 +1858,7 @@ void TouchInputMapper::configureVirtualKeysLocked() { virtualKey.scanCode = virtualKeyDefinition.scanCode; int32_t keyCode; uint32_t flags; - if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode, + if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, & keyCode, & flags)) { LOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); @@ -2640,9 +2695,11 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, int32_t c2 = signExtendNybble(in.orientation & 0x0f); if (c1 != 0 || c2 != 0) { orientation = atan2f(c1, c2) * 0.5f; - float minorAxisScale = (16.0f - pythag(c1, c2)) / 16.0f; - toolMinor *= minorAxisScale; - touchMinor *= minorAxisScale; + float scale = 1.0f + pythag(c1, c2) / 16.0f; + touchMajor *= scale; + touchMinor /= scale; + toolMajor *= scale; + toolMinor /= scale; } else { orientation = 0; } @@ -2683,15 +2740,16 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, // Write output coords. PointerCoords& out = pointerCoords[outIndex]; - out.x = x; - out.y = y; - out.pressure = pressure; - out.size = size; - out.touchMajor = touchMajor; - out.touchMinor = touchMinor; - out.toolMajor = toolMajor; - out.toolMinor = toolMinor; - out.orientation = orientation; + out.clear(); + out.setAxisValue(AMOTION_EVENT_AXIS_X, x); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); pointerIds[outIndex] = int32_t(id); @@ -2703,14 +2761,17 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, // Check edge flags by looking only at the first pointer since the flags are // global to the event. if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { - if (pointerCoords[0].x <= 0) { + float x = pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X); + float y = pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y); + + if (x <= 0) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT; - } else if (pointerCoords[0].x >= mLocked.orientedSurfaceWidth) { + } else if (x >= mLocked.orientedSurfaceWidth) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT; } - if (pointerCoords[0].y <= 0) { + if (y <= 0) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP; - } else if (pointerCoords[0].y >= mLocked.orientedSurfaceHeight) { + } else if (y >= mLocked.orientedSurfaceHeight) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM; } } @@ -3705,4 +3766,235 @@ void MultiTouchInputMapper::configureRawAxes() { } +// --- JoystickInputMapper --- + +JoystickInputMapper::JoystickInputMapper(InputDevice* device) : + InputMapper(device) { +} + +JoystickInputMapper::~JoystickInputMapper() { +} + +uint32_t JoystickInputMapper::getSources() { + return AINPUT_SOURCE_JOYSTICK; +} + +void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + for (size_t i = 0; i < mAxes.size(); i++) { + const Axis& axis = mAxes.valueAt(i); + info->addMotionRange(axis.axis, axis.min, axis.max, axis.flat, axis.fuzz); + } +} + +void JoystickInputMapper::dump(String8& dump) { + dump.append(INDENT2 "Joystick Input Mapper:\n"); + + dump.append(INDENT3 "Axes:\n"); + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + const char* label = getAxisLabel(axis.axis); + char name[32]; + if (label) { + strncpy(name, label, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + } else { + snprintf(name, sizeof(name), "%d", axis.axis); + } + dump.appendFormat(INDENT4 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, " + "scale=%0.3f, offset=%0.3f\n", + name, axis.min, axis.max, axis.flat, axis.fuzz, + axis.scale, axis.offset); + dump.appendFormat(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, rawFlat=%d, rawFuzz=%d\n", + mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, + axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz); + } +} + +void JoystickInputMapper::configure() { + InputMapper::configure(); + + // Collect all axes. + for (int32_t abs = 0; abs <= ABS_MAX; abs++) { + RawAbsoluteAxisInfo rawAxisInfo; + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), abs, &rawAxisInfo); + if (rawAxisInfo.valid) { + int32_t axisId; + bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisId); + if (!explicitlyMapped) { + // Axis is not explicitly mapped, will choose a generic axis later. + axisId = -1; + } + + Axis axis; + if (isCenteredAxis(axisId)) { + float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; + axis.initialize(rawAxisInfo, axisId, explicitlyMapped, + scale, offset, -1.0f, 1.0f, + rawAxisInfo.flat * scale, rawAxisInfo.fuzz * scale); + } else { + float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + axis.initialize(rawAxisInfo, axisId, explicitlyMapped, + scale, 0.0f, 0.0f, 1.0f, + rawAxisInfo.flat * scale, rawAxisInfo.fuzz * scale); + } + + // To eliminate noise while the joystick is at rest, filter out small variations + // in axis values up front. + axis.filter = axis.flat * 0.25f; + + mAxes.add(abs, axis); + } + } + + // If there are too many axes, start dropping them. + // Prefer to keep explicitly mapped axes. + if (mAxes.size() > PointerCoords::MAX_AXES) { + LOGI("Joystick '%s' has %d axes but the framework only supports a maximum of %d.", + getDeviceName().string(), mAxes.size(), PointerCoords::MAX_AXES); + pruneAxes(true); + pruneAxes(false); + } + + // Assign generic axis ids to remaining axes. + int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + if (axis.axis < 0) { + while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 + && haveAxis(nextGenericAxisId)) { + nextGenericAxisId += 1; + } + + if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { + axis.axis = nextGenericAxisId; + nextGenericAxisId += 1; + } else { + LOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " + "have already been assigned to other axes.", + getDeviceName().string(), mAxes.keyAt(i)); + mAxes.removeItemsAt(i--); + numAxes -= 1; + } + } + } +} + +bool JoystickInputMapper::haveAxis(int32_t axis) { + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + if (mAxes.valueAt(i).axis == axis) { + return true; + } + } + return false; +} + +void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { + size_t i = mAxes.size(); + while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) { + if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) { + continue; + } + LOGI("Discarding joystick '%s' axis %d because there are too many axes.", + getDeviceName().string(), mAxes.keyAt(i)); + mAxes.removeItemsAt(i); + } +} + +bool JoystickInputMapper::isCenteredAxis(int32_t axis) { + switch (axis) { + case AMOTION_EVENT_AXIS_X: + case AMOTION_EVENT_AXIS_Y: + case AMOTION_EVENT_AXIS_Z: + case AMOTION_EVENT_AXIS_RX: + case AMOTION_EVENT_AXIS_RY: + case AMOTION_EVENT_AXIS_RZ: + case AMOTION_EVENT_AXIS_HAT_X: + case AMOTION_EVENT_AXIS_HAT_Y: + case AMOTION_EVENT_AXIS_ORIENTATION: + return true; + default: + return false; + } +} + +void JoystickInputMapper::reset() { + // Recenter all axes. + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + axis.newValue = 0; + } + + sync(when, true /*force*/); + + InputMapper::reset(); +} + +void JoystickInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_ABS: { + ssize_t index = mAxes.indexOfKey(rawEvent->scanCode); + if (index >= 0) { + Axis& axis = mAxes.editValueAt(index); + float newValue = rawEvent->value * axis.scale + axis.offset; + if (newValue != axis.newValue) { + axis.newValue = newValue; + } + } + break; + } + + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_REPORT: + sync(rawEvent->when, false /*force*/); + break; + } + break; + } +} + +void JoystickInputMapper::sync(nsecs_t when, bool force) { + if (!force && !haveAxesChangedSignificantly()) { + return; + } + + int32_t metaState = mContext->getGlobalMetaState(); + + PointerCoords pointerCoords; + pointerCoords.clear(); + + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + pointerCoords.setAxisValue(axis.axis, axis.newValue); + axis.oldValue = axis.newValue; + } + + int32_t pointerId = 0; + getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0, + AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, &pointerId, &pointerCoords, 0, 0, 0); +} + +bool JoystickInputMapper::haveAxesChangedSignificantly() { + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + if (axis.newValue != axis.oldValue + && fabs(axis.newValue - axis.oldValue) > axis.filter) { + return true; + } + } + return false; +} + } // namespace android diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 7619682..cf41535 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -354,6 +354,9 @@ public: protected: InputDevice* mDevice; InputReaderContext* mContext; + + static void dumpRawAbsoluteAxisInfo(String8& dump, + const RawAbsoluteAxisInfo& axis, const char* name); }; @@ -478,7 +481,9 @@ private: enum { FIELD_BTN_MOUSE = 1, FIELD_REL_X = 2, - FIELD_REL_Y = 4 + FIELD_REL_Y = 4, + FIELD_REL_WHEEL = 8, + FIELD_REL_HWHEEL = 16, }; uint32_t fields; @@ -486,6 +491,8 @@ private: bool btnMouse; int32_t relX; int32_t relY; + int32_t relWheel; + int32_t relHWheel; inline void clear() { fields = 0; @@ -497,6 +504,12 @@ private: float mYScale; float mXPrecision; float mYPrecision; + + bool mHaveVWheel; + bool mHaveHWheel; + float mVWheelScale; + float mHWheelScale; + sp<PointerControllerInterface> mPointerController; struct LockedState { @@ -968,6 +981,69 @@ private: void sync(nsecs_t when); }; + +class JoystickInputMapper : public InputMapper { +public: + JoystickInputMapper(InputDevice* device); + virtual ~JoystickInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(String8& dump); + virtual void configure(); + virtual void reset(); + virtual void process(const RawEvent* rawEvent); + +private: + struct Axis { + RawAbsoluteAxisInfo rawAxisInfo; + + int32_t axis; // axis id + bool explicitlyMapped; // true if the axis was explicitly assigned an axis id + + float scale; // scale factor from raw to normalized values + float offset; // offset to add after scaling for normalization + + float min; // normalized inclusive minimum + float max; // normalized inclusive maximum + float flat; // normalized flat region size + float fuzz; // normalized error tolerance + + float oldValue; // previous value + float newValue; // most recent value + + float filter; // filter out small variations of this size + + void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, + int32_t axis, bool explicitlyMapped, float scale, float offset, + float min, float max, float flat, float fuzz) { + this->rawAxisInfo = rawAxisInfo; + this->axis = axis; + this->explicitlyMapped = explicitlyMapped; + this->scale = scale; + this->offset = offset; + this->min = min; + this->max = max; + this->flat = flat; + this->fuzz = fuzz; + this->filter = 0; + this->oldValue = 0; + this->newValue = 0; + } + }; + + // Axes indexed by raw ABS_* axis index. + KeyedVector<int32_t, Axis> mAxes; + + void sync(nsecs_t when, bool force); + + bool haveAxis(int32_t axis); + void pruneAxes(bool ignoreExplicitlyMappedAxes); + bool haveAxesChangedSignificantly(); + + static bool isCenteredAxis(int32_t axis); +}; + } // namespace android #endif // _UI_INPUT_READER_H diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index 25030d8..fac71bb 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -561,7 +561,11 @@ private: return -1; } - virtual status_t scancodeToKeycode(int32_t deviceId, int scancode, + virtual bool hasRelativeAxis(int32_t deviceId, int axis) const { + return false; + } + + virtual status_t mapKey(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const { Device* device = getDevice(deviceId); if (device) { @@ -579,6 +583,11 @@ private: return NAME_NOT_FOUND; } + virtual status_t mapAxis(int32_t deviceId, int scancode, + int32_t* outAxis) const { + return NAME_NOT_FOUND; + } + virtual void addExcludedDevice(const char* deviceName) { mExcludedDevices.add(String8(deviceName)); } @@ -878,7 +887,8 @@ public: InstrumentedInputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputDispatcherInterface>& dispatcher) : - InputReader(eventHub, policy, dispatcher) { + InputReader(eventHub, policy, dispatcher), + mNextDevice(NULL) { } virtual ~InstrumentedInputReader() { @@ -1472,15 +1482,15 @@ protected: float x, float y, float pressure, float size, float touchMajor, float touchMinor, float toolMajor, float toolMinor, float orientation) { - ASSERT_NEAR(x, coords.x, 1); - ASSERT_NEAR(y, coords.y, 1); - ASSERT_NEAR(pressure, coords.pressure, EPSILON); - ASSERT_NEAR(size, coords.size, EPSILON); - ASSERT_NEAR(touchMajor, coords.touchMajor, 1); - ASSERT_NEAR(touchMinor, coords.touchMinor, 1); - ASSERT_NEAR(toolMajor, coords.toolMajor, 1); - ASSERT_NEAR(toolMinor, coords.toolMinor, 1); - ASSERT_NEAR(orientation, coords.orientation, EPSILON); + ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON); + ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON); + ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), 1); + ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), 1); + ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), 1); + ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), 1); + ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON); } }; @@ -2077,7 +2087,6 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { 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_MOVE, args.action); - ASSERT_NEAR(0.0f, args.pointerCoords[0].x, EPSILON); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); } @@ -2892,8 +2901,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotate processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(50, args.pointerCoords[0].x, 1); - ASSERT_NEAR(75, args.pointerCoords[0].y, 1); + ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2914,8 +2923,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(50, args.pointerCoords[0].x, 1); - ASSERT_NEAR(75, args.pointerCoords[0].y, 1); + ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2927,8 +2936,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(75, args.pointerCoords[0].x, 1); - ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].y, 1); + ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2940,8 +2949,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].x, 1); - ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].y, 1); + ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2953,8 +2962,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].x, 1); - ASSERT_NEAR(50, args.pointerCoords[0].y, 1); + ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 5a36417..8c07e15 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -63,7 +63,10 @@ class AlarmManagerService extends IAlarmManager.Stub { private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP; private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME; private static final int TIME_CHANGED_MASK = 1 << 16; - + + // Alignment quantum for inexact repeating alarms + private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES; + private static final String TAG = "AlarmManager"; private static final String ClockReceiver_TAG = "ClockReceiver"; private static final boolean localLOGV = false; @@ -83,17 +86,6 @@ class AlarmManagerService extends IAlarmManager.Stub { private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>(); private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder(); - // slots corresponding with the inexact-repeat interval buckets, - // ordered from shortest to longest - private static final long sInexactSlotIntervals[] = { - AlarmManager.INTERVAL_FIFTEEN_MINUTES, - AlarmManager.INTERVAL_HALF_HOUR, - AlarmManager.INTERVAL_HOUR, - AlarmManager.INTERVAL_HALF_DAY, - AlarmManager.INTERVAL_DAY - }; - private long mInexactDeliveryTimes[] = { 0, 0, 0, 0, 0}; - private int mDescriptor; private int mBroadcastRefCount = 0; private PowerManager.WakeLock mWakeLock; @@ -199,58 +191,40 @@ class AlarmManagerService extends IAlarmManager.Stub { return; } - // find the slot in the delivery-times array that we will use - int intervalSlot; - for (intervalSlot = 0; intervalSlot < sInexactSlotIntervals.length; intervalSlot++) { - if (sInexactSlotIntervals[intervalSlot] == interval) { - break; - } + if (interval <= 0) { + Slog.w(TAG, "setInexactRepeating ignored because interval " + interval + + " is invalid"); + return; } - - // Non-bucket intervals just fall back to the less-efficient - // unbucketed recurring alarm implementation - if (intervalSlot >= sInexactSlotIntervals.length) { + + // If the requested interval isn't a multiple of 15 minutes, just treat it as exact + if (interval % QUANTUM != 0) { + if (localLOGV) Slog.v(TAG, "Interval " + interval + " not a quantum multiple"); setRepeating(type, triggerAtTime, interval, operation); return; } - // Align bucketed alarm deliveries by trying to match - // the shortest-interval bucket already scheduled - long bucketTime = 0; - for (int slot = 0; slot < mInexactDeliveryTimes.length; slot++) { - if (mInexactDeliveryTimes[slot] > 0) { - bucketTime = mInexactDeliveryTimes[slot]; - break; - } - } - - if (bucketTime == 0) { - // If nothing is scheduled yet, just start at the requested time - bucketTime = triggerAtTime; - } else { - // Align the new alarm with the existing bucketed sequence. To achieve - // alignment, we slide the start time around by min{interval, slot interval} - long adjustment = (interval <= sInexactSlotIntervals[intervalSlot]) - ? interval : sInexactSlotIntervals[intervalSlot]; - - // The bucket may have started in the past; adjust - while (bucketTime < triggerAtTime) { - bucketTime += adjustment; - } + // Translate times into the ELAPSED timebase for alignment purposes so that + // alignment never tries to match against wall clock times. + final boolean isRtc = (type == AlarmManager.RTC || type == AlarmManager.RTC_WAKEUP); + final long skew = (isRtc) + ? System.currentTimeMillis() - SystemClock.elapsedRealtime() + : 0; - // Or the bucket may be set to start more than an interval beyond - // our requested trigger time; pull it back to meet our needs - while (bucketTime > triggerAtTime + adjustment) { - bucketTime -= adjustment; - } + // Slip forward to the next ELAPSED-timebase quantum after the stated time. If + // we're *at* a quantum point, leave it alone. + final long adjustedTriggerTime; + long offset = (triggerAtTime - skew) % QUANTUM; + if (offset != 0) { + adjustedTriggerTime = triggerAtTime - offset + QUANTUM; + } else { + adjustedTriggerTime = triggerAtTime; } - // Remember where this bucket started (reducing the amount of later - // fixup required) and set the alarm with the new, bucketed start time. - if (localLOGV) Slog.v(TAG, "setInexactRepeating: interval=" + interval - + " bucketTime=" + bucketTime); - mInexactDeliveryTimes[intervalSlot] = bucketTime; - setRepeating(type, bucketTime, interval, operation); + // Set the alarm based on the quantum-aligned start time + if (localLOGV) Slog.v(TAG, "setInexactRepeating: type=" + type + " interval=" + interval + + " trigger=" + adjustedTriggerTime + " orig=" + triggerAtTime); + setRepeating(type, adjustedTriggerTime, interval, operation); } public void setTime(long millis) { diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index ad25657..f28e2b1 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -22,7 +22,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -57,7 +56,6 @@ import android.content.res.XmlResourceParser; import android.net.Uri; import android.os.Binder; import android.os.Bundle; -import android.os.Handler; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; @@ -69,11 +67,13 @@ import android.util.Slog; import android.util.TypedValue; import android.util.Xml; import android.widget.RemoteViews; +import android.widget.RemoteViewsService; import com.android.internal.appwidget.IAppWidgetHost; import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.util.FastXmlSerializer; import com.android.internal.widget.IRemoteViewsAdapterConnection; +import com.android.internal.widget.IRemoteViewsFactory; class AppWidgetService extends IAppWidgetService.Stub { @@ -153,9 +153,12 @@ class AppWidgetService extends IAppWidgetService.Stub } } - // Manages connections to RemoteViewsServices + // Manages active connections to RemoteViewsServices private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>(); + // Manages persistent references to RemoteViewsServices from different App Widgets + private final HashMap<FilterComparison, HashSet<Integer>> + mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>(); Context mContext; Locale mLocale; @@ -429,6 +432,7 @@ class AppWidgetService extends IAppWidgetService.Stub } } + // Binds to a specific RemoteViewsService public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) { synchronized (mAppWidgetIds) { AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId); @@ -452,8 +456,8 @@ class AppWidgetService extends IAppWidgetService.Stub // that first. (This does not allow multiple connections to the same service under // the same key) ServiceConnectionProxy conn = null; - Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, - new FilterComparison(intent)); + FilterComparison fc = new FilterComparison(intent); + Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc); if (mBoundRemoteViewsServices.containsKey(key)) { conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key); conn.disconnect(); @@ -471,9 +475,15 @@ class AppWidgetService extends IAppWidgetService.Stub } finally { Binder.restoreCallingIdentity(token); } + + // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine + // when we can call back to the RemoteViewsService later to destroy associated + // factories. + incrementAppWidgetServiceRefCount(appWidgetId, fc); } } + // Unbinds from a specific RemoteViewsService public void unbindRemoteViewsService(int appWidgetId, Intent intent) { synchronized (mAppWidgetIds) { // Unbind from the RemoteViewsService (which will trigger a callback to the bound @@ -500,6 +510,7 @@ class AppWidgetService extends IAppWidgetService.Stub } } + // Unbinds from a RemoteViewsService when we delete an app widget private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) { int appWidgetId = id.appWidgetId; // Unbind all connections to Services bound to this AppWidgetId @@ -515,6 +526,71 @@ class AppWidgetService extends IAppWidgetService.Stub it.remove(); } } + + // Check if we need to destroy any services (if no other app widgets are + // referencing the same service) + decrementAppWidgetServiceRefCount(appWidgetId); + } + + // Destroys the cached factory on the RemoteViewsService's side related to the specified intent + private void destroyRemoteViewsService(final Intent intent) { + final ServiceConnection conn = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + final IRemoteViewsFactory cb = + IRemoteViewsFactory.Stub.asInterface(service); + try { + cb.onDestroy(intent); + } catch (RemoteException e) { + e.printStackTrace(); + } + mContext.unbindService(this); + } + @Override + public void onServiceDisconnected(android.content.ComponentName name) { + // Do nothing + } + }; + + // Bind to the service and remove the static intent->factory mapping in the + // RemoteViewsService. + final long token = Binder.clearCallingIdentity(); + try { + mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + // Adds to the ref-count for a given RemoteViewsService intent + private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) { + HashSet<Integer> appWidgetIds = null; + if (mRemoteViewsServicesAppWidgets.containsKey(fc)) { + appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc); + } else { + appWidgetIds = new HashSet<Integer>(); + mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds); + } + appWidgetIds.add(appWidgetId); + } + + // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if + // the ref-count reaches zero. + private void decrementAppWidgetServiceRefCount(int appWidgetId) { + Iterator<FilterComparison> it = + mRemoteViewsServicesAppWidgets.keySet().iterator(); + while (it.hasNext()) { + final FilterComparison key = it.next(); + final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key); + if (ids.remove(appWidgetId)) { + // If we have removed the last app widget referencing this service, then we + // should destroy it and remove it from this set + if (ids.isEmpty()) { + destroyRemoteViewsService(key.getIntent()); + it.remove(); + } + } + } } public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) { @@ -897,15 +973,15 @@ class AppWidgetService extends IAppWidgetService.Stub + "AppWidget provider '" + component + '\''); return null; } - + AttributeSet attrs = Xml.asAttributeSet(parser); - + int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { // drain whitespace, comments, etc. } - + String nodeName = parser.getName(); if (!"appwidget-provider".equals(nodeName)) { Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for" @@ -925,10 +1001,10 @@ class AppWidgetService extends IAppWidgetService.Stub Resources res = mPackageManager.getResourcesForApplication( activityInfo.applicationInfo); - + TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AppWidgetProviderInfo); - + // These dimensions has to be resolved in the application's context. // We simply send back the raw complex data, which will be // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}. @@ -937,7 +1013,7 @@ class AppWidgetService extends IAppWidgetService.Stub info.minWidth = value != null ? value.data : 0; value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight); info.minHeight = value != null ? value.data : 0; - + info.updatePeriodMillis = sa.getInt( com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0); info.initialLayout = sa.getResourceId( @@ -953,6 +1029,9 @@ class AppWidgetService extends IAppWidgetService.Stub com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0); info.autoAdvanceViewId = sa.getResourceId( com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1); + info.resizeMode = sa.getInt( + com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode, + AppWidgetProviderInfo.RESIZE_NONE); sa.recycle(); } catch (Exception e) { diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 2f12a95..b7d0a8f 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -286,8 +286,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { com.android.internal.R.string.config_default_dns_server); } try { - mDefaultDns = InetAddress.getByName(dns); - } catch (UnknownHostException e) { + mDefaultDns = NetworkUtils.numericToInetAddress(dns); + } catch (IllegalArgumentException e) { loge("Error setting defaultDns using " + dns); } @@ -696,15 +696,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { // TODO - move this into the MobileDataStateTracker int usedNetworkType = networkType; if(networkType == ConnectivityManager.TYPE_MOBILE) { - if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { - usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS; - } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { - usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL; - } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) || - TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) { - usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN; - } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) { - usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI; + usedNetworkType = convertFeatureToNetworkType(feature); + if (usedNetworkType < 0) { + Slog.e(TAG, "Can't match any netTracker!"); + usedNetworkType = networkType; } } NetworkStateTracker network = mNetTrackers[usedNetworkType]; @@ -848,15 +843,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { // TODO - move to MobileDataStateTracker int usedNetworkType = networkType; if (networkType == ConnectivityManager.TYPE_MOBILE) { - if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { - usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS; - } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { - usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL; - } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) || - TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) { - usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN; - } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) { - usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI; + usedNetworkType = convertFeatureToNetworkType(feature); + if (usedNetworkType < 0) { + usedNetworkType = networkType; } } tracker = mNetTrackers[usedNetworkType]; @@ -1412,13 +1401,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { LinkProperties p = nt.getLinkProperties(); if (p == null) return; String interfaceName = p.getInterfaceName(); - InetAddress defaultGatewayAddr = p.getGateway(); + if (TextUtils.isEmpty(interfaceName)) return; + for (InetAddress gateway : p.getGateways()) { - if ((interfaceName != null) && (defaultGatewayAddr != null )) { - if (!NetworkUtils.addDefaultRoute(interfaceName, defaultGatewayAddr) && DBG) { + if (!NetworkUtils.addDefaultRoute(interfaceName, gateway) && DBG) { NetworkInfo networkInfo = nt.getNetworkInfo(); log("addDefaultRoute for " + networkInfo.getTypeName() + - " (" + interfaceName + "), GatewayAddr=" + defaultGatewayAddr); + " (" + interfaceName + "), GatewayAddr=" + gateway.getHostAddress()); } } } @@ -2140,7 +2129,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (proxy == null) proxy = new ProxyProperties("", 0, ""); log("sending Proxy Broadcast for " + proxy); Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING | + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy); mContext.sendStickyBroadcast(intent); } @@ -2173,4 +2163,24 @@ public class ConnectivityService extends IConnectivityManager.Stub { private void loge(String s) { Slog.e(TAG, s); } + int convertFeatureToNetworkType(String feature){ + int networkType = -1; + if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { + networkType = ConnectivityManager.TYPE_MOBILE_MMS; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { + networkType = ConnectivityManager.TYPE_MOBILE_SUPL; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) || + TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) { + networkType = ConnectivityManager.TYPE_MOBILE_DUN; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) { + networkType = ConnectivityManager.TYPE_MOBILE_HIPRI; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_FOTA)) { + networkType = ConnectivityManager.TYPE_MOBILE_FOTA; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_IMS)) { + networkType = ConnectivityManager.TYPE_MOBILE_IMS; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_CBS)) { + networkType = ConnectivityManager.TYPE_MOBILE_CBS; + } + return networkType; + } } diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index b2d534b..df2cd1b 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -79,9 +79,9 @@ import java.util.Set; * Implementation of the device policy APIs. */ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { - private static final int REQUEST_EXPIRE_PASSWORD = 5571; + private static final String TAG = "DevicePolicyManagerService"; - static final String TAG = "DevicePolicyManagerService"; + private static final int REQUEST_EXPIRE_PASSWORD = 5571; private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * 86400 * 1000; // 5 days, in ms diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 6636fb7..44b8590 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -26,7 +26,7 @@ import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputBindResult; -import com.android.server.StatusBarManagerService; +import com.android.server.EventLogTags; import org.xmlpull.v1.XmlPullParserException; @@ -330,6 +330,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.DEFAULT_INPUT_METHOD), false, this); resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.ENABLED_INPUT_METHODS), false, this); + resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this); } @@ -594,13 +596,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (imi == null && mCurMethodId != null) { imi = mMethodMap.get(mCurMethodId); } - final List<InputMethodSubtype> enabledSubtypes = + List<InputMethodSubtype> enabledSubtypes = mSettings.getEnabledInputMethodSubtypeListLocked(imi); - if (!allowsImplicitlySelectedSubtypes || enabledSubtypes.size() > 0) { - return enabledSubtypes; - } else { - return getApplicableSubtypesLocked(mRes, getSubtypes(imi)); + if (allowsImplicitlySelectedSubtypes && enabledSubtypes.isEmpty()) { + enabledSubtypes = getApplicableSubtypesLocked(mRes, getSubtypes(imi)); } + return InputMethodSubtype.sort(mContext, 0, imi, enabledSubtypes); } public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi, @@ -1948,14 +1949,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private boolean canAddToLastInputMethod(InputMethodSubtype subtype) { if (subtype == null) return true; - String[] extraValues = subtype.getExtraValue().split(","); - final int N = extraValues.length; - for (int i = 0; i < N; ++i) { - if (SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME.equals(extraValues[i])) { - return false; - } - } - return true; + return !subtype.containsExtraValueKey(SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME); } private void saveCurrentInputMethodAndSubtypeToHistory() { diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/Installer.java index 85eca60..08d1b82 100644 --- a/services/java/com/android/server/Installer.java +++ b/services/java/com/android/server/Installer.java @@ -166,17 +166,11 @@ class Installer { } } - public int install(String name, boolean useEncryptedFilesystem, int uid, int gid) { + public int install(String name, int uid, int gid) { StringBuilder builder = new StringBuilder("install"); builder.append(' '); builder.append(name); builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } - builder.append(' '); builder.append(uid); builder.append(' '); builder.append(gid); @@ -209,57 +203,33 @@ class Installer { return execute(builder.toString()); } - public int remove(String name, boolean useEncryptedFilesystem) { + public int remove(String name) { StringBuilder builder = new StringBuilder("remove"); builder.append(' '); builder.append(name); - builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } return execute(builder.toString()); } - public int rename(String oldname, String newname, boolean useEncryptedFilesystem) { + public int rename(String oldname, String newname) { StringBuilder builder = new StringBuilder("rename"); builder.append(' '); builder.append(oldname); builder.append(' '); builder.append(newname); - builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } return execute(builder.toString()); } - public int deleteCacheFiles(String name, boolean useEncryptedFilesystem) { + public int deleteCacheFiles(String name) { StringBuilder builder = new StringBuilder("rmcache"); builder.append(' '); builder.append(name); - builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } return execute(builder.toString()); } - public int clearUserData(String name, boolean useEncryptedFilesystem) { + public int clearUserData(String name) { StringBuilder builder = new StringBuilder("rmuserdata"); builder.append(' '); builder.append(name); - builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } return execute(builder.toString()); } @@ -292,8 +262,8 @@ class Installer { return execute(builder.toString()); } - public int getSizeInfo(String pkgName, String apkPath, - String fwdLockApkPath, PackageStats pStats, boolean useEncryptedFilesystem) { + public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath, + PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); @@ -301,12 +271,6 @@ class Installer { builder.append(apkPath); builder.append(' '); builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); - builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } String s = transaction(builder.toString()); String res[] = s.split(" "); diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java index a8b2840..b78389b 100644 --- a/services/java/com/android/server/IntentResolver.java +++ b/services/java/com/android/server/IntentResolver.java @@ -27,6 +27,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import android.net.Uri; +import android.util.FastImmutableArraySet; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Slog; @@ -34,7 +36,6 @@ import android.util.LogPrinter; import android.util.Printer; import android.util.Config; -import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; @@ -207,10 +208,11 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { final boolean debug = localLOGV || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); + FastImmutableArraySet<String> categories = getFastIntentCategories(intent); final String scheme = intent.getScheme(); int N = listCut.size(); for (int i = 0; i < N; ++i) { - buildResolveList(intent, debug, defaultOnly, + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, listCut.get(i), resultList); } sortResults(resultList); @@ -286,20 +288,21 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { if (debug) Slog.v(TAG, "Action list: " + firstTypeCut); } + FastImmutableArraySet<String> categories = getFastIntentCategories(intent); if (firstTypeCut != null) { - buildResolveList(intent, debug, defaultOnly, + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, firstTypeCut, finalList); } if (secondTypeCut != null) { - buildResolveList(intent, debug, defaultOnly, + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, secondTypeCut, finalList); } if (thirdTypeCut != null) { - buildResolveList(intent, debug, defaultOnly, + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, thirdTypeCut, finalList); } if (schemeCut != null) { - buildResolveList(intent, debug, defaultOnly, + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, schemeCut, finalList); } sortResults(finalList); @@ -322,6 +325,15 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { return true; } + /** + * Returns whether the object associated with the given filter is + * "stopped," that is whether it should not be included in the result + * if the intent requests to excluded stopped objects. + */ + protected boolean isFilterStopped(F filter) { + return false; + } + protected String packageForFilter(F filter) { return null; } @@ -478,9 +490,21 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { return false; } - private void buildResolveList(Intent intent, boolean debug, boolean defaultOnly, + private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) { + final Set<String> categories = intent.getCategories(); + if (categories == null) { + return null; + } + return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()])); + } + + private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories, + boolean debug, boolean defaultOnly, String resolvedType, String scheme, List<F> src, List<R> dest) { - Set<String> categories = intent.getCategories(); + final String action = intent.getAction(); + final Uri data = intent.getData(); + + final boolean excludingStopped = intent.isExcludingStopped(); final int N = src != null ? src.size() : 0; boolean hasNonDefaults = false; @@ -490,6 +514,13 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { int match; if (debug) Slog.v(TAG, "Matching against filter " + filter); + if (excludingStopped && isFilterStopped(filter)) { + if (debug) { + Slog.v(TAG, " Filter's target is stopped; skipping"); + } + continue; + } + // Do we already have this one? if (!allowFilterResult(filter, dest)) { if (debug) { @@ -498,8 +529,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { continue; } - match = filter.match( - intent.getAction(), resolvedType, scheme, intent.getData(), categories, TAG); + match = filter.match(action, resolvedType, scheme, data, categories, TAG); if (match >= 0) { if (debug) Slog.v(TAG, " Filter matched! match=0x" + Integer.toHexString(match)); diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 152605f..44f5df2 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -26,6 +26,8 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.net.InterfaceConfiguration; import android.net.INetworkManagementEventObserver; +import android.net.LinkAddress; +import android.net.NetworkUtils; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.os.INetworkManagementService; @@ -246,7 +248,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { } Slog.d(TAG, String.format("rsp <%s>", rsp)); - // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3] + // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz [flag1 flag2 flag3] StringTokenizer st = new StringTokenizer(rsp); InterfaceConfiguration cfg; @@ -265,18 +267,21 @@ class NetworkManagementService extends INetworkManagementService.Stub { cfg = new InterfaceConfiguration(); cfg.hwAddr = st.nextToken(" "); + InetAddress addr = null; + int prefixLength = 0; try { - cfg.addr = InetAddress.getByName(st.nextToken(" ")); - } catch (UnknownHostException uhe) { - Slog.e(TAG, "Failed to parse ipaddr", uhe); + addr = NetworkUtils.numericToInetAddress(st.nextToken(" ")); + } catch (IllegalArgumentException iae) { + Slog.e(TAG, "Failed to parse ipaddr", iae); } try { - cfg.mask = InetAddress.getByName(st.nextToken(" ")); - } catch (UnknownHostException uhe) { - Slog.e(TAG, "Failed to parse netmask", uhe); + prefixLength = Integer.parseInt(st.nextToken(" ")); + } catch (NumberFormatException nfe) { + Slog.e(TAG, "Failed to parse prefixLength", nfe); } + cfg.addr = new LinkAddress(addr, prefixLength); cfg.interfaceFlags = st.nextToken("]").trim() +"]"; } catch (NoSuchElementException nsee) { throw new IllegalStateException( @@ -288,8 +293,13 @@ class NetworkManagementService extends INetworkManagementService.Stub { public void setInterfaceConfig( String iface, InterfaceConfiguration cfg) throws IllegalStateException { - String cmd = String.format("interface setcfg %s %s %s %s", iface, - cfg.addr.getHostAddress(), cfg.mask.getHostAddress(), + LinkAddress linkAddr = cfg.addr; + if (linkAddr == null || linkAddr.getAddress() == null) { + throw new IllegalStateException("Null LinkAddress given"); + } + String cmd = String.format("interface setcfg %s %s %d %s", iface, + linkAddr.getAddress().getHostAddress(), + linkAddr.getNetworkPrefixLength(), cfg.interfaceFlags); try { mConnector.doCommand(cmd); @@ -441,7 +451,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { try { String cmd = "tether dns set"; for (String s : dns) { - cmd += " " + InetAddress.getByName(s).getHostAddress(); + cmd += " " + NetworkUtils.numericToInetAddress(s).getHostAddress(); } try { mConnector.doCommand(cmd); @@ -449,7 +459,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { throw new IllegalStateException( "Unable to communicate to native daemon for setting tether dns"); } - } catch (UnknownHostException e) { + } catch (IllegalArgumentException e) { throw new IllegalStateException("Error resolving dns name", e); } } @@ -509,11 +519,11 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty, - InetAddress.getByName(localAddr).getHostAddress(), - InetAddress.getByName(remoteAddr).getHostAddress(), - InetAddress.getByName(dns1Addr).getHostAddress(), - InetAddress.getByName(dns2Addr).getHostAddress())); - } catch (UnknownHostException e) { + NetworkUtils.numericToInetAddress(localAddr).getHostAddress(), + NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(), + NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(), + NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress())); + } catch (IllegalArgumentException e) { throw new IllegalStateException("Error resolving addr", e); } catch (NativeDaemonConnectorException e) { throw new IllegalStateException("Error communicating to native daemon to attach pppd", e); @@ -600,11 +610,10 @@ class NetworkManagementService extends INetworkManagementService.Stub { * argv7 - Preamble * argv8 - Max SCB */ - String str = String.format("softap set " + wlanIface + " " + softapIface + - " %s %s %s", convertQuotedString(wifiConfig.SSID), - wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? - "wpa2-psk" : "open", - convertQuotedString(wifiConfig.preSharedKey)); + String str = String.format("softap set " + wlanIface + " " + softapIface + + " %s %s %s", convertQuotedString(wifiConfig.SSID), + getSecurityType(wifiConfig), + convertQuotedString(wifiConfig.preSharedKey)); mConnector.doCommand(str); } mConnector.doCommand(String.format("softap startap")); @@ -621,6 +630,17 @@ class NetworkManagementService extends INetworkManagementService.Stub { return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"'; } + private String getSecurityType(WifiConfiguration wifiConfig) { + switch (wifiConfig.getAuthType()) { + case KeyMgmt.WPA_PSK: + return "wpa-psk"; + case KeyMgmt.WPA2_PSK: + return "wpa2-psk"; + default: + return "open"; + } + } + public void stopAccessPoint() throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); @@ -646,7 +666,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { } else { String str = String.format("softap set " + wlanIface + " " + softapIface + " %s %s %s", convertQuotedString(wifiConfig.SSID), - wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? "wpa2-psk" : "open", + getSecurityType(wifiConfig), convertQuotedString(wifiConfig.preSharedKey)); mConnector.doCommand(str); } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 47dce41..e738145 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -37,7 +37,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.database.ContentObserver; -import android.hardware.UsbManager; +import android.hardware.usb.UsbManager; import android.media.AudioManager; import android.net.Uri; import android.os.Binder; diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 9ee71e8..461a3e5 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -85,7 +85,6 @@ import android.os.Process; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.provider.Settings; import android.security.SystemKeyStore; import android.util.*; import android.view.Display; @@ -114,12 +113,10 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.zip.ZipEntry; -import java.util.zip.ZipException; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; @@ -140,6 +137,7 @@ class PackageManagerService extends IPackageManager.Stub { private static final boolean DEBUG_PREFERRED = false; private static final boolean DEBUG_UPGRADE = false; private static final boolean DEBUG_INSTALL = false; + private static final boolean DEBUG_STOPPED = false; private static final boolean MULTIPLE_APPLICATION_UIDS = true; private static final int RADIO_UID = Process.PHONE_UID; @@ -153,8 +151,6 @@ class PackageManagerService extends IPackageManager.Stub { private static final boolean GET_CERTIFICATES = true; - private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled"; - private static final int REMOVE_EVENTS = FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_FROM; private static final int ADD_EVENTS = @@ -216,10 +212,6 @@ class PackageManagerService extends IPackageManager.Stub { // This is where all application persistent data goes. final File mAppDataDir; - // If Encrypted File System feature is enabled, all application persistent data - // should go here instead. - final File mSecureAppDataDir; - // This is the object monitoring the framework dir. final FileObserver mFrameworkInstallObserver; @@ -364,6 +356,7 @@ class PackageManagerService extends IPackageManager.Stub { static final int MCS_GIVE_UP = 11; static final int UPDATED_MEDIA_STATUS = 12; static final int WRITE_SETTINGS = 13; + static final int WRITE_STOPPED_PACKAGES = 14; static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds @@ -607,11 +600,14 @@ class PackageManagerService extends IPackageManager.Stub { } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, res.pkg.applicationInfo.packageName, - extras, null); + extras, null, null); if (update) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, res.pkg.applicationInfo.packageName, - extras, null); + extras, null, null); + sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, + null, null, + res.pkg.applicationInfo.packageName, null); } if (res.removedInfo.args != null) { // Remove the replaced package's older resources safely now @@ -665,10 +661,19 @@ class PackageManagerService extends IPackageManager.Stub { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mPackages) { removeMessages(WRITE_SETTINGS); + removeMessages(WRITE_STOPPED_PACKAGES); mSettings.writeLP(); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; + case WRITE_STOPPED_PACKAGES: { + Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + synchronized (mPackages) { + removeMessages(WRITE_STOPPED_PACKAGES); + mSettings.writeStoppedLP(); + } + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + } break; } } } @@ -679,6 +684,12 @@ class PackageManagerService extends IPackageManager.Stub { } } + void scheduleWriteStoppedPackagesLocked() { + if (!mHandler.hasMessages(WRITE_STOPPED_PACKAGES)) { + mHandler.sendEmptyMessageDelayed(WRITE_STOPPED_PACKAGES, WRITE_SETTINGS_DELAY); + } + } + static boolean installOnSd(int flags) { if (((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) || ((flags & PackageManager.INSTALL_INTERNAL) != 0)) { @@ -785,7 +796,6 @@ class PackageManagerService extends IPackageManager.Stub { File dataDir = Environment.getDataDirectory(); mAppDataDir = new File(dataDir, "data"); - mSecureAppDataDir = new File(dataDir, "secure/data"); mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); if (mInstaller == null) { @@ -795,7 +805,6 @@ class PackageManagerService extends IPackageManager.Stub { File miscDir = new File(dataDir, "misc"); miscDir.mkdirs(); mAppDataDir.mkdirs(); - mSecureAppDataDir.mkdirs(); mDrmAppPrivateInstallDir.mkdirs(); } @@ -964,9 +973,7 @@ class PackageManagerService extends IPackageManager.Stub { + " no longer exists; wiping its data"; reportSettingsProblem(Log.WARN, msg); if (mInstaller != null) { - // XXX how to set useEncryptedFSDir for packages that - // are not encrypted? - mInstaller.remove(ps.name, true); + mInstaller.remove(ps.name); } } } @@ -1050,8 +1057,7 @@ class PackageManagerService extends IPackageManager.Stub { void cleanupInstallFailedPackage(PackageSetting ps) { Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name); if (mInstaller != null) { - boolean useSecureFS = false; - int retCode = mInstaller.remove(ps.name, useSecureFS); + int retCode = mInstaller.remove(ps.name); if (retCode < 0) { Slog.w(TAG, "Couldn't remove app data directory for package: " + ps.name + ", retcode=" + retCode); @@ -1498,6 +1504,7 @@ class PackageManagerService extends IPackageManager.Stub { ps.pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg).getPath(); ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; ps.pkg.mSetEnabled = ps.enabled; + ps.pkg.mSetStopped = ps.stopped; } return generatePackageInfo(ps.pkg, flags); } @@ -2796,13 +2803,7 @@ class PackageManagerService extends IPackageManager.Stub { } private File getDataPathForPackage(PackageParser.Package pkg) { - boolean useEncryptedFSDir = false; - File dataPath; - if (useEncryptedFSDir) { - dataPath = new File(mSecureAppDataDir, pkg.packageName); - } else { - dataPath = new File(mAppDataDir, pkg.packageName); - } + final File dataPath = new File(mAppDataDir, pkg.packageName); return dataPath; } @@ -3132,7 +3133,6 @@ class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.dataDir = dataPath.getPath(); } else { // This is a normal package, need to make its data directory. - boolean useEncryptedFSDir = false; dataPath = getDataPathForPackage(pkg); boolean uidError = false; @@ -3149,7 +3149,7 @@ class PackageManagerService extends IPackageManager.Stub { // If this is a system app, we can at least delete its // current data so the application will still work. if (mInstaller != null) { - int ret = mInstaller.remove(pkgName, useEncryptedFSDir); + int ret = mInstaller.remove(pkgName); if (ret >= 0) { // Old data gone! String msg = "System package " + pkg.packageName @@ -3160,7 +3160,7 @@ class PackageManagerService extends IPackageManager.Stub { recovered = true; // And now re-install the app. - ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid, + ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid); if (ret == -1) { // Ack should not happen! @@ -3201,7 +3201,7 @@ class PackageManagerService extends IPackageManager.Stub { Log.v(TAG, "Want this data dir: " + dataPath); //invoke installer to do the actual installation if (mInstaller != null) { - int ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid, + int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid); if(ret < 0) { // Error from installer @@ -4112,6 +4112,18 @@ class PackageManagerService extends IPackageManager.Stub { } @Override + protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter) { + PackageParser.Package p = filter.activity.owner; + if (p != null) { + PackageSetting ps = (PackageSetting)p.mExtras; + if (ps != null) { + return ps.stopped; + } + } + return false; + } + + @Override protected String packageForFilter(PackageParser.ActivityIntentInfo info) { return info.activity.owner.packageName; } @@ -4269,6 +4281,18 @@ class PackageManagerService extends IPackageManager.Stub { } @Override + protected boolean isFilterStopped(PackageParser.ServiceIntentInfo filter) { + PackageParser.Package p = filter.service.owner; + if (p != null) { + PackageSetting ps = (PackageSetting)p.mExtras; + if (ps != null) { + return ps.stopped; + } + } + return false; + } + + @Override protected String packageForFilter(PackageParser.ServiceIntentInfo info) { return info.service.owner.packageName; } @@ -4371,7 +4395,7 @@ class PackageManagerService extends IPackageManager.Stub { }; private static final void sendPackageBroadcast(String action, String pkg, - Bundle extras, IIntentReceiver finishedReceiver) { + Bundle extras, String targetPkg, IIntentReceiver finishedReceiver) { IActivityManager am = ActivityManagerNative.getDefault(); if (am != null) { try { @@ -4380,6 +4404,9 @@ class PackageManagerService extends IPackageManager.Stub { if (extras != null) { intent.putExtras(extras); } + if (targetPkg != null) { + intent.setPackage(targetPkg); + } intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); am.broadcastIntent(null, intent, null, finishedReceiver, 0, null, null, null, finishedReceiver != null, false); @@ -4515,13 +4542,13 @@ class PackageManagerService extends IPackageManager.Stub { extras.putInt(Intent.EXTRA_UID, removedUid); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false); sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, - extras, null); + extras, null, null); } if (addedPackage != null) { Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, addedUid); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage, - extras, null); + extras, null, null); } } @@ -4781,7 +4808,7 @@ class PackageManagerService extends IPackageManager.Stub { final File externalMediaDir = Environment .getExternalStorageAppMediaDirectory(mStats.packageName); mStats.externalMediaSize = mContainerService - .calculateDirectorySize(externalCacheDir.getPath()); + .calculateDirectorySize(externalMediaDir.getPath()); final File externalObbDir = Environment .getExternalStorageAppObbDirectory(mStats.packageName); @@ -6177,10 +6204,10 @@ class PackageManagerService extends IPackageManager.Stub { mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); - final boolean succeded = deletePackageX(packageName, true, true, flags); + final int returnCode = deletePackageX(packageName, true, true, flags); if (observer != null) { try { - observer.packageDeleted(succeded); + observer.packageDeleted(packageName, returnCode); } catch (RemoteException e) { Log.i(TAG, "Observer no longer exists."); } //end catch @@ -6203,17 +6230,17 @@ class PackageManagerService extends IPackageManager.Stub { * persisting settings for later use * sending a broadcast if necessary */ - private boolean deletePackageX(String packageName, boolean sendBroadCast, + private int deletePackageX(String packageName, boolean sendBroadCast, boolean deleteCodeAndResources, int flags) { - PackageRemovedInfo info = new PackageRemovedInfo(); - boolean res; + final PackageRemovedInfo info = new PackageRemovedInfo(); + final boolean res; IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface( ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)); try { if (dpm != null && dpm.packageHasActiveAdmins(packageName)) { Slog.w(TAG, "Not removing package " + packageName + ": has active device admin"); - return false; + return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER; } } catch (RemoteException e) { } @@ -6223,7 +6250,7 @@ class PackageManagerService extends IPackageManager.Stub { flags | REMOVE_CHATTY, info, true); } - if(res && sendBroadCast) { + if (res && sendBroadCast) { boolean systemUpdate = info.isRemovedPackageSystemUpdate; info.sendBroadcast(deleteCodeAndResources, systemUpdate); @@ -6234,8 +6261,12 @@ class PackageManagerService extends IPackageManager.Stub { extras.putInt(Intent.EXTRA_UID, info.removedUid >= 0 ? info.removedUid : info.uid); extras.putBoolean(Intent.EXTRA_REPLACING, true); - sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, null); - sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras, null); + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, null, null); + sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, + extras, null, null); + sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, + null, packageName, null); } } // Force a gc here. @@ -6247,7 +6278,8 @@ class PackageManagerService extends IPackageManager.Stub { info.args.doPostDeleteLI(deleteCodeAndResources); } } - return res; + + return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR; } static class PackageRemovedInfo { @@ -6266,10 +6298,11 @@ class PackageManagerService extends IPackageManager.Stub { extras.putBoolean(Intent.EXTRA_REPLACING, true); } if (removedPackage != null) { - sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras, null); + sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, + extras, null, null); } if (removedUid >= 0) { - sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null); + sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null); } } } @@ -6293,9 +6326,8 @@ class PackageManagerService extends IPackageManager.Stub { deletedPs = mSettings.mPackages.get(packageName); } if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { - boolean useEncryptedFSDir = false; if (mInstaller != null) { - int retCode = mInstaller.remove(packageName, useEncryptedFSDir); + int retCode = mInstaller.remove(packageName); if (retCode < 0) { Slog.w(TAG, "Couldn't remove app data or cache directory for package: " + packageName + ", retcode=" + retCode); @@ -6535,7 +6567,6 @@ class PackageManagerService extends IPackageManager.Stub { p = ps.pkg; } } - boolean useEncryptedFSDir = false; if (!dataOnly) { //need to check this only for fully installed applications @@ -6550,7 +6581,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (mInstaller != null) { - int retCode = mInstaller.clearUserData(packageName, useEncryptedFSDir); + int retCode = mInstaller.clearUserData(packageName); if (retCode < 0) { Slog.w(TAG, "Couldn't remove cache files for package: " + packageName); @@ -6601,9 +6632,8 @@ class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); return false; } - boolean useEncryptedFSDir = false; if (mInstaller != null) { - int retCode = mInstaller.deleteCacheFiles(packageName, useEncryptedFSDir); + int retCode = mInstaller.deleteCacheFiles(packageName); if (retCode < 0) { Slog.w(TAG, "Couldn't remove cache files for package: " + packageName); @@ -6663,10 +6693,8 @@ class PackageManagerService extends IPackageManager.Stub { } publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null; } - boolean useEncryptedFSDir = false; if (mInstaller != null) { - int res = mInstaller.getSizeInfo(packageName, p.mPath, - publicSrcDir, pStats, useEncryptedFSDir); + int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, pStats); if (res < 0) { return false; } else { @@ -6971,7 +6999,45 @@ class PackageManagerService extends IPackageManager.Stub { extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList); extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag); extras.putInt(Intent.EXTRA_UID, packageUid); - sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null); + sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null); + } + + public void setPackageStoppedState(String packageName, boolean stopped) { + PackageSetting pkgSetting; + final int uid = Binder.getCallingUid(); + final int permission = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); + final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); + synchronized (mPackages) { + pkgSetting = mSettings.mPackages.get(packageName); + if (pkgSetting == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (!allowedByPermission && (uid != pkgSetting.userId)) { + throw new SecurityException( + "Permission Denial: attempt to change stopped state from pid=" + + Binder.getCallingPid() + + ", uid=" + uid + ", package uid=" + pkgSetting.userId); + } + if (DEBUG_STOPPED && stopped) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Slog.i(TAG, "Stopping package " + packageName, e); + } + if (pkgSetting.stopped != stopped) { + pkgSetting.stopped = stopped; + pkgSetting.pkg.mSetStopped = stopped; + if (pkgSetting.notLaunched) { + if (pkgSetting.installerPackageName != null) { + sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, + pkgSetting.installerPackageName, null, + pkgSetting.name, null); + } + pkgSetting.notLaunched = false; + } + scheduleWriteStoppedPackagesLocked(); + } + } } public String getInstallerPackageName(String packageName) { @@ -7321,11 +7387,15 @@ class PackageManagerService extends IPackageManager.Stub { date.setTime(ps.firstInstallTime); pw.println(sdf.format(date)); pw.print(" lastUpdateTime="); date.setTime(ps.lastUpdateTime); pw.println(sdf.format(date)); + if (ps.installerPackageName != null) { + pw.print(" installerPackageName="); pw.println(ps.installerPackageName); + } pw.print(" signatures="); pw.println(ps.signatures); pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed); pw.print(" haveGids="); pw.println(ps.haveGids); pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags)); pw.print(" installStatus="); pw.print(ps.installStatus); + pw.print(" stopped="); pw.print(ps.stopped); pw.print(" enabled="); pw.println(ps.enabled); if (ps.disabledComponents.size() > 0) { pw.println(" disabledComponents:"); @@ -7862,6 +7932,13 @@ class PackageManagerService extends IPackageManager.Stub { boolean permissionsFixed; boolean haveGids; + // Whether this package is currently stopped, thus can not be + // started until explicitly launched by the user. + public boolean stopped; + + // Set to true if we have never launched this app. + public boolean notLaunched; + /* Explicitly disabled components */ HashSet<String> disabledComponents = new HashSet<String>(0); /* Explicitly enabled components */ @@ -7906,6 +7983,8 @@ class PackageManagerService extends IPackageManager.Stub { permissionsFixed = base.permissionsFixed; haveGids = base.haveGids; + stopped = base.stopped; + notLaunched = base.notLaunched; disabledComponents = (HashSet<String>) base.disabledComponents.clone(); @@ -7962,6 +8041,8 @@ class PackageManagerService extends IPackageManager.Stub { signatures = base.signatures; permissionsFixed = base.permissionsFixed; haveGids = base.haveGids; + stopped = base.stopped; + notLaunched = base.notLaunched; disabledComponents = base.disabledComponents; enabledComponents = base.enabledComponents; enabled = base.enabled; @@ -8060,6 +8141,8 @@ class PackageManagerService extends IPackageManager.Stub { private final File mSettingsFilename; private final File mBackupSettingsFilename; private final File mPackageListFilename; + private final File mStoppedPackagesFilename; + private final File mBackupStoppedPackagesFilename; private final HashMap<String, PackageSetting> mPackages = new HashMap<String, PackageSetting>(); // List of replaced system applications @@ -8159,6 +8242,8 @@ class PackageManagerService extends IPackageManager.Stub { mSettingsFilename = new File(systemDir, "packages.xml"); mBackupSettingsFilename = new File(systemDir, "packages-backup.xml"); mPackageListFilename = new File(systemDir, "packages.list"); + mStoppedPackagesFilename = new File(systemDir, "packages-stopped.xml"); + mBackupStoppedPackagesFilename = new File(systemDir, "packages-stopped-backup.xml"); } PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage, @@ -8414,6 +8499,16 @@ class PackageManagerService extends IPackageManager.Stub { nativeLibraryPathString, vc, pkgFlags); p.setTimeStamp(codePath.lastModified()); p.sharedUser = sharedUser; + // If this is not a system app, it starts out stopped. + if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { + if (DEBUG_STOPPED) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Slog.i(TAG, "Stopping package " + name, e); + } + p.stopped = true; + p.notLaunched = true; + } if (sharedUser != null) { p.userId = sharedUser.userId; } else if (MULTIPLE_APPLICATION_UIDS) { @@ -8460,6 +8555,7 @@ class PackageManagerService extends IPackageManager.Stub { private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg) { p.pkg = pkg; pkg.mSetEnabled = p.enabled; + pkg.mSetStopped = p.stopped; final String codePath = pkg.applicationInfo.sourceDir; final String resourcePath = pkg.applicationInfo.publicSourceDir; // Update code path if needed @@ -8676,6 +8772,180 @@ class PackageManagerService extends IPackageManager.Stub { } } + void writeStoppedLP() { + // Keep the old stopped packages around until we know the new ones have + // been successfully written. + if (mStoppedPackagesFilename.exists()) { + // Presence of backup settings file indicates that we failed + // to persist packages earlier. So preserve the older + // backup for future reference since the current packages + // might have been corrupted. + if (!mBackupStoppedPackagesFilename.exists()) { + if (!mStoppedPackagesFilename.renameTo(mBackupStoppedPackagesFilename)) { + Log.wtf(TAG, "Unable to backup package manager stopped packages, " + + "current changes will be lost at reboot"); + return; + } + } else { + mStoppedPackagesFilename.delete(); + Slog.w(TAG, "Preserving older stopped packages backup"); + } + } + + try { + FileOutputStream fstr = new FileOutputStream(mStoppedPackagesFilename); + BufferedOutputStream str = new BufferedOutputStream(fstr); + + //XmlSerializer serializer = XmlUtils.serializerInstance(); + XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(str, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, "stopped-packages"); + + for (PackageSetting pkg : mPackages.values()) { + if (pkg.stopped) { + serializer.startTag(null, "pkg"); + serializer.attribute(null, "name", pkg.name); + if (pkg.notLaunched) { + serializer.attribute(null, "nl", "1"); + } + serializer.endTag(null, "pkg"); + } + } + + serializer.endTag(null, "stopped-packages"); + + serializer.endDocument(); + + str.flush(); + FileUtils.sync(fstr); + str.close(); + + // New settings successfully written, old ones are no longer + // needed. + mBackupStoppedPackagesFilename.delete(); + FileUtils.setPermissions(mStoppedPackagesFilename.toString(), + FileUtils.S_IRUSR|FileUtils.S_IWUSR + |FileUtils.S_IRGRP|FileUtils.S_IWGRP + |FileUtils.S_IROTH, + -1, -1); + + // Done, all is good! + return; + + } catch(java.io.IOException e) { + Log.wtf(TAG, "Unable to write package manager stopped packages, " + + " current changes will be lost at reboot", e); + } + + // Clean up partially written files + if (mStoppedPackagesFilename.exists()) { + if (!mStoppedPackagesFilename.delete()) { + Log.i(TAG, "Failed to clean up mangled file: " + mStoppedPackagesFilename); + } + } + } + + // Note: assumed "stopped" field is already cleared in all packages. + void readStoppedLP() { + FileInputStream str = null; + if (mBackupStoppedPackagesFilename.exists()) { + try { + str = new FileInputStream(mBackupStoppedPackagesFilename); + mReadMessages.append("Reading from backup stopped packages file\n"); + reportSettingsProblem(Log.INFO, "Need to read from backup stopped packages file"); + if (mSettingsFilename.exists()) { + // If both the backup and normal file exist, we + // ignore the normal one since it might have been + // corrupted. + Slog.w(TAG, "Cleaning up stopped packages file " + + mStoppedPackagesFilename); + mStoppedPackagesFilename.delete(); + } + } catch (java.io.IOException e) { + // We'll try for the normal settings file. + } + } + + try { + if (str == null) { + if (!mStoppedPackagesFilename.exists()) { + mReadMessages.append("No stopped packages file found\n"); + reportSettingsProblem(Log.INFO, "No stopped packages file file; " + + "assuming all started"); + // At first boot, make sure no packages are stopped. + // We usually want to have third party apps initialize + // in the stopped state, but not at first boot. + for (PackageSetting pkg : mPackages.values()) { + pkg.stopped = false; + pkg.notLaunched = false; + } + return; + } + str = new FileInputStream(mStoppedPackagesFilename); + } + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(str, null); + + int type; + while ((type=parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + mReadMessages.append("No start tag found in stopped packages file\n"); + reportSettingsProblem(Log.WARN, + "No start tag found in package manager stopped packages"); + return; + } + + int outerDepth = parser.getDepth(); + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("pkg")) { + String name = parser.getAttributeValue(null, "name"); + PackageSetting ps = mPackages.get(name); + if (ps != null) { + ps.stopped = true; + if ("1".equals(parser.getAttributeValue(null, "nl"))) { + ps.notLaunched = true; + } + } else { + Slog.w(TAG, "No package known for stopped package: " + name); + } + XmlUtils.skipCurrentTag(parser); + } else { + Slog.w(TAG, "Unknown element under <stopped-packages>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + str.close(); + + } catch(XmlPullParserException e) { + mReadMessages.append("Error reading: " + e.toString()); + reportSettingsProblem(Log.ERROR, "Error reading stopped packages: " + e); + Log.wtf(TAG, "Error reading package manager stopped packages", e); + + } catch(java.io.IOException e) { + mReadMessages.append("Error reading: " + e.toString()); + reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); + Log.wtf(TAG, "Error reading package manager stopped packages", e); + + } + } + void writeLP() { //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024); @@ -8688,7 +8958,8 @@ class PackageManagerService extends IPackageManager.Stub { // might have been corrupted. if (!mBackupSettingsFilename.exists()) { if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) { - Slog.w(TAG, "Unable to backup package manager settings, current changes will be lost at reboot"); + Log.wtf(TAG, "Unable to backup package manager settings, " + + " current changes will be lost at reboot"); return; } } else { @@ -8850,17 +9121,21 @@ class PackageManagerService extends IPackageManager.Stub { |FileUtils.S_IROTH, -1, -1); + writeStoppedLP(); + return; } catch(XmlPullParserException e) { - Slog.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); + Log.wtf(TAG, "Unable to write package manager settings, " + + "current changes will be lost at reboot", e); } catch(java.io.IOException e) { - Slog.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); + Log.wtf(TAG, "Unable to write package manager settings, " + + "current changes will be lost at reboot", e); } // Clean up partially written files if (mSettingsFilename.exists()) { if (!mSettingsFilename.delete()) { - Log.i(TAG, "Failed to clean up mangled file: " + mSettingsFilename); + Log.wtf(TAG, "Failed to clean up mangled file: " + mSettingsFilename); } } //Debug.stopMethodTracing(); @@ -9084,6 +9359,7 @@ class PackageManagerService extends IPackageManager.Stub { if (type != XmlPullParser.START_TAG) { mReadMessages.append("No start tag found in settings file\n"); reportSettingsProblem(Log.WARN, "No start tag found in package manager settings"); + Log.wtf(TAG, "No start tag found in package manager settings"); return false; } @@ -9147,12 +9423,12 @@ class PackageManagerService extends IPackageManager.Stub { } catch(XmlPullParserException e) { mReadMessages.append("Error reading: " + e.toString()); reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); - Slog.e(TAG, "Error reading package manager settings", e); + Log.wtf(TAG, "Error reading package manager settings", e); } catch(java.io.IOException e) { mReadMessages.append("Error reading: " + e.toString()); reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); - Slog.e(TAG, "Error reading package manager settings", e); + Log.wtf(TAG, "Error reading package manager settings", e); } @@ -9186,6 +9462,8 @@ class PackageManagerService extends IPackageManager.Stub { } mPendingPackages.clear(); + readStoppedLP(); + mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, " + mSharedUsers.size() + " shared uids\n"); @@ -9991,7 +10269,7 @@ class PackageManagerService extends IPackageManager.Stub { } String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; - sendPackageBroadcast(action, null, extras, finishedReceiver); + sendPackageBroadcast(action, null, extras, null, finishedReceiver); } } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index caf6376..d80a2cd 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -40,7 +40,6 @@ import android.hardware.SensorManager; import android.os.BatteryManager; import android.os.BatteryStats; import android.os.Binder; -import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -50,7 +49,6 @@ import android.os.Power; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.WorkSource; import android.provider.Settings.SettingNotFoundException; @@ -69,14 +67,13 @@ import static android.provider.Settings.System.WINDOW_ANIMATION_SCALE; import static android.provider.Settings.System.TRANSITION_ANIMATION_SCALE; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Observable; import java.util.Observer; -class PowerManagerService extends IPowerManager.Stub +public class PowerManagerService extends IPowerManager.Stub implements LocalPowerManager, Watchdog.Monitor { private static final String TAG = "PowerManagerService"; @@ -2689,7 +2686,7 @@ class PowerManagerService extends IPowerManager.Stub } } - void setPolicy(WindowManagerPolicy p) { + public void setPolicy(WindowManagerPolicy p) { synchronized (mLocks) { mPolicy = p; mLocks.notifyAll(); diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java index 5ada77b..8df8177 100644 --- a/services/java/com/android/server/StatusBarManagerService.java +++ b/services/java/com/android/server/StatusBarManagerService.java @@ -39,6 +39,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; import com.android.internal.statusbar.StatusBarNotification; +import com.android.server.wm.WindowManagerService; import java.io.FileDescriptor; import java.io.PrintWriter; diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java index a1f43b4..80b0174 100644 --- a/services/java/com/android/server/SystemBackupAgent.java +++ b/services/java/com/android/server/SystemBackupAgent.java @@ -16,6 +16,7 @@ package com.android.server; + import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupAgentHelper; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 33e9908..d160963 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -17,6 +17,8 @@ package com.android.server; import com.android.server.am.ActivityManagerService; +import com.android.server.usb.UsbService; +import com.android.server.wm.WindowManagerService; import com.android.internal.app.ShutdownThread; import com.android.internal.os.BinderInternal; import com.android.internal.os.SamplingProfilerIntegration; @@ -405,6 +407,7 @@ class ServerThread extends Thread { Slog.i(TAG, "USB Observer"); // Listen for USB changes usb = new UsbService(context); + ServiceManager.addService(Context.USB_SERVICE, usb); } catch (Throwable e) { Slog.e(TAG, "Failure starting UsbService", e); } diff --git a/services/java/com/android/server/UsbService.java b/services/java/com/android/server/UsbService.java deleted file mode 100644 index 8ef03d4..0000000 --- a/services/java/com/android/server/UsbService.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2010 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; - -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.hardware.UsbManager; -import android.net.Uri; -import android.os.Handler; -import android.os.Message; -import android.os.UEventObserver; -import android.provider.Settings; -import android.util.Log; -import android.util.Slog; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.util.ArrayList; - -/** - * <p>UsbService monitors for changes to USB state. - */ -class UsbService { - private static final String TAG = UsbService.class.getSimpleName(); - private static final boolean LOG = false; - - private static final String USB_CONNECTED_MATCH = - "DEVPATH=/devices/virtual/switch/usb_connected"; - private static final String USB_CONFIGURATION_MATCH = - "DEVPATH=/devices/virtual/switch/usb_configuration"; - private static final String USB_FUNCTIONS_MATCH = - "DEVPATH=/devices/virtual/usb_composite/"; - private static final String USB_CONNECTED_PATH = - "/sys/class/switch/usb_connected/state"; - private static final String USB_CONFIGURATION_PATH = - "/sys/class/switch/usb_configuration/state"; - private static final String USB_COMPOSITE_CLASS_PATH = - "/sys/class/usb_composite"; - - private static final int MSG_UPDATE = 0; - - // Delay for debouncing USB disconnects. - // We often get rapid connect/disconnect events when enabling USB functions, - // which need debouncing. - private static final int UPDATE_DELAY = 1000; - - // current connected and configuration state - private int mConnected; - private int mConfiguration; - - // last broadcasted connected and configuration state - private int mLastConnected = -1; - private int mLastConfiguration = -1; - - // lists of enabled and disabled USB functions - private final ArrayList<String> mEnabledFunctions = new ArrayList<String>(); - private final ArrayList<String> mDisabledFunctions = new ArrayList<String>(); - - private boolean mSystemReady; - - private final Context mContext; - - private final UEventObserver mUEventObserver = new UEventObserver() { - @Override - public void onUEvent(UEventObserver.UEvent event) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Slog.v(TAG, "USB UEVENT: " + event.toString()); - } - - synchronized (this) { - String name = event.get("SWITCH_NAME"); - String state = event.get("SWITCH_STATE"); - if (name != null && state != null) { - try { - int intState = Integer.parseInt(state); - if ("usb_connected".equals(name)) { - mConnected = intState; - // trigger an Intent broadcast - if (mSystemReady) { - // debounce disconnects - update(mConnected == 0); - } - } else if ("usb_configuration".equals(name)) { - mConfiguration = intState; - // trigger an Intent broadcast - if (mSystemReady) { - update(mConnected == 0); - } - } - } catch (NumberFormatException e) { - Slog.e(TAG, "Could not parse switch state from event " + event); - } - } else { - String function = event.get("FUNCTION"); - String enabledStr = event.get("ENABLED"); - if (function != null && enabledStr != null) { - // Note: we do not broadcast a change when a function is enabled or disabled. - // We just record the state change for the next broadcast. - boolean enabled = "1".equals(enabledStr); - if (enabled) { - if (!mEnabledFunctions.contains(function)) { - mEnabledFunctions.add(function); - } - mDisabledFunctions.remove(function); - } else { - if (!mDisabledFunctions.contains(function)) { - mDisabledFunctions.add(function); - } - mEnabledFunctions.remove(function); - } - } - } - } - } - }; - - public UsbService(Context context) { - mContext = context; - init(); // set initial status - - if (mConfiguration >= 0) { - mUEventObserver.startObserving(USB_CONNECTED_MATCH); - mUEventObserver.startObserving(USB_CONFIGURATION_MATCH); - mUEventObserver.startObserving(USB_FUNCTIONS_MATCH); - } - } - - private final void init() { - char[] buffer = new char[1024]; - - mConfiguration = -1; - try { - FileReader file = new FileReader(USB_CONNECTED_PATH); - int len = file.read(buffer, 0, 1024); - file.close(); - mConnected = Integer.valueOf((new String(buffer, 0, len)).trim()); - - file = new FileReader(USB_CONFIGURATION_PATH); - len = file.read(buffer, 0, 1024); - file.close(); - mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim()); - - } catch (FileNotFoundException e) { - Slog.i(TAG, "This kernel does not have USB configuration switch support"); - } catch (Exception e) { - Slog.e(TAG, "" , e); - } - if (mConfiguration < 0) - return; - - try { - File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles(); - for (int i = 0; i < files.length; i++) { - File file = new File(files[i], "enable"); - FileReader reader = new FileReader(file); - int len = reader.read(buffer, 0, 1024); - reader.close(); - int value = Integer.valueOf((new String(buffer, 0, len)).trim()); - String functionName = files[i].getName(); - if (value == 1) { - mEnabledFunctions.add(functionName); - } else { - mDisabledFunctions.add(functionName); - } - } - } catch (FileNotFoundException e) { - Slog.w(TAG, "This kernel does not have USB composite class support"); - } catch (Exception e) { - Slog.e(TAG, "" , e); - } - } - - private void initHostSupport() { - // temporarily disabled - } - - void systemReady() { - synchronized (this) { - if (mContext.getResources().getBoolean( - com.android.internal.R.bool.config_hasUsbHostSupport)) { - // start monitoring for connected USB devices - initHostSupport(); - } - - update(false); - mSystemReady = true; - } - } - - private final void update(boolean delayed) { - mHandler.removeMessages(MSG_UPDATE); - mHandler.sendEmptyMessageDelayed(MSG_UPDATE, delayed ? UPDATE_DELAY : 0); - } - - private final Handler mHandler = new Handler() { - private void addEnabledFunctions(Intent intent) { - // include state of all USB functions in our extras - for (int i = 0; i < mEnabledFunctions.size(); i++) { - intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED); - } - for (int i = 0; i < mDisabledFunctions.size(); i++) { - intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED); - } - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_UPDATE: - synchronized (this) { - if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) { - - final ContentResolver cr = mContext.getContentResolver(); - if (Settings.Secure.getInt(cr, - Settings.Secure.DEVICE_PROVISIONED, 0) == 0) { - Slog.i(TAG, "Device not provisioned, skipping USB broadcast"); - return; - } - - mLastConnected = mConnected; - mLastConfiguration = mConfiguration; - - // send a sticky broadcast containing current USB state - Intent intent = new Intent(UsbManager.ACTION_USB_STATE); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0); - intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration); - addEnabledFunctions(intent); - mContext.sendStickyBroadcast(intent); - } - } - break; - } - } - }; -} diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index 997e750..b1a6a9a 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -70,8 +70,6 @@ import com.android.internal.content.PackageMonitor; import com.android.internal.service.wallpaper.ImageWallpaper; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; -import com.android.server.DevicePolicyManagerService.ActiveAdmin; -import com.android.server.DevicePolicyManagerService.MyPackageMonitor; class WallpaperManagerService extends IWallpaperManager.Stub { static final String TAG = "WallpaperService"; diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 22583f7..adc49ae 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -39,24 +39,26 @@ import android.net.wifi.WifiConfiguration.KeyMgmt; import android.net.wifi.WpsConfiguration; import android.net.wifi.WpsResult; import android.net.ConnectivityManager; -import android.net.InterfaceConfiguration; import android.net.DhcpInfo; import android.net.NetworkInfo; import android.net.NetworkInfo.State; +import android.net.NetworkInfo.DetailedState; +import android.net.TrafficStats; import android.os.Binder; import android.os.Handler; +import android.os.Messenger; import android.os.HandlerThread; import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.os.WorkSource; import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; -import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -116,8 +118,19 @@ public class WifiService extends IWifiManager.Stub { private final IBatteryStats mBatteryStats; - ConnectivityManager mCm; - private String[] mWifiRegexs; + private boolean mEnableTrafficStatsPoll = false; + private int mTrafficStatsPollToken = 0; + private long mTxPkts; + private long mRxPkts; + /* Tracks last reported data activity */ + private int mDataActivity; + private String mInterfaceName; + + /** + * Interval in milliseconds between polling for traffic + * statistics + */ + private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000; /** * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a @@ -188,18 +201,20 @@ public class WifiService extends IWifiManager.Stub { /** * Asynchronous channel to WifiStateMachine */ - private AsyncChannel mChannel; + private AsyncChannel mWifiStateMachineChannel; /** - * TODO: Possibly change WifiService into an AsyncService. + * Clients receiving asynchronous messages */ - private class WifiServiceHandler extends Handler { - private AsyncChannel mWshChannel; + private List<AsyncChannel> mClients = new ArrayList<AsyncChannel>(); - WifiServiceHandler(android.os.Looper looper, Context context) { + /** + * Handles client connections + */ + private class AsyncServiceHandler extends Handler { + + AsyncServiceHandler(android.os.Looper looper) { super(looper); - mWshChannel = new AsyncChannel(); - mWshChannel.connect(context, this, mWifiStateMachine.getHandler()); } @Override @@ -207,14 +222,57 @@ public class WifiService extends IWifiManager.Stub { switch (msg.what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - mChannel = mWshChannel; + Slog.d(TAG, "New client listening to asynchronous messages"); + mClients.add((AsyncChannel) msg.obj); + } else { + Slog.e(TAG, "Client connection failure, error=" + msg.arg1); + } + break; + } + case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { + AsyncChannel ac = new AsyncChannel(); + ac.connect(mContext, this, msg.replyTo); + break; + } + case WifiManager.CMD_ENABLE_TRAFFIC_STATS_POLL: { + mEnableTrafficStatsPoll = (msg.arg1 == 1); + mTrafficStatsPollToken++; + if (mEnableTrafficStatsPoll) { + notifyOnDataActivity(); + sendMessageDelayed(Message.obtain(this, WifiManager.CMD_TRAFFIC_STATS_POLL, + mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS); + } + break; + } + case WifiManager.CMD_TRAFFIC_STATS_POLL: { + if (msg.arg1 == mTrafficStatsPollToken) { + notifyOnDataActivity(); + sendMessageDelayed(Message.obtain(this, WifiManager.CMD_TRAFFIC_STATS_POLL, + mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS); + } + break; + } + case WifiManager.CMD_CONNECT_NETWORK: { + if (msg.obj != null) { + mWifiStateMachine.connectNetwork((WifiConfiguration)msg.obj); } else { - Slog.d(TAG, "WifiServicehandler.handleMessage could not connect error=" + - msg.arg1); - mChannel = null; + mWifiStateMachine.connectNetwork(msg.arg1); } break; } + case WifiManager.CMD_SAVE_NETWORK: { + mWifiStateMachine.saveNetwork((WifiConfiguration)msg.obj); + break; + } + case WifiManager.CMD_FORGET_NETWORK: { + mWifiStateMachine.forgetNetwork(msg.arg1); + break; + } + case WifiManager.CMD_START_WPS: { + //replyTo has the original source + mWifiStateMachine.startWps(msg.replyTo, (WpsConfiguration)msg.obj); + break; + } default: { Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg); break; @@ -222,7 +280,40 @@ public class WifiService extends IWifiManager.Stub { } } } - WifiServiceHandler mHandler; + private AsyncServiceHandler mAsyncServiceHandler; + + /** + * Handles interaction with WifiStateMachine + */ + private class WifiStateMachineHandler extends Handler { + private AsyncChannel mWsmChannel; + + WifiStateMachineHandler(android.os.Looper looper) { + super(looper); + mWsmChannel = new AsyncChannel(); + mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler()); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + mWifiStateMachineChannel = mWsmChannel; + } else { + Slog.e(TAG, "WifiStateMachine connection failure, error=" + msg.arg1); + mWifiStateMachineChannel = null; + } + break; + } + default: { + Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg); + break; + } + } + } + } + WifiStateMachineHandler mWifiStateMachineHandler; /** * Temporary for computing UIDS that are responsible for starting WIFI. @@ -232,7 +323,10 @@ public class WifiService extends IWifiManager.Stub { WifiService(Context context) { mContext = context; - mWifiStateMachine = new WifiStateMachine(mContext); + + mInterfaceName = SystemProperties.get("wifi.interface", "wlan0"); + + mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName); mWifiStateMachine.enableRssiPolling(true); mBatteryStats = BatteryStatsService.getService(); @@ -240,10 +334,6 @@ public class WifiService extends IWifiManager.Stub { Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0); - HandlerThread wifiThread = new HandlerThread("WifiService"); - wifiThread.start(); - mHandler = new WifiServiceHandler(wifiThread.getLooper(), context); - mContext.registerReceiver( new BroadcastReceiver() { @Override @@ -259,20 +349,6 @@ public class WifiService extends IWifiManager.Stub { }, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); - mContext.registerReceiver( - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - - ArrayList<String> available = intent.getStringArrayListExtra( - ConnectivityManager.EXTRA_AVAILABLE_TETHER); - ArrayList<String> active = intent.getStringArrayListExtra( - ConnectivityManager.EXTRA_ACTIVE_TETHER); - updateTetherState(available, active); - - } - },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); - IntentFilter filter = new IntentFilter(); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); @@ -293,6 +369,7 @@ public class WifiService extends IWifiManager.Stub { switch(mNetworkInfo.getDetailedState()) { case CONNECTED: case DISCONNECTED: + evaluateTrafficStatsPolling(); resetNotification(); break; } @@ -303,6 +380,11 @@ public class WifiService extends IWifiManager.Stub { } }, filter); + HandlerThread wifiThread = new HandlerThread("WifiService"); + wifiThread.start(); + mAsyncServiceHandler = new AsyncServiceHandler(wifiThread.getLooper()); + mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper()); + // Setting is in seconds NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; @@ -328,52 +410,6 @@ public class WifiService extends IWifiManager.Stub { setWifiEnabled(wifiEnabled); } - private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) { - - boolean wifiTethered = false; - boolean wifiAvailable = false; - - IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); - - if (mCm == null) { - mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - } - - mWifiRegexs = mCm.getTetherableWifiRegexs(); - - for (String intf : available) { - for (String regex : mWifiRegexs) { - if (intf.matches(regex)) { - - InterfaceConfiguration ifcg = null; - try { - ifcg = service.getInterfaceConfig(intf); - if (ifcg != null) { - /* IP/netmask: 192.168.43.1/255.255.255.0 */ - ifcg.addr = InetAddress.getByName("192.168.43.1"); - ifcg.mask = InetAddress.getByName("255.255.255.0"); - ifcg.interfaceFlags = "[up]"; - - service.setInterfaceConfig(intf, ifcg); - } - } catch (Exception e) { - Slog.e(TAG, "Error configuring interface " + intf + ", :" + e); - setWifiApEnabled(null, false); - return; - } - - if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { - Slog.e(TAG, "Error tethering on " + intf); - setWifiApEnabled(null, false); - return; - } - break; - } - } - } - } - private boolean testAndClearWifiSavedState() { final ContentResolver cr = mContext.getContentResolver(); int wifiSavedState = 0; @@ -408,10 +444,10 @@ public class WifiService extends IWifiManager.Stub { */ public boolean pingSupplicant() { enforceAccessPermission(); - if (mChannel != null) { - return mWifiStateMachine.syncPingSupplicant(mChannel); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncPingSupplicant(mWifiStateMachineChannel); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return false; } } @@ -572,15 +608,12 @@ public class WifiService extends IWifiManager.Stub { public synchronized void setWifiApConfiguration(WifiConfiguration wifiConfig) { enforceChangePermission(); final ContentResolver cr = mContext.getContentResolver(); - boolean isWpa; if (wifiConfig == null) return; + int authType = wifiConfig.getAuthType(); Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_SSID, wifiConfig.SSID); - isWpa = wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK); - Settings.Secure.putInt(cr, - Settings.Secure.WIFI_AP_SECURITY, - isWpa ? KeyMgmt.WPA_PSK : KeyMgmt.NONE); - if (isWpa) + Settings.Secure.putInt(cr, Settings.Secure.WIFI_AP_SECURITY, authType); + if (authType != KeyMgmt.NONE) Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_PASSWD, wifiConfig.preSharedKey); } @@ -624,10 +657,10 @@ public class WifiService extends IWifiManager.Stub { */ public int addOrUpdateNetwork(WifiConfiguration config) { enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.syncAddOrUpdateNetwork(mChannel, config); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return -1; } } @@ -640,10 +673,10 @@ public class WifiService extends IWifiManager.Stub { */ public boolean removeNetwork(int netId) { enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.syncRemoveNetwork(mChannel, netId); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncRemoveNetwork(mWifiStateMachineChannel, netId); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return false; } } @@ -657,10 +690,11 @@ public class WifiService extends IWifiManager.Stub { */ public boolean enableNetwork(int netId, boolean disableOthers) { enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.syncEnableNetwork(mChannel, netId, disableOthers); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, netId, + disableOthers); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return false; } } @@ -673,10 +707,10 @@ public class WifiService extends IWifiManager.Stub { */ public boolean disableNetwork(int netId) { enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.syncDisableNetwork(mChannel, netId); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncDisableNetwork(mWifiStateMachineChannel, netId); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return false; } } @@ -713,10 +747,10 @@ public class WifiService extends IWifiManager.Stub { public boolean saveConfiguration() { boolean result = true; enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.syncSaveConfig(mChannel); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return false; } } @@ -828,34 +862,20 @@ public class WifiService extends IWifiManager.Stub { mWifiStateMachine.clearBlacklist(); } - public void connectNetworkWithId(int networkId) { - enforceChangePermission(); - mWifiStateMachine.connectNetwork(networkId); - } - public void connectNetworkWithConfig(WifiConfiguration config) { - enforceChangePermission(); - mWifiStateMachine.connectNetwork(config); - } - public void saveNetwork(WifiConfiguration config) { - enforceChangePermission(); - mWifiStateMachine.saveNetwork(config); - } - - public void forgetNetwork(int netId) { - enforceChangePermission(); - mWifiStateMachine.forgetNetwork(netId); - } - - public WpsResult startWps(WpsConfiguration config) { + /** + * Get a reference to handler. This is used by a client to establish + * an AsyncChannel communication with WifiService + */ + public Messenger getMessenger() { + /* Enforce the highest permissions + TODO: when we consider exposing the asynchronous API, think about + how to provide both access and change permissions seperately + */ + enforceAccessPermission(); enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.startWps(mChannel, config); - } else { - Slog.e(TAG, "mChannel is not initialized"); - return new WpsResult(WpsResult.Status.FAILURE); - } + return new Messenger(mAsyncServiceHandler); } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -879,6 +899,7 @@ public class WifiService extends IWifiManager.Stub { // Once the screen is on, we are not keeping WIFI running // because of any locks so clear that tracking immediately. reportStartWorkSource(); + evaluateTrafficStatsPolling(); mWifiStateMachine.enableRssiPolling(true); if (mBackgroundScanSupported) { mWifiStateMachine.enableBackgroundScan(false); @@ -890,6 +911,7 @@ public class WifiService extends IWifiManager.Stub { Slog.d(TAG, "ACTION_SCREEN_OFF"); } mScreenOff = true; + evaluateTrafficStatsPolling(); mWifiStateMachine.enableRssiPolling(false); if (mBackgroundScanSupported) { mWifiStateMachine.enableBackgroundScan(true); @@ -1496,6 +1518,50 @@ public class WifiService extends IWifiManager.Stub { } } + /** + * Evaluate if traffic stats polling is needed based on + * connection and screen on status + */ + private void evaluateTrafficStatsPolling() { + Message msg; + if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED && !mScreenOff) { + msg = Message.obtain(mAsyncServiceHandler, + WifiManager.CMD_ENABLE_TRAFFIC_STATS_POLL, 1, 0); + } else { + msg = Message.obtain(mAsyncServiceHandler, + WifiManager.CMD_ENABLE_TRAFFIC_STATS_POLL, 0, 0); + } + msg.sendToTarget(); + } + + private void notifyOnDataActivity() { + long sent, received; + long preTxPkts = mTxPkts, preRxPkts = mRxPkts; + int dataActivity = WifiManager.DATA_ACTIVITY_NONE; + + mTxPkts = TrafficStats.getTxPackets(mInterfaceName); + mRxPkts = TrafficStats.getRxPackets(mInterfaceName); + + if (preTxPkts > 0 || preRxPkts > 0) { + sent = mTxPkts - preTxPkts; + received = mRxPkts - preRxPkts; + if (sent > 0) { + dataActivity |= WifiManager.DATA_ACTIVITY_OUT; + } + if (received > 0) { + dataActivity |= WifiManager.DATA_ACTIVITY_IN; + } + + if (dataActivity != mDataActivity && !mScreenOff) { + mDataActivity = dataActivity; + for (AsyncChannel client : mClients) { + client.sendMessage(WifiManager.DATA_ACTIVITY_NOTIFICATION, mDataActivity); + } + } + } + } + + private void checkAndSetNotification() { // If we shouldn't place a notification on available networks, then // don't bother doing any of the following diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java index 46d6bef..94531bb 100644 --- a/services/java/com/android/server/WifiWatchdogService.java +++ b/services/java/com/android/server/WifiWatchdogService.java @@ -22,8 +22,9 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; +import android.net.ConnectivityManager; +import android.net.LinkProperties; import android.net.NetworkInfo; -import android.net.DhcpInfo; import android.net.wifi.ScanResult; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; @@ -42,6 +43,7 @@ import java.net.InetAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; +import java.util.Collection; import java.util.List; import java.util.Random; @@ -77,7 +79,8 @@ public class WifiWatchdogService { private Context mContext; private ContentResolver mContentResolver; private WifiManager mWifiManager; - + private ConnectivityManager mConnectivityManager; + /** * The main watchdog thread. */ @@ -310,19 +313,26 @@ public class WifiWatchdogService { } /** - * Gets the DNS of the current AP. + * Gets the first DNS of the current AP. * - * @return The DNS of the current AP. + * @return The first DNS of the current AP. */ - private int getDns() { - DhcpInfo addressInfo = mWifiManager.getDhcpInfo(); - if (addressInfo != null) { - return addressInfo.dns1; - } else { - return -1; + private InetAddress getDns() { + if (mConnectivityManager == null) { + mConnectivityManager = (ConnectivityManager)mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); } + + LinkProperties linkProperties = mConnectivityManager.getLinkProperties( + ConnectivityManager.TYPE_WIFI); + if (linkProperties == null) return null; + + Collection<InetAddress> dnses = linkProperties.getDnses(); + if (dnses == null || dnses.size() == 0) return null; + + return dnses.iterator().next(); } - + /** * Checks whether the DNS can be reached using multiple attempts according * to the current setting values. @@ -330,29 +340,28 @@ public class WifiWatchdogService { * @return Whether the DNS is reachable */ private boolean checkDnsConnectivity() { - int dns = getDns(); - if (dns == -1) { + InetAddress dns = getDns(); + if (dns == null) { if (V) { myLogV("checkDnsConnectivity: Invalid DNS, returning false"); } return false; } - + if (V) { - myLogV("checkDnsConnectivity: Checking 0x" + - Integer.toHexString(Integer.reverseBytes(dns)) + " for connectivity"); + myLogV("checkDnsConnectivity: Checking " + dns.getHostAddress() + " for connectivity"); } int numInitialIgnoredPings = getInitialIgnoredPingCount(); int numPings = getPingCount(); int pingDelay = getPingDelayMs(); int acceptableLoss = getAcceptablePacketLossPercentage(); - + /** See {@link Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} */ int ignoredPingCounter = 0; int pingCounter = 0; int successCounter = 0; - + // No connectivity check needed if (numPings == 0) { return true; @@ -371,20 +380,20 @@ public class WifiWatchdogService { pingCounter++; successCounter++; } - + if (V) { Slog.v(TAG, (dnsAlive ? " +" : " Ignored: -")); } if (shouldCancel()) return false; - + try { Thread.sleep(pingDelay); } catch (InterruptedException e) { Slog.w(TAG, "Interrupted while pausing between pings", e); } } - + // Do the pings that we use to measure packet loss for (; pingCounter < numPings; pingCounter++) { if (shouldCancel()) return false; @@ -401,40 +410,41 @@ public class WifiWatchdogService { } if (shouldCancel()) return false; - + try { Thread.sleep(pingDelay); } catch (InterruptedException e) { Slog.w(TAG, "Interrupted while pausing between pings", e); } } - + int packetLossPercentage = 100 * (numPings - successCounter) / numPings; if (D) { Slog.d(TAG, packetLossPercentage + "% packet loss (acceptable is " + acceptableLoss + "%)"); } - + return !shouldCancel() && (packetLossPercentage <= acceptableLoss); } private boolean backgroundCheckDnsConnectivity() { - int dns = getDns(); - if (false && V) { - myLogV("backgroundCheckDnsConnectivity: Background checking " + dns + - " for connectivity"); - } - - if (dns == -1) { + InetAddress dns = getDns(); + + if (dns == null) { if (V) { myLogV("backgroundCheckDnsConnectivity: DNS is empty, returning false"); } return false; } - + + if (false && V) { + myLogV("backgroundCheckDnsConnectivity: Background checking " + + dns.getHostAddress() + " for connectivity"); + } + return DnsPinger.isDnsReachable(dns, getBackgroundCheckTimeoutMs()); } - + /** * Signals the current action to cancel. */ @@ -1207,43 +1217,37 @@ public class WifiWatchdogService { /** Used to generate IDs */ private static Random sRandom = new Random(); - - static boolean isDnsReachable(int dns, int timeout) { + + static boolean isDnsReachable(InetAddress dnsAddress, int timeout) { DatagramSocket socket = null; try { socket = new DatagramSocket(); - + // Set some socket properties socket.setSoTimeout(timeout); - + byte[] buf = new byte[DNS_QUERY_BASE_SIZE]; fillQuery(buf); - + // Send the DNS query - byte parts[] = new byte[4]; - parts[0] = (byte)(dns & 0xff); - parts[1] = (byte)((dns >> 8) & 0xff); - parts[2] = (byte)((dns >> 16) & 0xff); - parts[3] = (byte)((dns >> 24) & 0xff); - InetAddress dnsAddress = InetAddress.getByAddress(parts); DatagramPacket packet = new DatagramPacket(buf, buf.length, dnsAddress, DNS_PORT); socket.send(packet); - + // Wait for reply (blocks for the above timeout) DatagramPacket replyPacket = new DatagramPacket(buf, buf.length); socket.receive(replyPacket); // If a timeout occurred, an exception would have been thrown. We got a reply! return true; - + } catch (SocketException e) { if (V) { Slog.v(TAG, "DnsPinger.isReachable received SocketException", e); } return false; - + } catch (UnknownHostException e) { if (V) { Slog.v(TAG, "DnsPinger.isReachable is unable to resolve the DNS host", e); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index c24fe8e..e6dfb7f 100755..100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -24,8 +24,8 @@ import com.android.server.ProcessMap; import com.android.server.ProcessStats; import com.android.server.SystemServer; import com.android.server.Watchdog; -import com.android.server.WindowManagerService; import com.android.server.am.ActivityStack.ActivityState; +import com.android.server.wm.WindowManagerService; import dalvik.system.Zygote; @@ -180,6 +180,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES; private static final String SYSTEM_SECURE = "ro.secure"; + private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; // This is the maximum number of application processes we would like // to have running. Due to the asynchronous nature of things, we can @@ -3145,6 +3146,10 @@ public final class ActivityManagerService extends ActivityManagerNative return; } forceStopPackageLocked(packageName, pkgUid); + try { + pm.setPackageStoppedState(packageName, true); + } catch (RemoteException e) { + } } } finally { Binder.restoreCallingIdentity(callingId); @@ -4969,11 +4974,6 @@ public final class ActivityManagerService extends ActivityManagerNative enforceCallingPermission(android.Manifest.permission.GET_TASKS, "getRecentTasks()"); - final boolean canReadFb = (flags&ActivityManager.TASKS_GET_THUMBNAILS) != 0 - && checkCallingPermission( - android.Manifest.permission.READ_FRAME_BUFFER) - == PackageManager.PERMISSION_GRANTED; - IPackageManager pm = AppGlobals.getPackageManager(); ActivityRecord resumed = mMainStack.mResumedActivity; @@ -4991,17 +4991,10 @@ public final class ActivityManagerService extends ActivityManagerNative ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); rti.id = tr.numActivities > 0 ? tr.taskId : -1; + rti.persistentId = tr.taskId; rti.baseIntent = new Intent( tr.intent != null ? tr.intent : tr.affinityIntent); rti.origActivity = tr.origActivity; - - if (canReadFb) { - if (resumed != null && resumed.task == tr) { - rti.thumbnail = resumed.stack.screenshotActivities(resumed); - } else { - rti.thumbnail = tr.lastThumbnail; - } - } rti.description = tr.lastDescription; if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) { @@ -5030,6 +5023,26 @@ public final class ActivityManagerService extends ActivityManagerNative } } + public Bitmap getTaskThumbnail(int id) { + synchronized (this) { + enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, + "getTaskThumbnail()"); + ActivityRecord resumed = mMainStack.mResumedActivity; + final int N = mRecentTasks.size(); + for (int i=0; i<N; i++) { + TaskRecord tr = mRecentTasks.get(i); + if (tr.taskId == id) { + if (resumed != null && resumed.task == tr) { + return resumed.stack.screenshotActivities(resumed); + } else { + return tr.lastThumbnail; + } + } + } + } + return null; + } + private final int findAffinityTaskTopLocked(int startIndex, String affinity) { int j; TaskRecord startTask = ((ActivityRecord)mMainStack.mHistory.get(startIndex)).task; @@ -5085,6 +5098,9 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<N; i++) { TaskRecord tr = mRecentTasks.get(i); if (tr.taskId == task) { + if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { + mMainStack.mUserLeaving = true; + } if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) { // Caller wants the home activity moved with it. To accomplish this, // we'll just move the home task to the top first. @@ -5097,6 +5113,9 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i); if (hr.task.taskId == task) { + if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { + mMainStack.mUserLeaving = true; + } if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) { // Caller wants the home activity moved with it. To accomplish this, // we'll just move the home task to the top first. @@ -5530,20 +5549,31 @@ public final class ActivityManagerService extends ActivityManagerNative // started. if (i >= N) { final long origId = Binder.clearCallingIdentity(); - ProcessRecord proc = startProcessLocked(cpi.processName, - cpr.appInfo, false, 0, "content provider", - new ComponentName(cpi.applicationInfo.packageName, - cpi.name), false); - if (proc == null) { - Slog.w(TAG, "Unable to launch app " - + cpi.applicationInfo.packageName + "/" - + cpi.applicationInfo.uid + " for provider " - + name + ": process is bad"); - return null; - } - cpr.launchingApp = proc; - mLaunchingProviders.add(cpr); - Binder.restoreCallingIdentity(origId); + + try { + // Content provider is now in use, its package can't be stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + cpr.appInfo.packageName, false); + } catch (RemoteException e) { + } + + ProcessRecord proc = startProcessLocked(cpi.processName, + cpr.appInfo, false, 0, "content provider", + new ComponentName(cpi.applicationInfo.packageName, + cpi.name), false); + if (proc == null) { + Slog.w(TAG, "Unable to launch app " + + cpi.applicationInfo.packageName + "/" + + cpi.applicationInfo.uid + " for provider " + + name + ": process is bad"); + return null; + } + cpr.launchingApp = proc; + mLaunchingProviders.add(cpr); + } finally { + Binder.restoreCallingIdentity(origId); + } } // Make sure the provider is published (the same provider class @@ -5800,6 +5830,13 @@ public final class ActivityManagerService extends ActivityManagerNative updateLruProcessLocked(app, true, true); } + // This package really, really can not be stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + info.packageName, false); + } catch (RemoteException e) { + } + if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) == (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) { app.persistent = true; @@ -9078,15 +9115,17 @@ public final class ActivityManagerService extends ActivityManagerNative ServiceRecord.StartItem si = r.pendingStarts.remove(0); if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: " + r + " " + r.intent + " args=" + si.intent); - if (si.intent == null) { - // If somehow we got a dummy start at the front, then - // just drop it here. + if (si.intent == null && N > 1) { + // If somehow we got a dummy null intent in the middle, + // then skip it. DO NOT skip a null intent when it is + // the only one in the list -- this is to support the + // onStartCommand(null) case. continue; } si.deliveredTime = SystemClock.uptimeMillis(); r.deliveredStarts.add(si); si.deliveryCount++; - if (si.targetPermissionUid >= 0) { + if (si.targetPermissionUid >= 0 && si.intent != null) { grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid, r.packageName, si.intent, si.getUriPermissionsLocked()); } @@ -9340,6 +9379,13 @@ public final class ActivityManagerService extends ActivityManagerNative // restarting state. mRestartingServices.remove(r); + // Service is now being launched, its package can't be stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + r.packageName, false); + } catch (RemoteException e) { + } + final String appName = r.processName; ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid); if (app != null && app.thread != null) { @@ -9637,7 +9683,7 @@ public final class ActivityManagerService extends ActivityManagerNative // r.record is null if findServiceLocked() failed the caller permission check if (r.record == null) { throw new SecurityException( - "Permission Denial: Accessing service " + "Permission Denial: Accessing service " + r.record.name + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + r.permission); @@ -10234,6 +10280,13 @@ public final class ActivityManagerService extends ActivityManagerNative ss = stats.getServiceStatsLocked(app.uid, app.packageName, app.name); } + // Backup agent is now in use, its package can't be stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + app.packageName, false); + } catch (RemoteException e) { + } + BackupRecord r = new BackupRecord(ss, app, backupMode); ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName); // startProcessLocked() returns existing proc's record if it's already running @@ -10521,6 +10574,9 @@ public final class ActivityManagerService extends ActivityManagerNative boolean ordered, boolean sticky, int callingPid, int callingUid) { intent = new Intent(intent); + // By default broadcasts do not go to stopped apps. + intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); + if (DEBUG_BROADCAST_LIGHT) Slog.v( TAG, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent + " ordered=" + ordered); @@ -11552,6 +11608,13 @@ public final class ActivityManagerService extends ActivityManagerNative info.activityInfo.name); r.curReceiver = info.activityInfo; + // Broadcast is being executed, its package can't be stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + r.curComponent.getPackageName(), false); + } catch (RemoteException e) { + } + // Is this receiver's application already running? ProcessRecord app = getProcessRecordLocked(targetProcess, info.activityInfo.applicationInfo.uid); @@ -12878,8 +12941,8 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("Unknown process: " + process); } - boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0")); - if (isSecure) { + boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); + if (!isDebuggable) { if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) { throw new SecurityException("Process not debuggable: " + proc); } @@ -12940,8 +13003,8 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("Unknown process: " + process); } - boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0")); - if (isSecure) { + boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); + if (!isDebuggable) { if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) { throw new SecurityException("Process not debuggable: " + proc); } diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 5b44d39..3a613bb 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -1293,6 +1293,14 @@ public class ActivityStack { } } + // Launching this app's activity, make sure the app is no longer + // considered stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + next.packageName, false); + } catch (RemoteException e1) { + } + // We are starting up the next activity, so tell the window manager // that the previous one will be hidden soon. This way it can know // to ignore it when computing the desired screen orientation. diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 26c7e71..5853696 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -26,13 +26,15 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; -import android.hardware.UsbManager; +import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; import android.net.InterfaceConfiguration; import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkInfo; +import android.net.NetworkUtils; import android.os.Binder; import android.os.Environment; import android.os.Handler; @@ -89,7 +91,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private BroadcastReceiver mStateReceiver; private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129"; - private static final String USB_NETMASK = "255.255.255.0"; + private static final int USB_PREFIX_LENGTH = 24; // USB is 192.168.42.1 and 255.255.255.0 // Wifi is 192.168.43.1 and 255.255.255.0 @@ -566,8 +568,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { try { ifcg = service.getInterfaceConfig(iface); if (ifcg != null) { - ifcg.addr = InetAddress.getByName(USB_NEAR_IFACE_ADDR); - ifcg.mask = InetAddress.getByName(USB_NETMASK); + InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR); + ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH); if (enabled) { ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); } else { @@ -1188,8 +1190,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub { try { service.startTethering(mDhcpRange); } catch (Exception e) { - transitionTo(mStartTetheringErrorState); - return false; + try { + service.stopTethering(); + service.startTethering(mDhcpRange); + } catch (Exception ee) { + transitionTo(mStartTetheringErrorState); + return false; + } } try { service.setDnsForwarders(mDnsServers); diff --git a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java new file mode 100644 index 0000000..9a96e7f --- /dev/null +++ b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java @@ -0,0 +1,908 @@ +/* + * 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.usb; + +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.content.res.XmlResourceParser; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbInterface; +import android.hardware.usb.UsbManager; +import android.os.Binder; +import android.os.FileUtils; +import android.os.Process; +import android.util.Log; +import android.util.SparseArray; +import android.util.Xml; + +import com.android.internal.content.PackageMonitor; +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +class UsbDeviceSettingsManager { + + private static final String TAG = "UsbDeviceSettingsManager"; + private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml"); + + private final Context mContext; + + // maps UID to user approved USB devices + private final SparseArray<ArrayList<DeviceFilter>> mDevicePermissionMap = + new SparseArray<ArrayList<DeviceFilter>>(); + // maps UID to user approved USB accessories + private final SparseArray<ArrayList<AccessoryFilter>> mAccessoryPermissionMap = + new SparseArray<ArrayList<AccessoryFilter>>(); + // Maps DeviceFilter to user preferred application package + private final HashMap<DeviceFilter, String> mDevicePreferenceMap = + new HashMap<DeviceFilter, String>(); + // Maps DeviceFilter to user preferred application package + private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap = + new HashMap<AccessoryFilter, String>(); + + private final Object mLock = new Object(); + + // This class is used to describe a USB device. + // When used in HashMaps all values must be specified, + // but wildcards can be used for any of the fields in + // the package meta-data. + private static class DeviceFilter { + // USB Vendor ID (or -1 for unspecified) + public final int mVendorId; + // USB Product ID (or -1 for unspecified) + public final int mProductId; + // USB device or interface class (or -1 for unspecified) + public final int mClass; + // USB device subclass (or -1 for unspecified) + public final int mSubclass; + // USB device protocol (or -1 for unspecified) + public final int mProtocol; + + public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol) { + mVendorId = vid; + mProductId = pid; + mClass = clasz; + mSubclass = subclass; + mProtocol = protocol; + } + + public DeviceFilter(UsbDevice device) { + mVendorId = device.getVendorId(); + mProductId = device.getProductId(); + mClass = device.getDeviceClass(); + mSubclass = device.getDeviceSubclass(); + mProtocol = device.getDeviceProtocol(); + } + + public static DeviceFilter read(XmlPullParser parser) + throws XmlPullParserException, IOException { + int vendorId = -1; + int productId = -1; + int deviceClass = -1; + int deviceSubclass = -1; + int deviceProtocol = -1; + + int count = parser.getAttributeCount(); + for (int i = 0; i < count; i++) { + String name = parser.getAttributeName(i); + // All attribute values are ints + int value = Integer.parseInt(parser.getAttributeValue(i)); + + if ("vendor-id".equals(name)) { + vendorId = value; + } else if ("product-id".equals(name)) { + productId = value; + } else if ("class".equals(name)) { + deviceClass = value; + } else if ("subclass".equals(name)) { + deviceSubclass = value; + } else if ("protocol".equals(name)) { + deviceProtocol = value; + } + } + return new DeviceFilter(vendorId, productId, + deviceClass, deviceSubclass, deviceProtocol); + } + + public void write(XmlSerializer serializer) throws IOException { + serializer.startTag(null, "usb-device"); + if (mVendorId != -1) { + serializer.attribute(null, "vendor-id", Integer.toString(mVendorId)); + } + if (mProductId != -1) { + serializer.attribute(null, "product-id", Integer.toString(mProductId)); + } + if (mClass != -1) { + serializer.attribute(null, "class", Integer.toString(mClass)); + } + if (mSubclass != -1) { + serializer.attribute(null, "subclass", Integer.toString(mSubclass)); + } + if (mProtocol != -1) { + serializer.attribute(null, "protocol", Integer.toString(mProtocol)); + } + serializer.endTag(null, "usb-device"); + } + + private boolean matches(int clasz, int subclass, int protocol) { + return ((mClass == -1 || clasz == mClass) && + (mSubclass == -1 || subclass == mSubclass) && + (mProtocol == -1 || protocol == mProtocol)); + } + + public boolean matches(UsbDevice device) { + if (mVendorId != -1 && device.getVendorId() != mVendorId) return false; + if (mProductId != -1 && device.getProductId() != mProductId) return false; + + // check device class/subclass/protocol + if (matches(device.getDeviceClass(), device.getDeviceSubclass(), + device.getDeviceProtocol())) return true; + + // if device doesn't match, check the interfaces + int count = device.getInterfaceCount(); + for (int i = 0; i < count; i++) { + UsbInterface intf = device.getInterface(i); + if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), + intf.getInterfaceProtocol())) return true; + } + + return false; + } + + @Override + public boolean equals(Object obj) { + // can't compare if we have wildcard strings + if (mVendorId == -1 || mProductId == -1 || + mClass == -1 || mSubclass == -1 || mProtocol == -1) { + return false; + } + if (obj instanceof DeviceFilter) { + DeviceFilter filter = (DeviceFilter)obj; + return (filter.mVendorId == mVendorId && + filter.mProductId == mProductId && + filter.mClass == mClass && + filter.mSubclass == mSubclass && + filter.mProtocol == mProtocol); + } + if (obj instanceof UsbDevice) { + UsbDevice device = (UsbDevice)obj; + return (device.getVendorId() == mVendorId && + device.getProductId() == mProductId && + device.getDeviceClass() == mClass && + device.getDeviceSubclass() == mSubclass && + device.getDeviceProtocol() == mProtocol); + } + return false; + } + + @Override + public int hashCode() { + return (((mVendorId << 16) | mProductId) ^ + ((mClass << 16) | (mSubclass << 8) | mProtocol)); + } + + @Override + public String toString() { + return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId + + ",mClass=" + mClass + ",mSubclass=" + mSubclass + + ",mProtocol=" + mProtocol + "]"; + } + } + + // This class is used to describe a USB accessory. + // When used in HashMaps all values must be specified, + // but wildcards can be used for any of the fields in + // the package meta-data. + private static class AccessoryFilter { + // USB accessory manufacturer (or null for unspecified) + public final String mManufacturer; + // USB accessory model (or null for unspecified) + public final String mModel; + // USB accessory type (or null for unspecified) + public final String mType; + // USB accessory version (or null for unspecified) + public final String mVersion; + + public AccessoryFilter(String manufacturer, String model, String type, String version) { + mManufacturer = manufacturer; + mModel = model; + mType = type; + mVersion = version; + } + + public AccessoryFilter(UsbAccessory accessory) { + mManufacturer = accessory.getManufacturer(); + mModel = accessory.getModel(); + mType = accessory.getType(); + mVersion = accessory.getVersion(); + } + + public static AccessoryFilter read(XmlPullParser parser) + throws XmlPullParserException, IOException { + String manufacturer = null; + String model = null; + String type = null; + String version = null; + + int count = parser.getAttributeCount(); + for (int i = 0; i < count; i++) { + String name = parser.getAttributeName(i); + String value = parser.getAttributeValue(i); + + if ("manufacturer".equals(name)) { + manufacturer = value; + } else if ("model".equals(name)) { + model = value; + } else if ("type".equals(name)) { + type = value; + } else if ("version".equals(name)) { + version = value; + } + } + return new AccessoryFilter(manufacturer, model, type, version); + } + + public void write(XmlSerializer serializer)throws IOException { + serializer.startTag(null, "usb-accessory"); + if (mManufacturer != null) { + serializer.attribute(null, "manufacturer", mManufacturer); + } + if (mModel != null) { + serializer.attribute(null, "model", mModel); + } + if (mType != null) { + serializer.attribute(null, "type", mType); + } + if (mVersion != null) { + serializer.attribute(null, "version", mVersion); + } + serializer.endTag(null, "usb-accessory"); + } + + public boolean matches(UsbAccessory acc) { + if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false; + if (mModel != null && !acc.getModel().equals(mModel)) return false; + if (mType != null && !acc.getType().equals(mType)) return false; + if (mVersion != null && !acc.getVersion().equals(mVersion)) return false; + return true; + } + + @Override + public boolean equals(Object obj) { + // can't compare if we have wildcard strings + if (mManufacturer == null || mModel == null || mType == null || mVersion == null) { + return false; + } + if (obj instanceof AccessoryFilter) { + AccessoryFilter filter = (AccessoryFilter)obj; + return (mManufacturer.equals(filter.mManufacturer) && + mModel.equals(filter.mModel) && + mType.equals(filter.mType) && + mVersion.equals(filter.mVersion)); + } + if (obj instanceof UsbAccessory) { + UsbAccessory accessory = (UsbAccessory)obj; + return (mManufacturer.equals(accessory.getManufacturer()) && + mModel.equals(accessory.getModel()) && + mType.equals(accessory.getType()) && + mVersion.equals(accessory.getVersion())); + } + return false; + } + + @Override + public int hashCode() { + return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^ + (mModel == null ? 0 : mModel.hashCode()) ^ + (mType == null ? 0 : mType.hashCode()) ^ + (mVersion == null ? 0 : mVersion.hashCode())); + } + + @Override + public String toString() { + return "AccessoryFilter[mManufacturer=\"" + mManufacturer + + "\", mModel=\"" + mModel + + "\", mType=\"" + mType + + "\", mVersion=\"" + mVersion + "\"]"; + } + } + + private class MyPackageMonitor extends PackageMonitor { + public void onPackageRemoved(String packageName, int uid) { + synchronized (mLock) { + // clear all activity preferences for the package + if (clearPackageDefaultsLocked(packageName)) { + writeSettingsLocked(); + } + } + } + + public void onUidRemoved(int uid) { + synchronized (mLock) { + // clear all permissions for the UID + if (clearUidDefaultsLocked(uid)) { + writeSettingsLocked(); + } + } + } + } + MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); + + public UsbDeviceSettingsManager(Context context) { + mContext = context; + synchronized (mLock) { + readSettingsLocked(); + } + mPackageMonitor.register(context, true); + } + + private void readDevicePermission(XmlPullParser parser) + throws XmlPullParserException, IOException { + int uid = -1; + ArrayList<DeviceFilter> filters = new ArrayList<DeviceFilter>(); + int count = parser.getAttributeCount(); + for (int i = 0; i < count; i++) { + if ("uid".equals(parser.getAttributeName(i))) { + uid = Integer.parseInt(parser.getAttributeValue(i)); + break; + } + } + XmlUtils.nextElement(parser); + while ("usb-device".equals(parser.getName())) { + filters.add(DeviceFilter.read(parser)); + XmlUtils.nextElement(parser); + } + mDevicePermissionMap.put(uid, filters); + } + + private void readAccessoryPermission(XmlPullParser parser) + throws XmlPullParserException, IOException { + int uid = -1; + ArrayList<AccessoryFilter> filters = new ArrayList<AccessoryFilter>(); + int count = parser.getAttributeCount(); + for (int i = 0; i < count; i++) { + if ("uid".equals(parser.getAttributeName(i))) { + uid = Integer.parseInt(parser.getAttributeValue(i)); + break; + } + } + XmlUtils.nextElement(parser); + while ("usb-accessory".equals(parser.getName())) { + filters.add(AccessoryFilter.read(parser)); + XmlUtils.nextElement(parser); + } + mAccessoryPermissionMap.put(uid, filters); + } + + private void readPreference(XmlPullParser parser) + throws XmlPullParserException, IOException { + String packageName = null; + int count = parser.getAttributeCount(); + for (int i = 0; i < count; i++) { + if ("package".equals(parser.getAttributeName(i))) { + packageName = parser.getAttributeValue(i); + break; + } + } + XmlUtils.nextElement(parser); + if ("usb-device".equals(parser.getName())) { + DeviceFilter filter = DeviceFilter.read(parser); + mDevicePreferenceMap.put(filter, packageName); + } else if ("usb-accessory".equals(parser.getName())) { + AccessoryFilter filter = AccessoryFilter.read(parser); + mAccessoryPreferenceMap.put(filter, packageName); + } + XmlUtils.nextElement(parser); + } + + private void readSettingsLocked() { + FileInputStream stream = null; + try { + stream = new FileInputStream(sSettingsFile); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(stream, null); + + XmlUtils.nextElement(parser); + while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { + String tagName = parser.getName(); + if ("device-permission".equals(tagName)) { + readDevicePermission(parser); + } else if ("accessory-permission".equals(tagName)) { + readAccessoryPermission(parser); + } else if ("preference".equals(tagName)) { + readPreference(parser); + } else { + XmlUtils.nextElement(parser); + } + } + } catch (FileNotFoundException e) { + Log.w(TAG, "settings file not found"); + } catch (Exception e) { + Log.e(TAG, "error reading settings file, deleting to start fresh", e); + sSettingsFile.delete(); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } + } + + private void writeSettingsLocked() { + FileOutputStream fos = null; + try { + FileOutputStream fstr = new FileOutputStream(sSettingsFile); + Log.d(TAG, "writing settings to " + fstr); + BufferedOutputStream str = new BufferedOutputStream(fstr); + FastXmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(str, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + serializer.startTag(null, "settings"); + + int count = mDevicePermissionMap.size(); + for (int i = 0; i < count; i++) { + int uid = mDevicePermissionMap.keyAt(i); + ArrayList<DeviceFilter> filters = mDevicePermissionMap.valueAt(i); + serializer.startTag(null, "device-permission"); + serializer.attribute(null, "uid", Integer.toString(uid)); + int filterCount = filters.size(); + for (int j = 0; j < filterCount; j++) { + filters.get(j).write(serializer); + } + serializer.endTag(null, "device-permission"); + } + + count = mAccessoryPermissionMap.size(); + for (int i = 0; i < count; i++) { + int uid = mAccessoryPermissionMap.keyAt(i); + ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i); + serializer.startTag(null, "accessory-permission"); + serializer.attribute(null, "uid", Integer.toString(uid)); + int filterCount = filters.size(); + for (int j = 0; j < filterCount; j++) { + filters.get(j).write(serializer); + } + serializer.endTag(null, "accessory-permission"); + } + + for (DeviceFilter filter : mDevicePreferenceMap.keySet()) { + serializer.startTag(null, "preference"); + serializer.attribute(null, "package", mDevicePreferenceMap.get(filter)); + filter.write(serializer); + serializer.endTag(null, "preference"); + } + + for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) { + serializer.startTag(null, "preference"); + serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter)); + filter.write(serializer); + serializer.endTag(null, "preference"); + } + + serializer.endTag(null, "settings"); + serializer.endDocument(); + + str.flush(); + FileUtils.sync(fstr); + str.close(); + } catch (Exception e) { + Log.e(TAG, "error writing settings file, deleting to start fresh", e); + sSettingsFile.delete(); + } + } + + // Checks to see if a package matches a device or accessory. + // Only one of device and accessory should be non-null. + private boolean packageMatchesLocked(ResolveInfo info, String metaDataName, + UsbDevice device, UsbAccessory accessory) { + ActivityInfo ai = info.activityInfo; + PackageManager pm = mContext.getPackageManager(); + + XmlResourceParser parser = null; + try { + parser = ai.loadXmlMetaData(pm, metaDataName); + if (parser == null) { + Log.w(TAG, "no meta-data for " + info); + return false; + } + + XmlUtils.nextElement(parser); + while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { + String tagName = parser.getName(); + if (device != null && "usb-device".equals(tagName)) { + DeviceFilter filter = DeviceFilter.read(parser); + if (filter.matches(device)) { + return true; + } + } + else if (accessory != null && "usb-accessory".equals(tagName)) { + AccessoryFilter filter = AccessoryFilter.read(parser); + if (filter.matches(accessory)) { + return true; + } + } + XmlUtils.nextElement(parser); + } + } catch (Exception e) { + Log.w(TAG, "Unable to load component info " + info.toString(), e); + } finally { + if (parser != null) parser.close(); + } + return false; + } + + private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) { + ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>(); + PackageManager pm = mContext.getPackageManager(); + List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, + PackageManager.GET_META_DATA); + int count = resolveInfos.size(); + for (int i = 0; i < count; i++) { + ResolveInfo resolveInfo = resolveInfos.get(i); + if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) { + matches.add(resolveInfo); + } + } + return matches; + } + + private final ArrayList<ResolveInfo> getAccessoryMatchesLocked( + UsbAccessory accessory, Intent intent) { + ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>(); + PackageManager pm = mContext.getPackageManager(); + List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, + PackageManager.GET_META_DATA); + int count = resolveInfos.size(); + for (int i = 0; i < count; i++) { + ResolveInfo resolveInfo = resolveInfos.get(i); + if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) { + matches.add(resolveInfo); + } + } + return matches; + } + + public void deviceAttached(UsbDevice device) { + Intent deviceIntent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); + deviceIntent.putExtra(UsbManager.EXTRA_DEVICE, device); + deviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + ArrayList<ResolveInfo> matches; + String defaultPackage; + synchronized (mLock) { + matches = getDeviceMatchesLocked(device, deviceIntent); + // Launch our default activity directly, if we have one. + // Otherwise we will start the UsbResolverActivity to allow the user to choose. + defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device)); + } + + if (defaultPackage != null) { + int count = matches.size(); + for (int i = 0; i < count; i++) { + ResolveInfo rInfo = matches.get(i); + if (rInfo.activityInfo != null && + defaultPackage.equals(rInfo.activityInfo.packageName)) { + try { + deviceIntent.setComponent(new ComponentName( + defaultPackage, rInfo.activityInfo.name)); + mContext.startActivity(deviceIntent); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "startActivity failed", e); + } + return; + } + } + } + + Intent intent = new Intent(mContext, UsbResolverActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + intent.putExtra(Intent.EXTRA_INTENT, deviceIntent); + intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, matches); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "unable to start UsbResolverActivity"); + } + } + + public void deviceDetached(UsbDevice device) { + Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED); + intent.putExtra(UsbManager.EXTRA_DEVICE, device); + Log.d(TAG, "usbDeviceRemoved, sending " + intent); + mContext.sendBroadcast(intent); + } + + public void accessoryAttached(UsbAccessory accessory) { + Intent accessoryIntent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); + accessoryIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); + accessoryIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + ArrayList<ResolveInfo> matches; + String defaultPackage; + synchronized (mLock) { + matches = getAccessoryMatchesLocked(accessory, accessoryIntent); + // Launch our default activity directly, if we have one. + // Otherwise we will start the UsbResolverActivity to allow the user to choose. + defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)); + } + + if (defaultPackage != null) { + int count = matches.size(); + for (int i = 0; i < count; i++) { + ResolveInfo rInfo = matches.get(i); + if (rInfo.activityInfo != null && + defaultPackage.equals(rInfo.activityInfo.packageName)) { + try { + accessoryIntent.setComponent(new ComponentName( + defaultPackage, rInfo.activityInfo.name)); + mContext.startActivity(accessoryIntent); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "startActivity failed", e); + } + return; + } + } + } + + Intent intent = new Intent(mContext, UsbResolverActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + intent.putExtra(Intent.EXTRA_INTENT, accessoryIntent); + intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, matches); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "unable to start UsbResolverActivity"); + } + } + + public void accessoryDetached(UsbAccessory accessory) { + Intent intent = new Intent( + UsbManager.ACTION_USB_ACCESSORY_DETACHED); + intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); + mContext.sendBroadcast(intent); + } + + public void checkPermission(UsbDevice device) { + if (device == null) return; + synchronized (mLock) { + ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(Binder.getCallingUid()); + if (filterList != null) { + int count = filterList.size(); + for (int i = 0; i < count; i++) { + DeviceFilter filter = filterList.get(i); + if (filter.equals(device)) { + // permission allowed + return; + } + } + } + } + throw new SecurityException("User has not given permission to device " + device); + } + + public void checkPermission(UsbAccessory accessory) { + if (accessory == null) return; + synchronized (mLock) { + ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(Binder.getCallingUid()); + if (filterList != null) { + int count = filterList.size(); + for (int i = 0; i < count; i++) { + AccessoryFilter filter = filterList.get(i); + if (filter.equals(accessory)) { + // permission allowed + return; + } + } + } + } + throw new SecurityException("User has not given permission to accessory " + accessory); + } + + public void setDevicePackage(UsbDevice device, String packageName) { + DeviceFilter filter = new DeviceFilter(device); + boolean changed = false; + synchronized (mLock) { + if (packageName == null) { + changed = (mDevicePreferenceMap.remove(filter) != null); + } else { + changed = !packageName.equals(mDevicePreferenceMap.get(filter)); + if (changed) { + mDevicePreferenceMap.put(filter, packageName); + } + } + if (changed) { + writeSettingsLocked(); + } + } + } + + public void setAccessoryPackage(UsbAccessory accessory, String packageName) { + AccessoryFilter filter = new AccessoryFilter(accessory); + boolean changed = false; + synchronized (mLock) { + if (packageName == null) { + changed = (mAccessoryPreferenceMap.remove(filter) != null); + } else { + changed = !packageName.equals(mAccessoryPreferenceMap.get(filter)); + if (changed) { + mAccessoryPreferenceMap.put(filter, packageName); + } + } + if (changed) { + writeSettingsLocked(); + } + } + } + + public void grantDevicePermission(UsbDevice device, int uid) { + synchronized (mLock) { + ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(uid); + if (filterList == null) { + filterList = new ArrayList<DeviceFilter>(); + mDevicePermissionMap.put(uid, filterList); + } else { + int count = filterList.size(); + for (int i = 0; i < count; i++) { + if (filterList.get(i).equals(device)) return; + } + } + filterList.add(new DeviceFilter(device)); + writeSettingsLocked(); + } + } + + public void grantAccessoryPermission(UsbAccessory accessory, int uid) { + synchronized (mLock) { + ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(uid); + if (filterList == null) { + filterList = new ArrayList<AccessoryFilter>(); + mAccessoryPermissionMap.put(uid, filterList); + } else { + int count = filterList.size(); + for (int i = 0; i < count; i++) { + if (filterList.get(i).equals(accessory)) return; + } + } + filterList.add(new AccessoryFilter(accessory)); + writeSettingsLocked(); + } + } + + public boolean hasDefaults(String packageName, int uid) { + synchronized (mLock) { + if (mDevicePermissionMap.get(uid) != null) return true; + if (mAccessoryPermissionMap.get(uid) != null) return true; + if (mDevicePreferenceMap.values().contains(packageName)) return true; + if (mAccessoryPreferenceMap.values().contains(packageName)) return true; + return false; + } + } + + public void clearDefaults(String packageName, int uid) { + synchronized (mLock) { + boolean packageCleared = clearPackageDefaultsLocked(packageName); + boolean uidCleared = clearUidDefaultsLocked(uid); + if (packageCleared || uidCleared) { + writeSettingsLocked(); + } + } + } + + private boolean clearUidDefaultsLocked(int uid) { + boolean cleared = false; + int index = mDevicePermissionMap.indexOfKey(uid); + if (index >= 0) { + mDevicePermissionMap.removeAt(index); + cleared = true; + } + index = mAccessoryPermissionMap.indexOfKey(uid); + if (index >= 0) { + mAccessoryPermissionMap.removeAt(index); + cleared = true; + } + return cleared; + } + + private boolean clearPackageDefaultsLocked(String packageName) { + boolean cleared = false; + synchronized (mLock) { + if (mDevicePreferenceMap.containsValue(packageName)) { + // make a copy of the key set to avoid ConcurrentModificationException + Object[] keys = mDevicePreferenceMap.keySet().toArray(); + for (int i = 0; i < keys.length; i++) { + Object key = keys[i]; + if (packageName.equals(mDevicePreferenceMap.get(key))) { + mDevicePreferenceMap.remove(key); + cleared = true; + } + } + } + if (mAccessoryPreferenceMap.containsValue(packageName)) { + // make a copy of the key set to avoid ConcurrentModificationException + Object[] keys = mAccessoryPreferenceMap.keySet().toArray(); + for (int i = 0; i < keys.length; i++) { + Object key = keys[i]; + if (packageName.equals(mAccessoryPreferenceMap.get(key))) { + mAccessoryPreferenceMap.remove(key); + cleared = true; + } + } + } + return cleared; + } + } + + public void dump(FileDescriptor fd, PrintWriter pw) { + synchronized (mLock) { + pw.println(" Device permissions:"); + int count = mDevicePermissionMap.size(); + for (int i = 0; i < count; i++) { + int uid = mDevicePermissionMap.keyAt(i); + pw.println(" " + "uid " + uid + ":"); + ArrayList<DeviceFilter> filters = mDevicePermissionMap.valueAt(i); + for (DeviceFilter filter : filters) { + pw.println(" " + filter); + } + } + pw.println(" Accessory permissions:"); + count = mAccessoryPermissionMap.size(); + for (int i = 0; i < count; i++) { + int uid = mAccessoryPermissionMap.keyAt(i); + pw.println(" " + "uid " + uid + ":"); + ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i); + for (AccessoryFilter filter : filters) { + pw.println(" " + filter); + } + } + pw.println(" Device preferences:"); + for (DeviceFilter filter : mDevicePreferenceMap.keySet()) { + pw.println(" " + filter + ": " + mDevicePreferenceMap.get(filter)); + } + pw.println(" Accessory preferences:"); + for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) { + pw.println(" " + filter + ": " + mAccessoryPreferenceMap.get(filter)); + } + } + } +} diff --git a/services/java/com/android/server/usb/UsbResolverActivity.java b/services/java/com/android/server/usb/UsbResolverActivity.java new file mode 100644 index 0000000..e8a09a5 --- /dev/null +++ b/services/java/com/android/server/usb/UsbResolverActivity.java @@ -0,0 +1,102 @@ +/* + * 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.usb; + +import com.android.internal.app.ResolverActivity; + +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.hardware.usb.IUsbManager; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.util.ArrayList; + +/* Activity for choosing an application for a USB device or accessory */ +public class UsbResolverActivity extends ResolverActivity { + public static final String TAG = "UsbResolverActivity"; + public static final String EXTRA_RESOLVE_INFOS = "rlist"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + Intent intent = getIntent(); + Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT); + if (!(targetParcelable instanceof Intent)) { + Log.w("UsbResolverActivity", "Target is not an intent: " + targetParcelable); + finish(); + return; + } + Intent target = (Intent)targetParcelable; + ArrayList<ResolveInfo> rList = intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS); + Log.d(TAG, "rList.size() " + rList.size()); + CharSequence title = getResources().getText(com.android.internal.R.string.chooseUsbActivity); + super.onCreate(savedInstanceState, target, title, null, rList, + true, /* Set alwaysUseOption to true to enable "always use this app" checkbox. */ + true /* Set alwaysChoose to display activity when only one choice is available. + This is necessary because this activity is needed for the user to allow + the application permission to access the device */ + ); + } + + protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) { + try { + IBinder b = ServiceManager.getService(USB_SERVICE); + IUsbManager service = IUsbManager.Stub.asInterface(b); + int uid = ri.activityInfo.applicationInfo.uid; + String action = intent.getAction(); + + if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { + UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + // grant permission for the device + service.grantDevicePermission(device, uid); + // set or clear default setting + if (alwaysCheck) { + service.setDevicePackage(device, ri.activityInfo.packageName); + } else { + service.setDevicePackage(device, null); + } + } else if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) { + UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra( + UsbManager.EXTRA_ACCESSORY); + // grant permission for the accessory + service.grantAccessoryPermission(accessory, uid); + // set or clear default setting + if (alwaysCheck) { + service.setAccessoryPackage(accessory, ri.activityInfo.packageName); + } else { + service.setAccessoryPackage(accessory, null); + } + } + + try { + startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "startActivity failed", e); + } + } catch (RemoteException e) { + Log.e(TAG, "onIntentSelected failed", e); + } + } +} diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java new file mode 100644 index 0000000..94c25e9 --- /dev/null +++ b/services/java/com/android/server/usb/UsbService.java @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2010 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.usb; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.hardware.usb.IUsbManager; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.hardware.usb.UsbManager; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Parcelable; +import android.os.ParcelFileDescriptor; +import android.os.UEventObserver; +import android.provider.Settings; +import android.util.Log; +import android.util.Slog; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * UsbService monitors for changes to USB state. + * This includes code for both USB host support (where the android device is the host) + * as well as USB device support (android device is connected to a USB host). + * Accessory mode is a special case of USB device mode, where the android device is + * connected to a USB host that supports the android accessory protocol. + */ +public class UsbService extends IUsbManager.Stub { + private static final String TAG = UsbService.class.getSimpleName(); + private static final boolean LOG = false; + + private static final String USB_CONNECTED_MATCH = + "DEVPATH=/devices/virtual/switch/usb_connected"; + private static final String USB_CONFIGURATION_MATCH = + "DEVPATH=/devices/virtual/switch/usb_configuration"; + private static final String USB_FUNCTIONS_MATCH = + "DEVPATH=/devices/virtual/usb_composite/"; + private static final String USB_CONNECTED_PATH = + "/sys/class/switch/usb_connected/state"; + private static final String USB_CONFIGURATION_PATH = + "/sys/class/switch/usb_configuration/state"; + private static final String USB_COMPOSITE_CLASS_PATH = + "/sys/class/usb_composite"; + + private static final int MSG_UPDATE_STATE = 0; + private static final int MSG_FUNCTION_ENABLED = 1; + private static final int MSG_FUNCTION_DISABLED = 2; + + // Delay for debouncing USB disconnects. + // We often get rapid connect/disconnect events when enabling USB functions, + // which need debouncing. + private static final int UPDATE_DELAY = 1000; + + // current connected and configuration state + private int mConnected; + private int mConfiguration; + + // last broadcasted connected and configuration state + private int mLastConnected = -1; + private int mLastConfiguration = -1; + + // lists of enabled and disabled USB functions (for USB device mode) + private final ArrayList<String> mEnabledFunctions = new ArrayList<String>(); + private final ArrayList<String> mDisabledFunctions = new ArrayList<String>(); + + // contains all connected USB devices (for USB host mode) + private final HashMap<String,UsbDevice> mDevices = new HashMap<String,UsbDevice>(); + + // USB busses to exclude from USB host support + private final String[] mHostBlacklist; + + private boolean mSystemReady; + + private UsbAccessory mCurrentAccessory; + // functions to restore after exiting accessory mode + private final ArrayList<String> mAccessoryRestoreFunctions = new ArrayList<String>(); + + private final Context mContext; + private final Object mLock = new Object(); + private final UsbDeviceSettingsManager mDeviceManager; + private final boolean mHasUsbHost; + private final boolean mHasUsbAccessory; + + /* + * Handles USB function enable/disable events (device mode) + */ + private final void functionEnabledLocked(String function, boolean enabled) { + boolean enteringAccessoryMode = + (mHasUsbAccessory && enabled && UsbManager.USB_FUNCTION_ACCESSORY.equals(function)); + + if (enteringAccessoryMode) { + // keep a list of functions to reenable after exiting accessory mode + mAccessoryRestoreFunctions.clear(); + int count = mEnabledFunctions.size(); + for (int i = 0; i < count; i++) { + String f = mEnabledFunctions.get(i); + // RNDIS should not be restored and adb is handled automatically + if (!UsbManager.USB_FUNCTION_RNDIS.equals(f) && + !UsbManager.USB_FUNCTION_ADB.equals(f) && + !UsbManager.USB_FUNCTION_ACCESSORY.equals(f)) { + mAccessoryRestoreFunctions.add(f); + } + } + } + if (enabled) { + if (!mEnabledFunctions.contains(function)) { + mEnabledFunctions.add(function); + } + mDisabledFunctions.remove(function); + } else { + if (!mDisabledFunctions.contains(function)) { + mDisabledFunctions.add(function); + } + mEnabledFunctions.remove(function); + } + + if (enteringAccessoryMode) { + String[] strings = nativeGetAccessoryStrings(); + if (strings != null) { + mCurrentAccessory = new UsbAccessory(strings); + Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); + mDeviceManager.accessoryAttached(mCurrentAccessory); + } else { + Log.e(TAG, "nativeGetAccessoryStrings failed"); + } + } + } + + /* + * Listens for uevent messages from the kernel to monitor the USB state (device mode) + */ + private final UEventObserver mUEventObserver = new UEventObserver() { + @Override + public void onUEvent(UEventObserver.UEvent event) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Slog.v(TAG, "USB UEVENT: " + event.toString()); + } + + synchronized (mLock) { + String name = event.get("SWITCH_NAME"); + String state = event.get("SWITCH_STATE"); + if (name != null && state != null) { + try { + int intState = Integer.parseInt(state); + if ("usb_connected".equals(name)) { + mConnected = intState; + // trigger an Intent broadcast + if (mSystemReady) { + // debounce disconnects to avoid problems bringing up USB tethering + update(mConnected == 0); + } + } else if ("usb_configuration".equals(name)) { + mConfiguration = intState; + // trigger an Intent broadcast + if (mSystemReady) { + update(mConnected == 0); + } + } + } catch (NumberFormatException e) { + Slog.e(TAG, "Could not parse switch state from event " + event); + } + } else { + String function = event.get("FUNCTION"); + String enabledStr = event.get("ENABLED"); + if (function != null && enabledStr != null) { + // Note: we do not broadcast a change when a function is enabled or disabled. + // We just record the state change for the next broadcast. + int what = ("1".equals(enabledStr) ? + MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED); + Message msg = Message.obtain(mHandler, what); + msg.obj = function; + mHandler.sendMessage(msg); + } + } + } + } + }; + + public UsbService(Context context) { + mContext = context; + mDeviceManager = new UsbDeviceSettingsManager(context); + PackageManager pm = mContext.getPackageManager(); + mHasUsbHost = pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST); + mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY); + + mHostBlacklist = context.getResources().getStringArray( + com.android.internal.R.array.config_usbHostBlacklist); + + init(); // set initial status + + if (mConfiguration >= 0) { + mUEventObserver.startObserving(USB_CONNECTED_MATCH); + mUEventObserver.startObserving(USB_CONFIGURATION_MATCH); + mUEventObserver.startObserving(USB_FUNCTIONS_MATCH); + } + } + + private final void init() { + char[] buffer = new char[1024]; + + // Read initial USB state (device mode) + mConfiguration = -1; + try { + FileReader file = new FileReader(USB_CONNECTED_PATH); + int len = file.read(buffer, 0, 1024); + file.close(); + mConnected = Integer.valueOf((new String(buffer, 0, len)).trim()); + + file = new FileReader(USB_CONFIGURATION_PATH); + len = file.read(buffer, 0, 1024); + file.close(); + mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim()); + + } catch (FileNotFoundException e) { + Slog.i(TAG, "This kernel does not have USB configuration switch support"); + } catch (Exception e) { + Slog.e(TAG, "" , e); + } + if (mConfiguration < 0) + return; + + // Read initial list of enabled and disabled functions (device mode) + try { + File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles(); + for (int i = 0; i < files.length; i++) { + File file = new File(files[i], "enable"); + FileReader reader = new FileReader(file); + int len = reader.read(buffer, 0, 1024); + reader.close(); + int value = Integer.valueOf((new String(buffer, 0, len)).trim()); + String functionName = files[i].getName(); + if (value == 1) { + mEnabledFunctions.add(functionName); + } else { + mDisabledFunctions.add(functionName); + } + } + } catch (FileNotFoundException e) { + Slog.w(TAG, "This kernel does not have USB composite class support"); + } catch (Exception e) { + Slog.e(TAG, "" , e); + } + } + + private boolean isBlackListed(String deviceName) { + int count = mHostBlacklist.length; + for (int i = 0; i < count; i++) { + if (deviceName.startsWith(mHostBlacklist[i])) { + return true; + } + } + return false; + } + + /* returns true if the USB device should not be accessible by applications (host mode) */ + private boolean isBlackListed(int clazz, int subClass, int protocol) { + // blacklist hubs + if (clazz == UsbConstants.USB_CLASS_HUB) return true; + + // blacklist HID boot devices (mouse and keyboard) + if (clazz == UsbConstants.USB_CLASS_HID && + subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) { + return true; + } + + return false; + } + + /* Called from JNI in monitorUsbHostBus() to report new USB devices (host mode) */ + private void usbDeviceAdded(String deviceName, int vendorID, int productID, + int deviceClass, int deviceSubclass, int deviceProtocol, + /* array of quintuples containing id, class, subclass, protocol + and number of endpoints for each interface */ + int[] interfaceValues, + /* array of quadruples containing address, attributes, max packet size + and interval for each endpoint */ + int[] endpointValues) { + + if (isBlackListed(deviceName) || + isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) { + return; + } + + synchronized (mLock) { + if (mDevices.get(deviceName) != null) { + Log.w(TAG, "device already on mDevices list: " + deviceName); + return; + } + + int numInterfaces = interfaceValues.length / 5; + Parcelable[] interfaces = new UsbInterface[numInterfaces]; + try { + // repackage interfaceValues as an array of UsbInterface + int intf, endp, ival = 0, eval = 0; + for (intf = 0; intf < numInterfaces; intf++) { + int interfaceId = interfaceValues[ival++]; + int interfaceClass = interfaceValues[ival++]; + int interfaceSubclass = interfaceValues[ival++]; + int interfaceProtocol = interfaceValues[ival++]; + int numEndpoints = interfaceValues[ival++]; + + Parcelable[] endpoints = new UsbEndpoint[numEndpoints]; + for (endp = 0; endp < numEndpoints; endp++) { + int address = endpointValues[eval++]; + int attributes = endpointValues[eval++]; + int maxPacketSize = endpointValues[eval++]; + int interval = endpointValues[eval++]; + endpoints[endp] = new UsbEndpoint(address, attributes, + maxPacketSize, interval); + } + + // don't allow if any interfaces are blacklisted + if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) { + return; + } + interfaces[intf] = new UsbInterface(interfaceId, interfaceClass, + interfaceSubclass, interfaceProtocol, endpoints); + } + } catch (Exception e) { + // beware of index out of bound exceptions, which might happen if + // a device does not set bNumEndpoints correctly + Log.e(TAG, "error parsing USB descriptors", e); + return; + } + + UsbDevice device = new UsbDevice(deviceName, vendorID, productID, + deviceClass, deviceSubclass, deviceProtocol, interfaces); + mDevices.put(deviceName, device); + mDeviceManager.deviceAttached(device); + } + } + + /* Called from JNI in monitorUsbHostBus to report USB device removal (host mode) */ + private void usbDeviceRemoved(String deviceName) { + synchronized (mLock) { + UsbDevice device = mDevices.remove(deviceName); + if (device != null) { + mDeviceManager.deviceDetached(device); + } + } + } + + private void initHostSupport() { + // Create a thread to call into native code to wait for USB host events. + // This thread will call us back on usbDeviceAdded and usbDeviceRemoved. + Runnable runnable = new Runnable() { + public void run() { + monitorUsbHostBus(); + } + }; + new Thread(null, runnable, "UsbService host thread").start(); + } + + public void systemReady() { + synchronized (mLock) { + if (mHasUsbHost) { + // start monitoring for connected USB devices + initHostSupport(); + } + + update(false); + mSystemReady = true; + } + } + + /* + * Sends a message to update the USB connected and configured state (device mode). + * If delayed is true, then we add a small delay in sending the message to debounce + * the USB connection when enabling USB tethering. + */ + private final void update(boolean delayed) { + mHandler.removeMessages(MSG_UPDATE_STATE); + mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0); + } + + /* Returns a list of all currently attached USB devices (host mdoe) */ + public void getDeviceList(Bundle devices) { + synchronized (mLock) { + for (String name : mDevices.keySet()) { + devices.putParcelable(name, mDevices.get(name)); + } + } + } + + /* Opens the specified USB device (host mode) */ + public ParcelFileDescriptor openDevice(String deviceName) { + synchronized (mLock) { + if (isBlackListed(deviceName)) { + throw new SecurityException("USB device is on a restricted bus"); + } + UsbDevice device = mDevices.get(deviceName); + if (device == null) { + // if it is not in mDevices, it either does not exist or is blacklisted + throw new IllegalArgumentException( + "device " + deviceName + " does not exist or is restricted"); + } + mDeviceManager.checkPermission(device); + return nativeOpenDevice(deviceName); + } + } + + /* returns the currently attached USB accessory (device mode) */ + public UsbAccessory getCurrentAccessory() { + synchronized (mLock) { + mDeviceManager.checkPermission(mCurrentAccessory); + return mCurrentAccessory; + } + } + + /* opens the currently attached USB accessory (device mode) */ + public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { + synchronized (mLock) { + if (mCurrentAccessory == null) { + throw new IllegalArgumentException("no accessory attached"); + } + if (!mCurrentAccessory.equals(accessory)) { + Log.e(TAG, accessory.toString() + " does not match current accessory " + + mCurrentAccessory); + throw new IllegalArgumentException("accessory not attached"); + } + mDeviceManager.checkPermission(mCurrentAccessory); + return nativeOpenAccessory(); + } + } + + public void setDevicePackage(UsbDevice device, String packageName) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + mDeviceManager.setDevicePackage(device, packageName); + } + + public void setAccessoryPackage(UsbAccessory accessory, String packageName) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + mDeviceManager.setAccessoryPackage(accessory, packageName); + } + + public void grantDevicePermission(UsbDevice device, int uid) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + mDeviceManager.grantDevicePermission(device, uid); + } + + public void grantAccessoryPermission(UsbAccessory accessory, int uid) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + mDeviceManager.grantAccessoryPermission(accessory, uid); + } + + public boolean hasDefaults(String packageName, int uid) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + return mDeviceManager.hasDefaults(packageName, uid); + } + + public void clearDefaults(String packageName, int uid) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + mDeviceManager.clearDefaults(packageName, uid); + } + + /* + * This handler is for deferred handling of events related to device mode and accessories. + */ + private final Handler mHandler = new Handler() { + private void addEnabledFunctionsLocked(Intent intent) { + // include state of all USB functions in our extras + for (int i = 0; i < mEnabledFunctions.size(); i++) { + intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED); + } + for (int i = 0; i < mDisabledFunctions.size(); i++) { + intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED); + } + } + + @Override + public void handleMessage(Message msg) { + synchronized (mLock) { + switch (msg.what) { + case MSG_UPDATE_STATE: + if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) { + if (mConnected == 0 && mCurrentAccessory != null) { + // turn off accessory mode when we are disconnected + if (UsbManager.setFunctionEnabled( + UsbManager.USB_FUNCTION_ACCESSORY, false)) { + Log.d(TAG, "exited USB accessory mode"); + + // restore previously enabled functions + for (String function : mAccessoryRestoreFunctions) { + if (UsbManager.setFunctionEnabled(function, true)) { + Log.e(TAG, "could not reenable function " + function); + } + } + mAccessoryRestoreFunctions.clear(); + + mDeviceManager.accessoryDetached(mCurrentAccessory); + mCurrentAccessory = null; + + // this will cause an immediate reset of the USB bus, + // so there is no point in sending the + // function disabled broadcast. + return; + } else { + Log.e(TAG, "could not disable USB_FUNCTION_ACCESSORY"); + } + } + + final ContentResolver cr = mContext.getContentResolver(); + if (Settings.Secure.getInt(cr, + Settings.Secure.DEVICE_PROVISIONED, 0) == 0) { + Slog.i(TAG, "Device not provisioned, skipping USB broadcast"); + return; + } + + mLastConnected = mConnected; + mLastConfiguration = mConfiguration; + + // send a sticky broadcast containing current USB state + Intent intent = new Intent(UsbManager.ACTION_USB_STATE); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0); + intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration); + addEnabledFunctionsLocked(intent); + mContext.sendStickyBroadcast(intent); + } + break; + case MSG_FUNCTION_ENABLED: + case MSG_FUNCTION_DISABLED: + functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED); + break; + } + } + } + }; + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump UsbManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + synchronized (mLock) { + pw.println("USB Manager State:"); + + pw.println(" USB Device State:"); + pw.print(" Enabled Functions: "); + for (int i = 0; i < mEnabledFunctions.size(); i++) { + pw.print(mEnabledFunctions.get(i) + " "); + } + pw.println(""); + pw.print(" Disabled Functions: "); + for (int i = 0; i < mDisabledFunctions.size(); i++) { + pw.print(mDisabledFunctions.get(i) + " "); + } + pw.println(""); + pw.println(" mConnected: " + mConnected + ", mConfiguration: " + mConfiguration); + + pw.println(" USB Host State:"); + for (String name : mDevices.keySet()) { + pw.println(" " + name + ": " + mDevices.get(name)); + } + pw.println(" mCurrentAccessory: " + mCurrentAccessory); + + mDeviceManager.dump(fd, pw); + } + } + + // host support + private native void monitorUsbHostBus(); + private native ParcelFileDescriptor nativeOpenDevice(String deviceName); + // accessory support + private native String[] nativeGetAccessoryStrings(); + private native ParcelFileDescriptor nativeOpenAccessory(); +} diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java new file mode 100644 index 0000000..d3d9df4 --- /dev/null +++ b/services/java/com/android/server/wm/AppWindowToken.java @@ -0,0 +1,413 @@ +/* + * 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.wm; + +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; + +import com.android.server.wm.WindowManagerService.H; + +import android.content.pm.ActivityInfo; +import android.os.Message; +import android.os.RemoteException; +import android.util.Slog; +import android.view.IApplicationToken; +import android.view.View; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Version of WindowToken that is specifically for a particular application (or + * really activity) that is displaying windows. + */ +class AppWindowToken extends WindowToken { + // Non-null only for application tokens. + final IApplicationToken appToken; + + // All of the windows and child windows that are included in this + // application token. Note this list is NOT sorted! + final ArrayList<WindowState> allAppWindows = new ArrayList<WindowState>(); + + int groupId = -1; + boolean appFullscreen; + int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + + // The input dispatching timeout for this application token in nanoseconds. + long inputDispatchingTimeoutNanos; + + // These are used for determining when all windows associated with + // an activity have been drawn, so they can be made visible together + // at the same time. + int lastTransactionSequence; + int numInterestingWindows; + int numDrawnWindows; + boolean inPendingTransaction; + boolean allDrawn; + + // Is this token going to be hidden in a little while? If so, it + // won't be taken into account for setting the screen orientation. + boolean willBeHidden; + + // Is this window's surface needed? This is almost like hidden, except + // it will sometimes be true a little earlier: when the token has + // been shown, but is still waiting for its app transition to execute + // before making its windows shown. + boolean hiddenRequested; + + // Have we told the window clients to hide themselves? + boolean clientHidden; + + // Last visibility state we reported to the app token. + boolean reportedVisible; + + // Set to true when the token has been removed from the window mgr. + boolean removed; + + // Have we been asked to have this token keep the screen frozen? + boolean freezingScreen; + + boolean animating; + Animation animation; + boolean hasTransformation; + final Transformation transformation = new Transformation(); + + // Offset to the window of all layers in the token, for use by + // AppWindowToken animations. + int animLayerAdjustment; + + // Information about an application starting window if displayed. + StartingData startingData; + WindowState startingWindow; + View startingView; + boolean startingDisplayed; + boolean startingMoved; + boolean firstWindowDrawn; + + // Input application handle used by the input dispatcher. + InputApplicationHandle mInputApplicationHandle; + + AppWindowToken(WindowManagerService _service, IApplicationToken _token) { + super(_service, _token.asBinder(), + WindowManager.LayoutParams.TYPE_APPLICATION, true); + appWindowToken = this; + appToken = _token; + mInputApplicationHandle = new InputApplicationHandle(this); + lastTransactionSequence = service.mTransactionSequence-1; + } + + public void setAnimation(Animation anim) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Setting animation in " + this + ": " + anim); + animation = anim; + animating = false; + anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION); + anim.scaleCurrentDuration(service.mTransitionAnimationScale); + int zorder = anim.getZAdjustment(); + int adj = 0; + if (zorder == Animation.ZORDER_TOP) { + adj = WindowManagerService.TYPE_LAYER_OFFSET; + } else if (zorder == Animation.ZORDER_BOTTOM) { + adj = -WindowManagerService.TYPE_LAYER_OFFSET; + } + + if (animLayerAdjustment != adj) { + animLayerAdjustment = adj; + updateLayers(); + } + } + + public void setDummyAnimation() { + if (animation == null) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Setting dummy animation in " + this); + animation = WindowManagerService.sDummyAnimation; + } + } + + public void clearAnimation() { + if (animation != null) { + animation = null; + animating = true; + } + } + + void updateLayers() { + final int N = allAppWindows.size(); + final int adj = animLayerAdjustment; + for (int i=0; i<N; i++) { + WindowState w = allAppWindows.get(i); + w.mAnimLayer = w.mLayer + adj; + if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": " + + w.mAnimLayer); + if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) { + service.setInputMethodAnimLayerAdjustment(adj); + } + if (w == service.mWallpaperTarget && service.mLowerWallpaperTarget == null) { + service.setWallpaperAnimLayerAdjustmentLocked(adj); + } + } + } + + void sendAppVisibilityToClients() { + final int N = allAppWindows.size(); + for (int i=0; i<N; i++) { + WindowState win = allAppWindows.get(i); + if (win == startingWindow && clientHidden) { + // Don't hide the starting window. + continue; + } + try { + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, + "Setting visibility of " + win + ": " + (!clientHidden)); + win.mClient.dispatchAppVisibility(!clientHidden); + } catch (RemoteException e) { + } + } + } + + void showAllWindowsLocked() { + final int NW = allAppWindows.size(); + for (int i=0; i<NW; i++) { + WindowState w = allAppWindows.get(i); + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, + "performing show on: " + w); + w.performShowLocked(); + } + } + + // This must be called while inside a transaction. + boolean stepAnimationLocked(long currentTime, int dw, int dh) { + if (!service.mDisplayFrozen && service.mPolicy.isScreenOn()) { + // We will run animations as long as the display isn't frozen. + + if (animation == WindowManagerService.sDummyAnimation) { + // This guy is going to animate, but not yet. For now count + // it as not animating for purposes of scheduling transactions; + // when it is really time to animate, this will be set to + // a real animation and the next call will execute normally. + return false; + } + + if ((allDrawn || animating || startingDisplayed) && animation != null) { + if (!animating) { + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Starting animation in " + this + + " @ " + currentTime + ": dw=" + dw + " dh=" + dh + + " scale=" + service.mTransitionAnimationScale + + " allDrawn=" + allDrawn + " animating=" + animating); + animation.initialize(dw, dh, dw, dh); + animation.setStartTime(currentTime); + animating = true; + } + transformation.clear(); + final boolean more = animation.getTransformation( + currentTime, transformation); + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Stepped animation in " + this + + ": more=" + more + ", xform=" + transformation); + if (more) { + // we're done! + hasTransformation = true; + return true; + } + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Finished animation in " + this + + " @ " + currentTime); + animation = null; + } + } else if (animation != null) { + // If the display is frozen, and there is a pending animation, + // clear it and make sure we run the cleanup code. + animating = true; + animation = null; + } + + hasTransformation = false; + + if (!animating) { + return false; + } + + clearAnimation(); + animating = false; + if (animLayerAdjustment != 0) { + animLayerAdjustment = 0; + updateLayers(); + } + if (service.mInputMethodTarget != null && service.mInputMethodTarget.mAppToken == this) { + service.moveInputMethodWindowsIfNeededLocked(true); + } + + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Animation done in " + this + + ": reportedVisible=" + reportedVisible); + + transformation.clear(); + + final int N = windows.size(); + for (int i=0; i<N; i++) { + windows.get(i).finishExit(); + } + updateReportedVisibilityLocked(); + + return false; + } + + void updateReportedVisibilityLocked() { + if (appToken == null) { + return; + } + + int numInteresting = 0; + int numVisible = 0; + boolean nowGone = true; + + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Update reported visibility: " + this); + final int N = allAppWindows.size(); + for (int i=0; i<N; i++) { + WindowState win = allAppWindows.get(i); + if (win == startingWindow || win.mAppFreezing + || win.mViewVisibility != View.VISIBLE + || win.mAttrs.type == TYPE_APPLICATION_STARTING + || win.mDestroying) { + continue; + } + if (WindowManagerService.DEBUG_VISIBILITY) { + Slog.v(WindowManagerService.TAG, "Win " + win + ": isDrawn=" + + win.isDrawnLw() + + ", isAnimating=" + win.isAnimating()); + if (!win.isDrawnLw()) { + Slog.v(WindowManagerService.TAG, "Not displayed: s=" + win.mSurface + + " pv=" + win.mPolicyVisibility + + " dp=" + win.mDrawPending + + " cdp=" + win.mCommitDrawPending + + " ah=" + win.mAttachedHidden + + " th=" + + (win.mAppToken != null + ? win.mAppToken.hiddenRequested : false) + + " a=" + win.mAnimating); + } + } + numInteresting++; + if (win.isDrawnLw()) { + if (!win.isAnimating()) { + numVisible++; + } + nowGone = false; + } else if (win.isAnimating()) { + nowGone = false; + } + } + + boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting; + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "VIS " + this + ": interesting=" + + numInteresting + " visible=" + numVisible); + if (nowVisible != reportedVisible) { + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v( + WindowManagerService.TAG, "Visibility changed in " + this + + ": vis=" + nowVisible); + reportedVisible = nowVisible; + Message m = service.mH.obtainMessage( + H.REPORT_APPLICATION_TOKEN_WINDOWS, + nowVisible ? 1 : 0, + nowGone ? 1 : 0, + this); + service.mH.sendMessage(m); + } + } + + WindowState findMainWindow() { + int j = windows.size(); + while (j > 0) { + j--; + WindowState win = windows.get(j); + if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION + || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) { + return win; + } + } + return null; + } + + void dump(PrintWriter pw, String prefix) { + super.dump(pw, prefix); + if (appToken != null) { + pw.print(prefix); pw.println("app=true"); + } + if (allAppWindows.size() > 0) { + pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows); + } + pw.print(prefix); pw.print("groupId="); pw.print(groupId); + pw.print(" appFullscreen="); pw.print(appFullscreen); + pw.print(" requestedOrientation="); pw.println(requestedOrientation); + pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested); + pw.print(" clientHidden="); pw.print(clientHidden); + pw.print(" willBeHidden="); pw.print(willBeHidden); + pw.print(" reportedVisible="); pw.println(reportedVisible); + if (paused || freezingScreen) { + pw.print(prefix); pw.print("paused="); pw.print(paused); + pw.print(" freezingScreen="); pw.println(freezingScreen); + } + if (numInterestingWindows != 0 || numDrawnWindows != 0 + || inPendingTransaction || allDrawn) { + pw.print(prefix); pw.print("numInterestingWindows="); + pw.print(numInterestingWindows); + pw.print(" numDrawnWindows="); pw.print(numDrawnWindows); + pw.print(" inPendingTransaction="); pw.print(inPendingTransaction); + pw.print(" allDrawn="); pw.println(allDrawn); + } + if (animating || animation != null) { + pw.print(prefix); pw.print("animating="); pw.print(animating); + pw.print(" animation="); pw.println(animation); + } + if (hasTransformation) { + pw.print(prefix); pw.print("XForm: "); + transformation.printShortString(pw); + pw.println(); + } + if (animLayerAdjustment != 0) { + pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment); + } + if (startingData != null || removed || firstWindowDrawn) { + pw.print(prefix); pw.print("startingData="); pw.print(startingData); + pw.print(" removed="); pw.print(removed); + pw.print(" firstWindowDrawn="); pw.println(firstWindowDrawn); + } + if (startingWindow != null || startingView != null + || startingDisplayed || startingMoved) { + pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow); + pw.print(" startingView="); pw.print(startingView); + pw.print(" startingDisplayed="); pw.print(startingDisplayed); + pw.print(" startingMoved"); pw.println(startingMoved); + } + } + + @Override + public String toString() { + if (stringName == null) { + StringBuilder sb = new StringBuilder(); + sb.append("AppWindowToken{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" token="); sb.append(token); sb.append('}'); + stringName = sb.toString(); + } + return stringName; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java new file mode 100644 index 0000000..1fcb869 --- /dev/null +++ b/services/java/com/android/server/wm/DimAnimator.java @@ -0,0 +1,187 @@ +/* + * 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.wm; + +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.util.Slog; +import android.util.TypedValue; +import android.view.Surface; +import android.view.SurfaceSession; + +import java.io.PrintWriter; + +/** + * DimAnimator class that controls the dim animation. This holds the surface and + * all state used for dim animation. + */ +class DimAnimator { + Surface mDimSurface; + boolean mDimShown = false; + float mDimCurrentAlpha; + float mDimTargetAlpha; + float mDimDeltaPerMs; + long mLastDimAnimTime; + + int mLastDimWidth, mLastDimHeight; + + DimAnimator (SurfaceSession session) { + if (mDimSurface == null) { + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + + mDimSurface + ": CREATE"); + try { + mDimSurface = new Surface(session, 0, + "DimSurface", + -1, 16, 16, PixelFormat.OPAQUE, + Surface.FX_SURFACE_DIM); + mDimSurface.setAlpha(0.0f); + } catch (Exception e) { + Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e); + } + } + } + + /** + * Show the dim surface. + */ + void show(int dw, int dh) { + if (!mDimShown) { + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" + + dw + "x" + dh + ")"); + mDimShown = true; + try { + mLastDimWidth = dw; + mLastDimHeight = dh; + mDimSurface.setPosition(0, 0); + mDimSurface.setSize(dw, dh); + mDimSurface.show(); + } catch (RuntimeException e) { + Slog.w(WindowManagerService.TAG, "Failure showing dim surface", e); + } + } else if (mLastDimWidth != dw || mLastDimHeight != dh) { + mLastDimWidth = dw; + mLastDimHeight = dh; + mDimSurface.setSize(dw, dh); + } + } + + /** + * Set's the dim surface's layer and update dim parameters that will be used in + * {@link updateSurface} after all windows are examined. + */ + void updateParameters(Resources res, WindowState w, long currentTime) { + mDimSurface.setLayer(w.mAnimLayer-1); + + final float target = w.mExiting ? 0 : w.mAttrs.dimAmount; + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + + ": layer=" + (w.mAnimLayer-1) + " target=" + target); + if (mDimTargetAlpha != target) { + // If the desired dim level has changed, then + // start an animation to it. + mLastDimAnimTime = currentTime; + long duration = (w.mAnimating && w.mAnimation != null) + ? w.mAnimation.computeDurationHint() + : WindowManagerService.DEFAULT_DIM_DURATION; + if (target > mDimTargetAlpha) { + TypedValue tv = new TypedValue(); + res.getValue(com.android.internal.R.fraction.config_dimBehindFadeDuration, + tv, true); + if (tv.type == TypedValue.TYPE_FRACTION) { + duration = (long)tv.getFraction((float)duration, (float)duration); + } else if (tv.type >= TypedValue.TYPE_FIRST_INT + && tv.type <= TypedValue.TYPE_LAST_INT) { + duration = tv.data; + } + } + if (duration < 1) { + // Don't divide by zero + duration = 1; + } + mDimTargetAlpha = target; + mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha) / duration; + } + } + + /** + * Updating the surface's alpha. Returns true if the animation continues, or returns + * false when the animation is finished and the dim surface is hidden. + */ + boolean updateSurface(boolean dimming, long currentTime, boolean displayFrozen) { + if (!dimming) { + if (mDimTargetAlpha != 0) { + mLastDimAnimTime = currentTime; + mDimTargetAlpha = 0; + mDimDeltaPerMs = (-mDimCurrentAlpha) / WindowManagerService.DEFAULT_DIM_DURATION; + } + } + + boolean animating = false; + if (mLastDimAnimTime != 0) { + mDimCurrentAlpha += mDimDeltaPerMs + * (currentTime-mLastDimAnimTime); + boolean more = true; + if (displayFrozen) { + // If the display is frozen, there is no reason to animate. + more = false; + } else if (mDimDeltaPerMs > 0) { + if (mDimCurrentAlpha > mDimTargetAlpha) { + more = false; + } + } else if (mDimDeltaPerMs < 0) { + if (mDimCurrentAlpha < mDimTargetAlpha) { + more = false; + } + } else { + more = false; + } + + // Do we need to continue animating? + if (more) { + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + + mDimSurface + ": alpha=" + mDimCurrentAlpha); + mLastDimAnimTime = currentTime; + mDimSurface.setAlpha(mDimCurrentAlpha); + animating = true; + } else { + mDimCurrentAlpha = mDimTargetAlpha; + mLastDimAnimTime = 0; + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + + mDimSurface + ": final alpha=" + mDimCurrentAlpha); + mDimSurface.setAlpha(mDimCurrentAlpha); + if (!dimming) { + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + + ": HIDE"); + try { + mDimSurface.hide(); + } catch (RuntimeException e) { + Slog.w(WindowManagerService.TAG, "Illegal argument exception hiding dim surface"); + } + mDimShown = false; + } + } + } + return animating; + } + + public void printTo(PrintWriter pw) { + pw.print(" mDimShown="); pw.print(mDimShown); + pw.print(" current="); pw.print(mDimCurrentAlpha); + pw.print(" target="); pw.print(mDimTargetAlpha); + pw.print(" delta="); pw.print(mDimDeltaPerMs); + pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime); + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java new file mode 100644 index 0000000..c8f8ff3 --- /dev/null +++ b/services/java/com/android/server/wm/DragState.java @@ -0,0 +1,368 @@ +/* + * 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.wm; + +import com.android.server.wm.WindowManagerService.H; + +import android.content.ClipData; +import android.content.ClipDescription; +import android.graphics.Region; +import android.os.IBinder; +import android.os.Message; +import android.os.Process; +import android.os.RemoteException; +import android.util.Slog; +import android.view.DragEvent; +import android.view.InputChannel; +import android.view.InputQueue; +import android.view.Surface; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowManagerPolicy; + +import java.util.ArrayList; + +/** + * Drag/drop state + */ +class DragState { + final WindowManagerService mService; + IBinder mToken; + Surface mSurface; + int mFlags; + IBinder mLocalWin; + ClipData mData; + ClipDescription mDataDescription; + boolean mDragResult; + float mCurrentX, mCurrentY; + float mThumbOffsetX, mThumbOffsetY; + InputChannel mServerChannel, mClientChannel; + WindowState mTargetWindow; + ArrayList<WindowState> mNotifiedWindows; + boolean mDragInProgress; + + private final Region mTmpRegion = new Region(); + + DragState(WindowManagerService service, IBinder token, Surface surface, + int flags, IBinder localWin) { + mService = service; + mToken = token; + mSurface = surface; + mFlags = flags; + mLocalWin = localWin; + mNotifiedWindows = new ArrayList<WindowState>(); + } + + void reset() { + if (mSurface != null) { + mSurface.destroy(); + } + mSurface = null; + mFlags = 0; + mLocalWin = null; + mToken = null; + mData = null; + mThumbOffsetX = mThumbOffsetY = 0; + mNotifiedWindows = null; + } + + void register() { + if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel"); + if (mClientChannel != null) { + Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel"); + } else { + InputChannel[] channels = InputChannel.openInputChannelPair("drag"); + mServerChannel = channels[0]; + mClientChannel = channels[1]; + mService.mInputManager.registerInputChannel(mServerChannel, null); + InputQueue.registerInputChannel(mClientChannel, mService.mDragInputHandler, + mService.mH.getLooper().getQueue()); + } + } + + void unregister() { + if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "unregistering drag input channel"); + if (mClientChannel == null) { + Slog.e(WindowManagerService.TAG, "Unregister of nonexistent drag input channel"); + } else { + mService.mInputManager.unregisterInputChannel(mServerChannel); + InputQueue.unregisterInputChannel(mClientChannel); + mClientChannel.dispose(); + mServerChannel.dispose(); + mClientChannel = null; + mServerChannel = null; + } + } + + int getDragLayerLw() { + return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG) + * WindowManagerService.TYPE_LAYER_MULTIPLIER + + WindowManagerService.TYPE_LAYER_OFFSET; + } + + /* call out to each visible window/session informing it about the drag + */ + void broadcastDragStartedLw(final float touchX, final float touchY) { + // Cache a base-class instance of the clip metadata so that parceling + // works correctly in calling out to the apps. + mDataDescription = (mData != null) ? mData.getDescription() : null; + mNotifiedWindows.clear(); + mDragInProgress = true; + + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")"); + } + + final int N = mService.mWindows.size(); + for (int i = 0; i < N; i++) { + sendDragStartedLw(mService.mWindows.get(i), touchX, touchY, mDataDescription); + } + } + + /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the + * designated window is potentially a drop recipient. There are race situations + * around DRAG_ENDED broadcast, so we make sure that once we've declared that + * the drag has ended, we never send out another DRAG_STARTED for this drag action. + * + * This method clones the 'event' parameter if it's being delivered to the same + * process, so it's safe for the caller to call recycle() on the event afterwards. + */ + private void sendDragStartedLw(WindowState newWin, float touchX, float touchY, + ClipDescription desc) { + // Don't actually send the event if the drag is supposed to be pinned + // to the originating window but 'newWin' is not that window. + if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) { + final IBinder winBinder = newWin.mClient.asBinder(); + if (winBinder != mLocalWin) { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "Not dispatching local DRAG_STARTED to " + newWin); + } + return; + } + } + + if (mDragInProgress && newWin.isPotentialDragTarget()) { + DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, + touchX - newWin.mFrame.left, touchY - newWin.mFrame.top, + null, desc, null, false); + try { + newWin.mClient.dispatchDragEvent(event); + // track each window that we've notified that the drag is starting + mNotifiedWindows.add(newWin); + } catch (RemoteException e) { + Slog.w(WindowManagerService.TAG, "Unable to drag-start window " + newWin); + } finally { + // if the callee was local, the dispatch has already recycled the event + if (Process.myPid() != newWin.mSession.mPid) { + event.recycle(); + } + } + } + } + + /* helper - construct and send a DRAG_STARTED event only if the window has not + * previously been notified, i.e. it became visible after the drag operation + * was begun. This is a rare case. + */ + void sendDragStartedIfNeededLw(WindowState newWin) { + if (mDragInProgress) { + // If we have sent the drag-started, we needn't do so again + for (WindowState ws : mNotifiedWindows) { + if (ws == newWin) { + return; + } + } + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "need to send DRAG_STARTED to new window " + newWin); + } + sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription); + } + } + + void broadcastDragEndedLw() { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED"); + } + DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, + 0, 0, null, null, null, mDragResult); + for (WindowState ws: mNotifiedWindows) { + try { + ws.mClient.dispatchDragEvent(evt); + } catch (RemoteException e) { + Slog.w(WindowManagerService.TAG, "Unable to drag-end window " + ws); + } + } + mNotifiedWindows.clear(); + mDragInProgress = false; + evt.recycle(); + } + + void endDragLw() { + mService.mDragState.broadcastDragEndedLw(); + + // stop intercepting input + mService.mDragState.unregister(); + mService.mInputMonitor.updateInputWindowsLw(true /*force*/); + + // free our resources and drop all the object references + mService.mDragState.reset(); + mService.mDragState = null; + + if (WindowManagerService.DEBUG_ORIENTATION) Slog.d(WindowManagerService.TAG, "Performing post-drag rotation"); + boolean changed = mService.setRotationUncheckedLocked( + WindowManagerPolicy.USE_LAST_ROTATION, 0, false); + if (changed) { + mService.mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); + } + } + + void notifyMoveLw(float x, float y) { + final int myPid = Process.myPid(); + + // Move the surface to the given touch + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw"); + Surface.openTransaction(); + try { + mSurface.setPosition((int)(x - mThumbOffsetX), (int)(y - mThumbOffsetY)); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DRAG " + + mSurface + ": pos=(" + + (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")"); + } finally { + Surface.closeTransaction(); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION notifyMoveLw"); + } + + // Tell the affected window + WindowState touchedWin = getTouchedWinAtPointLw(x, y); + if (touchedWin == null) { + if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "No touched win at x=" + x + " y=" + y); + return; + } + if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) { + final IBinder touchedBinder = touchedWin.mClient.asBinder(); + if (touchedBinder != mLocalWin) { + // This drag is pinned only to the originating window, but the drag + // point is outside that window. Pretend it's over empty space. + touchedWin = null; + } + } + try { + // have we dragged over a new window? + if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "sending DRAG_EXITED to " + mTargetWindow); + } + // force DRAG_EXITED_EVENT if appropriate + DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED, + x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top, + null, null, null, false); + mTargetWindow.mClient.dispatchDragEvent(evt); + if (myPid != mTargetWindow.mSession.mPid) { + evt.recycle(); + } + } + if (touchedWin != null) { + if (false && WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "sending DRAG_LOCATION to " + touchedWin); + } + DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION, + x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, + null, null, null, false); + touchedWin.mClient.dispatchDragEvent(evt); + if (myPid != touchedWin.mSession.mPid) { + evt.recycle(); + } + } + } catch (RemoteException e) { + Slog.w(WindowManagerService.TAG, "can't send drag notification to windows"); + } + mTargetWindow = touchedWin; + } + + // Tell the drop target about the data. Returns 'true' if we can immediately + // dispatch the global drag-ended message, 'false' if we need to wait for a + // result from the recipient. + boolean notifyDropLw(float x, float y) { + WindowState touchedWin = getTouchedWinAtPointLw(x, y); + if (touchedWin == null) { + // "drop" outside a valid window -- no recipient to apply a + // timeout to, and we can send the drag-ended message immediately. + mDragResult = false; + return true; + } + + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "sending DROP to " + touchedWin); + } + final int myPid = Process.myPid(); + final IBinder token = touchedWin.mClient.asBinder(); + DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP, + x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, + null, null, mData, false); + try { + touchedWin.mClient.dispatchDragEvent(evt); + + // 5 second timeout for this window to respond to the drop + mService.mH.removeMessages(H.DRAG_END_TIMEOUT, token); + Message msg = mService.mH.obtainMessage(H.DRAG_END_TIMEOUT, token); + mService.mH.sendMessageDelayed(msg, 5000); + } catch (RemoteException e) { + Slog.w(WindowManagerService.TAG, "can't send drop notification to win " + touchedWin); + return true; + } finally { + if (myPid != touchedWin.mSession.mPid) { + evt.recycle(); + } + } + mToken = token; + return false; + } + + // Find the visible, touch-deliverable window under the given point + private WindowState getTouchedWinAtPointLw(float xf, float yf) { + WindowState touchedWin = null; + final int x = (int) xf; + final int y = (int) yf; + final ArrayList<WindowState> windows = mService.mWindows; + final int N = windows.size(); + for (int i = N - 1; i >= 0; i--) { + WindowState child = windows.get(i); + final int flags = child.mAttrs.flags; + if (!child.isVisibleLw()) { + // not visible == don't tell about drags + continue; + } + if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { + // not touchable == don't tell about drags + continue; + } + + child.getTouchableRegion(mTmpRegion); + + final int touchFlags = flags & + (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); + if (mTmpRegion.contains(x, y) || touchFlags == 0) { + // Found it + touchedWin = child; + break; + } + } + + return touchedWin; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/FadeInOutAnimation.java b/services/java/com/android/server/wm/FadeInOutAnimation.java new file mode 100644 index 0000000..06f7657 --- /dev/null +++ b/services/java/com/android/server/wm/FadeInOutAnimation.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.wm; + +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +/** + * Animation that fade in after 0.5 interpolate time, or fade out in reverse order. + * This is used for opening/closing transition for apps in compatible mode. + */ +class FadeInOutAnimation extends Animation { + boolean mFadeIn; + + public FadeInOutAnimation(boolean fadeIn) { + setInterpolator(new AccelerateInterpolator()); + setDuration(WindowManagerService.DEFAULT_FADE_IN_OUT_DURATION); + mFadeIn = fadeIn; + } + + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + float x = interpolatedTime; + if (!mFadeIn) { + x = 1.0f - x; // reverse the interpolation for fade out + } + t.setAlpha(x); + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/InputApplication.java b/services/java/com/android/server/wm/InputApplication.java index ae09484..e04fd31 100644 --- a/services/java/com/android/server/InputApplication.java +++ b/services/java/com/android/server/wm/InputApplication.java @@ -14,7 +14,8 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; + /** * Describes input-related application properties for use by the input dispatcher. diff --git a/services/java/com/android/server/InputApplicationHandle.java b/services/java/com/android/server/wm/InputApplicationHandle.java index d396d11..64c8e7e 100644 --- a/services/java/com/android/server/InputApplicationHandle.java +++ b/services/java/com/android/server/wm/InputApplicationHandle.java @@ -14,7 +14,8 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; + /** * Functions as a handle for an application that can receive input. @@ -29,11 +30,11 @@ public final class InputApplicationHandle { private int ptr; // The window manager's application window token. - public final WindowManagerService.AppWindowToken appWindowToken; + public final AppWindowToken appWindowToken; private native void nativeDispose(); - public InputApplicationHandle(WindowManagerService.AppWindowToken appWindowToken) { + public InputApplicationHandle(AppWindowToken appWindowToken) { this.appWindowToken = appWindowToken; } diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/wm/InputManager.java index 8d249ff..80a2a96 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/wm/InputManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; import com.android.internal.util.XmlUtils; diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java new file mode 100644 index 0000000..b1833c4 --- /dev/null +++ b/services/java/com/android/server/wm/InputMonitor.java @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2010 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.wm; + +import android.graphics.Rect; +import android.os.Process; +import android.os.RemoteException; +import android.util.Log; +import android.util.Slog; +import android.view.KeyEvent; +import android.view.WindowManager; + +import java.util.ArrayList; + +final class InputMonitor { + private final WindowManagerService mService; + + // Current window with input focus for keys and other non-touch events. May be null. + private WindowState mInputFocus; + + // When true, prevents input dispatch from proceeding until set to false again. + private boolean mInputDispatchFrozen; + + // When true, input dispatch proceeds normally. Otherwise all events are dropped. + private boolean mInputDispatchEnabled = true; + + // When true, need to call updateInputWindowsLw(). + private boolean mUpdateInputWindowsNeeded = true; + + // Temporary list of windows information to provide to the input dispatcher. + private InputWindowList mTempInputWindows = new InputWindowList(); + + // Temporary input application object to provide to the input dispatcher. + private InputApplication mTempInputApplication = new InputApplication(); + + // Set to true when the first input device configuration change notification + // is received to indicate that the input devices are ready. + private final Object mInputDevicesReadyMonitor = new Object(); + private boolean mInputDevicesReady; + + public InputMonitor(WindowManagerService service) { + mService = service; + } + + /* Notifies the window manager about a broken input channel. + * + * Called by the InputManager. + */ + public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) { + if (inputWindowHandle == null) { + return; + } + + synchronized (mService.mWindowMap) { + WindowState windowState = (WindowState) inputWindowHandle.windowState; + Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState); + mService.removeWindowLocked(windowState.mSession, windowState); + } + } + + /* Notifies the window manager about an application that is not responding. + * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. + * + * Called by the InputManager. + */ + public long notifyANR(InputApplicationHandle inputApplicationHandle, + InputWindowHandle inputWindowHandle) { + AppWindowToken appWindowToken = null; + if (inputWindowHandle != null) { + synchronized (mService.mWindowMap) { + WindowState windowState = (WindowState) inputWindowHandle.windowState; + if (windowState != null) { + Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to " + + windowState.mAttrs.getTitle()); + appWindowToken = windowState.mAppToken; + } + } + } + + if (appWindowToken == null && inputApplicationHandle != null) { + appWindowToken = inputApplicationHandle.appWindowToken; + Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to application " + + appWindowToken.stringName); + } + + if (appWindowToken != null && appWindowToken.appToken != null) { + try { + // Notify the activity manager about the timeout and let it decide whether + // to abort dispatching or keep waiting. + boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(); + if (! abort) { + // The activity manager declined to abort dispatching. + // Wait a bit longer and timeout again later. + return appWindowToken.inputDispatchingTimeoutNanos; + } + } catch (RemoteException ex) { + } + } + return 0; // abort dispatching + } + + private void addDragInputWindowLw(InputWindowList windowList) { + final InputWindow inputWindow = windowList.add(); + inputWindow.inputChannel = mService.mDragState.mServerChannel; + inputWindow.name = "drag"; + inputWindow.layoutParamsFlags = 0; + inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; + inputWindow.dispatchingTimeoutNanos = WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + inputWindow.visible = true; + inputWindow.canReceiveKeys = false; + inputWindow.hasFocus = true; + inputWindow.hasWallpaper = false; + inputWindow.paused = false; + inputWindow.layer = mService.mDragState.getDragLayerLw(); + inputWindow.ownerPid = Process.myPid(); + inputWindow.ownerUid = Process.myUid(); + + // The drag window covers the entire display + inputWindow.frameLeft = 0; + inputWindow.frameTop = 0; + inputWindow.frameRight = mService.mDisplay.getWidth(); + inputWindow.frameBottom = mService.mDisplay.getHeight(); + + // The drag window cannot receive new touches. + inputWindow.touchableRegion.setEmpty(); + } + + public void setUpdateInputWindowsNeededLw() { + mUpdateInputWindowsNeeded = true; + } + + /* Updates the cached window information provided to the input dispatcher. */ + public void updateInputWindowsLw(boolean force) { + if (!force && !mUpdateInputWindowsNeeded) { + return; + } + mUpdateInputWindowsNeeded = false; + + // Populate the input window list with information about all of the windows that + // could potentially receive input. + // As an optimization, we could try to prune the list of windows but this turns + // out to be difficult because only the native code knows for sure which window + // currently has touch focus. + final ArrayList<WindowState> windows = mService.mWindows; + + // If there's a drag in flight, provide a pseudowindow to catch drag input + final boolean inDrag = (mService.mDragState != null); + if (inDrag) { + if (WindowManagerService.DEBUG_DRAG) { + Log.d(WindowManagerService.TAG, "Inserting drag window"); + } + addDragInputWindowLw(mTempInputWindows); + } + + final int N = windows.size(); + for (int i = N - 1; i >= 0; i--) { + final WindowState child = windows.get(i); + if (child.mInputChannel == null || child.mRemoved) { + // Skip this window because it cannot possibly receive input. + continue; + } + + final int flags = child.mAttrs.flags; + final int type = child.mAttrs.type; + + final boolean hasFocus = (child == mInputFocus); + final boolean isVisible = child.isVisibleLw(); + final boolean hasWallpaper = (child == mService.mWallpaperTarget) + && (type != WindowManager.LayoutParams.TYPE_KEYGUARD); + + // If there's a drag in progress and 'child' is a potential drop target, + // make sure it's been told about the drag + if (inDrag && isVisible) { + mService.mDragState.sendDragStartedIfNeededLw(child); + } + + // Add a window to our list of input windows. + final InputWindow inputWindow = mTempInputWindows.add(); + inputWindow.inputWindowHandle = child.mInputWindowHandle; + inputWindow.inputChannel = child.mInputChannel; + inputWindow.name = child.toString(); + inputWindow.layoutParamsFlags = flags; + inputWindow.layoutParamsType = type; + inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); + inputWindow.visible = isVisible; + inputWindow.canReceiveKeys = child.canReceiveKeys(); + inputWindow.hasFocus = hasFocus; + inputWindow.hasWallpaper = hasWallpaper; + inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false; + inputWindow.layer = child.mLayer; + inputWindow.ownerPid = child.mSession.mPid; + inputWindow.ownerUid = child.mSession.mUid; + + final Rect frame = child.mFrame; + inputWindow.frameLeft = frame.left; + inputWindow.frameTop = frame.top; + inputWindow.frameRight = frame.right; + inputWindow.frameBottom = frame.bottom; + + child.getTouchableRegion(inputWindow.touchableRegion); + } + + // Send windows to native code. + mService.mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray()); + + // Clear the list in preparation for the next round. + // Also avoids keeping InputChannel objects referenced unnecessarily. + mTempInputWindows.clear(); + } + + /* Notifies that the input device configuration has changed. */ + public void notifyConfigurationChanged() { + mService.sendNewConfiguration(); + + synchronized (mInputDevicesReadyMonitor) { + if (!mInputDevicesReady) { + mInputDevicesReady = true; + mInputDevicesReadyMonitor.notifyAll(); + } + } + } + + /* Waits until the built-in input devices have been configured. */ + public boolean waitForInputDevicesReady(long timeoutMillis) { + synchronized (mInputDevicesReadyMonitor) { + if (!mInputDevicesReady) { + try { + mInputDevicesReadyMonitor.wait(timeoutMillis); + } catch (InterruptedException ex) { + } + } + return mInputDevicesReady; + } + } + + /* Notifies that the lid switch changed state. */ + public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { + mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen); + } + + /* Provides an opportunity for the window manager policy to intercept early key + * processing as soon as the key has been read from the device. */ + public int interceptKeyBeforeQueueing( + KeyEvent event, int policyFlags, boolean isScreenOn) { + return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn); + } + + /* Provides an opportunity for the window manager policy to process a key before + * ordinary dispatch. */ + public boolean interceptKeyBeforeDispatching( + InputWindowHandle focus, KeyEvent event, int policyFlags) { + WindowState windowState = focus != null ? (WindowState) focus.windowState : null; + return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags); + } + + /* Provides an opportunity for the window manager policy to process a key that + * the application did not handle. */ + public KeyEvent dispatchUnhandledKey( + InputWindowHandle focus, KeyEvent event, int policyFlags) { + WindowState windowState = focus != null ? (WindowState) focus.windowState : null; + return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags); + } + + /* Called when the current input focus changes. + * Layer assignment is assumed to be complete by the time this is called. + */ + public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow); + } + + if (newWindow != mInputFocus) { + if (newWindow != null && newWindow.canReceiveKeys()) { + // Displaying a window implicitly causes dispatching to be unpaused. + // This is to protect against bugs if someone pauses dispatching but + // forgets to resume. + newWindow.mToken.paused = false; + } + + mInputFocus = newWindow; + setUpdateInputWindowsNeededLw(); + + if (updateInputWindows) { + updateInputWindowsLw(false /*force*/); + } + } + } + + public void setFocusedAppLw(AppWindowToken newApp) { + // Focused app has changed. + if (newApp == null) { + mService.mInputManager.setFocusedApplication(null); + } else { + mTempInputApplication.inputApplicationHandle = newApp.mInputApplicationHandle; + mTempInputApplication.name = newApp.toString(); + mTempInputApplication.dispatchingTimeoutNanos = + newApp.inputDispatchingTimeoutNanos; + + mService.mInputManager.setFocusedApplication(mTempInputApplication); + + mTempInputApplication.recycle(); + } + } + + public void pauseDispatchingLw(WindowToken window) { + if (! window.paused) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window); + } + + window.paused = true; + updateInputWindowsLw(true /*force*/); + } + } + + public void resumeDispatchingLw(WindowToken window) { + if (window.paused) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window); + } + + window.paused = false; + updateInputWindowsLw(true /*force*/); + } + } + + public void freezeInputDispatchingLw() { + if (! mInputDispatchFrozen) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.v(WindowManagerService.TAG, "Freezing input dispatching"); + } + + mInputDispatchFrozen = true; + updateInputDispatchModeLw(); + } + } + + public void thawInputDispatchingLw() { + if (mInputDispatchFrozen) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.v(WindowManagerService.TAG, "Thawing input dispatching"); + } + + mInputDispatchFrozen = false; + updateInputDispatchModeLw(); + } + } + + public void setEventDispatchingLw(boolean enabled) { + if (mInputDispatchEnabled != enabled) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled); + } + + mInputDispatchEnabled = enabled; + updateInputDispatchModeLw(); + } + } + + private void updateInputDispatchModeLw() { + mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen); + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/wm/InputWindow.java index 2c2cdfe..e3eb473 100644 --- a/services/java/com/android/server/InputWindow.java +++ b/services/java/com/android/server/wm/InputWindow.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; import android.graphics.Region; import android.view.InputChannel; diff --git a/services/java/com/android/server/InputWindowHandle.java b/services/java/com/android/server/wm/InputWindowHandle.java index 4b92939..cc508c6 100644 --- a/services/java/com/android/server/InputWindowHandle.java +++ b/services/java/com/android/server/wm/InputWindowHandle.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; import android.view.WindowManagerPolicy; diff --git a/services/java/com/android/server/InputWindowList.java b/services/java/com/android/server/wm/InputWindowList.java index 1cbb2cc..6077337 100644 --- a/services/java/com/android/server/InputWindowList.java +++ b/services/java/com/android/server/wm/InputWindowList.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; /** diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java index ef00b08..4356ce5 100644 --- a/services/java/com/android/server/ScreenRotationAnimation.java +++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; // TODO: use com.android.server.wm, once things move there +package com.android.server.wm; import android.content.Context; import android.graphics.Bitmap; diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java new file mode 100644 index 0000000..b9db177 --- /dev/null +++ b/services/java/com/android/server/wm/Session.java @@ -0,0 +1,419 @@ +/* + * 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.wm; + +import com.android.internal.view.IInputContext; +import com.android.internal.view.IInputMethodClient; +import com.android.internal.view.IInputMethodManager; +import com.android.server.wm.WindowManagerService.H; + +import android.content.ClipData; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; +import android.view.IWindow; +import android.view.IWindowSession; +import android.view.InputChannel; +import android.view.Surface; +import android.view.SurfaceSession; +import android.view.WindowManager; + +import java.io.PrintWriter; + +/** + * This class represents an active client session. There is generally one + * Session object per process that is interacting with the window manager. + */ +final class Session extends IWindowSession.Stub + implements IBinder.DeathRecipient { + final WindowManagerService mService; + final IInputMethodClient mClient; + final IInputContext mInputContext; + final int mUid; + final int mPid; + final String mStringName; + SurfaceSession mSurfaceSession; + int mNumWindow = 0; + boolean mClientDead = false; + + public Session(WindowManagerService service, IInputMethodClient client, + IInputContext inputContext) { + mService = service; + mClient = client; + mInputContext = inputContext; + mUid = Binder.getCallingUid(); + mPid = Binder.getCallingPid(); + StringBuilder sb = new StringBuilder(); + sb.append("Session{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" uid "); + sb.append(mUid); + sb.append("}"); + mStringName = sb.toString(); + + synchronized (mService.mWindowMap) { + if (mService.mInputMethodManager == null && mService.mHaveInputMethods) { + IBinder b = ServiceManager.getService( + Context.INPUT_METHOD_SERVICE); + mService.mInputMethodManager = IInputMethodManager.Stub.asInterface(b); + } + } + long ident = Binder.clearCallingIdentity(); + try { + // Note: it is safe to call in to the input method manager + // here because we are not holding our lock. + if (mService.mInputMethodManager != null) { + mService.mInputMethodManager.addClient(client, inputContext, + mUid, mPid); + } else { + client.setUsingInputMethod(false); + } + client.asBinder().linkToDeath(this, 0); + } catch (RemoteException e) { + // The caller has died, so we can just forget about this. + try { + if (mService.mInputMethodManager != null) { + mService.mInputMethodManager.removeClient(client); + } + } catch (RemoteException ee) { + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (RuntimeException e) { + // Log all 'real' exceptions thrown to the caller + if (!(e instanceof SecurityException)) { + Slog.e(WindowManagerService.TAG, "Window Session Crash", e); + } + throw e; + } + } + + public void binderDied() { + // Note: it is safe to call in to the input method manager + // here because we are not holding our lock. + try { + if (mService.mInputMethodManager != null) { + mService.mInputMethodManager.removeClient(mClient); + } + } catch (RemoteException e) { + } + synchronized(mService.mWindowMap) { + mClient.asBinder().unlinkToDeath(this, 0); + mClientDead = true; + killSessionLocked(); + } + } + + public int add(IWindow window, WindowManager.LayoutParams attrs, + int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) { + return mService.addWindow(this, window, attrs, viewVisibility, outContentInsets, + outInputChannel); + } + + public int addWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs, + int viewVisibility, Rect outContentInsets) { + return mService.addWindow(this, window, attrs, viewVisibility, outContentInsets, null); + } + + public void remove(IWindow window) { + mService.removeWindow(this, window); + } + + public int relayout(IWindow window, WindowManager.LayoutParams attrs, + int requestedWidth, int requestedHeight, int viewFlags, + boolean insetsPending, Rect outFrame, Rect outContentInsets, + Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { + //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); + int res = mService.relayoutWindow(this, window, attrs, + requestedWidth, requestedHeight, viewFlags, insetsPending, + outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface); + //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); + return res; + } + + public void setTransparentRegion(IWindow window, Region region) { + mService.setTransparentRegionWindow(this, window, region); + } + + public void setInsets(IWindow window, int touchableInsets, + Rect contentInsets, Rect visibleInsets, Region touchableArea) { + mService.setInsetsWindow(this, window, touchableInsets, contentInsets, + visibleInsets, touchableArea); + } + + public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { + mService.getWindowDisplayFrame(this, window, outDisplayFrame); + } + + public void finishDrawing(IWindow window) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "IWindow finishDrawing called for " + window); + mService.finishDrawingWindow(this, window); + } + + public void setInTouchMode(boolean mode) { + synchronized(mService.mWindowMap) { + mService.mInTouchMode = mode; + } + } + + public boolean getInTouchMode() { + synchronized(mService.mWindowMap) { + return mService.mInTouchMode; + } + } + + public boolean performHapticFeedback(IWindow window, int effectId, + boolean always) { + synchronized(mService.mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + return mService.mPolicy.performHapticFeedbackLw( + mService.windowForClientLocked(this, window, true), + effectId, always); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + /* Drag/drop */ + public IBinder prepareDrag(IWindow window, int flags, + int width, int height, Surface outSurface) { + return mService.prepareDragSurface(window, mSurfaceSession, flags, + width, height, outSurface); + } + + public boolean performDrag(IWindow window, IBinder dragToken, + float touchX, float touchY, float thumbCenterX, float thumbCenterY, + ClipData data) { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "perform drag: win=" + window + " data=" + data); + } + + synchronized (mService.mWindowMap) { + if (mService.mDragState == null) { + Slog.w(WindowManagerService.TAG, "No drag prepared"); + throw new IllegalStateException("performDrag() without prepareDrag()"); + } + + if (dragToken != mService.mDragState.mToken) { + Slog.w(WindowManagerService.TAG, "Performing mismatched drag"); + throw new IllegalStateException("performDrag() does not match prepareDrag()"); + } + + WindowState callingWin = mService.windowForClientLocked(null, window, false); + if (callingWin == null) { + Slog.w(WindowManagerService.TAG, "Bad requesting window " + window); + return false; // !!! TODO: throw here? + } + + // !!! TODO: if input is not still focused on the initiating window, fail + // the drag initiation (e.g. an alarm window popped up just as the application + // called performDrag() + + mService.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder()); + + // !!! TODO: extract the current touch (x, y) in screen coordinates. That + // will let us eliminate the (touchX,touchY) parameters from the API. + + // !!! FIXME: put all this heavy stuff onto the mH looper, as well as + // the actual drag event dispatch stuff in the dragstate + + mService.mDragState.register(); + mService.mInputMonitor.updateInputWindowsLw(true /*force*/); + if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel, + mService.mDragState.mServerChannel)) { + Slog.e(WindowManagerService.TAG, "Unable to transfer touch focus"); + mService.mDragState.unregister(); + mService.mDragState = null; + mService.mInputMonitor.updateInputWindowsLw(true /*force*/); + return false; + } + + mService.mDragState.mData = data; + mService.mDragState.mCurrentX = touchX; + mService.mDragState.mCurrentY = touchY; + mService.mDragState.broadcastDragStartedLw(touchX, touchY); + + // remember the thumb offsets for later + mService.mDragState.mThumbOffsetX = thumbCenterX; + mService.mDragState.mThumbOffsetY = thumbCenterY; + + // Make the surface visible at the proper location + final Surface surface = mService.mDragState.mSurface; + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag"); + Surface.openTransaction(); + try { + surface.setPosition((int)(touchX - thumbCenterX), + (int)(touchY - thumbCenterY)); + surface.setAlpha(.7071f); + surface.setLayer(mService.mDragState.getDragLayerLw()); + surface.show(); + } finally { + Surface.closeTransaction(); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION performDrag"); + } + } + + return true; // success! + } + + public void reportDropResult(IWindow window, boolean consumed) { + IBinder token = window.asBinder(); + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "Drop result=" + consumed + " reported by " + token); + } + + synchronized (mService.mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + if (mService.mDragState == null || mService.mDragState.mToken != token) { + Slog.w(WindowManagerService.TAG, "Invalid drop-result claim by " + window); + throw new IllegalStateException("reportDropResult() by non-recipient"); + } + + // The right window has responded, even if it's no longer around, + // so be sure to halt the timeout even if the later WindowState + // lookup fails. + mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder()); + WindowState callingWin = mService.windowForClientLocked(null, window, false); + if (callingWin == null) { + Slog.w(WindowManagerService.TAG, "Bad result-reporting window " + window); + return; // !!! TODO: throw here? + } + + mService.mDragState.mDragResult = consumed; + mService.mDragState.endDragLw(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public void dragRecipientEntered(IWindow window) { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "Drag into new candidate view @ " + window.asBinder()); + } + } + + public void dragRecipientExited(IWindow window) { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "Drag from old candidate view @ " + window.asBinder()); + } + } + + public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) { + synchronized(mService.mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + mService.setWindowWallpaperPositionLocked( + mService.windowForClientLocked(this, window, true), + x, y, xStep, yStep); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public void wallpaperOffsetsComplete(IBinder window) { + mService.wallpaperOffsetsComplete(window); + } + + public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, + int z, Bundle extras, boolean sync) { + synchronized(mService.mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + return mService.sendWindowWallpaperCommandLocked( + mService.windowForClientLocked(this, window, true), + action, x, y, z, extras, sync); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public void wallpaperCommandComplete(IBinder window, Bundle result) { + mService.wallpaperCommandComplete(window, result); + } + + void windowAddedLocked() { + if (mSurfaceSession == null) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession"); + mSurfaceSession = new SurfaceSession(); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i( + WindowManagerService.TAG, " NEW SURFACE SESSION " + mSurfaceSession); + mService.mSessions.add(this); + } + mNumWindow++; + } + + void windowRemovedLocked() { + mNumWindow--; + killSessionLocked(); + } + + void killSessionLocked() { + if (mNumWindow <= 0 && mClientDead) { + mService.mSessions.remove(this); + if (mSurfaceSession != null) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Last window removed from " + this + + ", destroying " + mSurfaceSession); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i( + WindowManagerService.TAG, " KILL SURFACE SESSION " + mSurfaceSession); + try { + mSurfaceSession.kill(); + } catch (Exception e) { + Slog.w(WindowManagerService.TAG, "Exception thrown when killing surface session " + + mSurfaceSession + " in session " + this + + ": " + e.toString()); + } + mSurfaceSession = null; + } + } + } + + void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow); + pw.print(" mClientDead="); pw.print(mClientDead); + pw.print(" mSurfaceSession="); pw.println(mSurfaceSession); + } + + @Override + public String toString() { + return mStringName; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/StartingData.java b/services/java/com/android/server/wm/StartingData.java new file mode 100644 index 0000000..625fcfe --- /dev/null +++ b/services/java/com/android/server/wm/StartingData.java @@ -0,0 +1,36 @@ +/* + * 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.wm; + +final class StartingData { + final String pkg; + final int theme; + final CharSequence nonLocalizedLabel; + final int labelRes; + final int icon; + final int windowFlags; + + StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel, + int _labelRes, int _icon, int _windowFlags) { + pkg = _pkg; + theme = _theme; + nonLocalizedLabel = _nonLocalizedLabel; + labelRes = _labelRes; + icon = _icon; + windowFlags = _windowFlags; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/StrictModeFlash.java b/services/java/com/android/server/wm/StrictModeFlash.java index 0a6c625..2c62080 100644 --- a/services/java/com/android/server/StrictModeFlash.java +++ b/services/java/com/android/server/wm/StrictModeFlash.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.android.server; // TODO: use com.android.server.wm, once things move there +package com.android.server.wm; + import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PixelFormat; -import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; import android.util.DisplayMetrics; diff --git a/services/java/com/android/server/ViewServer.java b/services/java/com/android/server/wm/ViewServer.java index 7b5d18a..cebd5e7 100644 --- a/services/java/com/android/server/ViewServer.java +++ b/services/java/com/android/server/wm/ViewServer.java @@ -14,7 +14,8 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; + import android.util.Slog; @@ -33,7 +34,7 @@ import java.io.OutputStreamWriter; /** * The ViewServer is local socket server that can be used to communicate with the * views of the opened windows. Communication with the views is ensured by the - * {@link com.android.server.WindowManagerService} and is a cross-process operation. + * {@link com.android.server.wm.WindowManagerService} and is a cross-process operation. * * {@hide} */ diff --git a/services/java/com/android/server/wm/Watermark.java b/services/java/com/android/server/wm/Watermark.java new file mode 100644 index 0000000..22126f3 --- /dev/null +++ b/services/java/com/android/server/wm/Watermark.java @@ -0,0 +1,177 @@ +/* + * 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.wm; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.graphics.Paint.FontMetricsInt; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.TypedValue; +import android.view.Display; +import android.view.Surface; +import android.view.SurfaceSession; +import android.view.Surface.OutOfResourcesException; + +/** + * Displays a watermark on top of the window manager's windows. + */ +class Watermark { + final String[] mTokens; + final String mText; + final Paint mTextPaint; + final int mTextWidth; + final int mTextHeight; + final int mTextAscent; + final int mTextDescent; + final int mDeltaX; + final int mDeltaY; + + Surface mSurface; + int mLastDW; + int mLastDH; + boolean mDrawNeeded; + + Watermark(Display display, SurfaceSession session, String[] tokens) { + final DisplayMetrics dm = new DisplayMetrics(); + display.getMetrics(dm); + + if (false) { + Log.i(WindowManagerService.TAG, "*********************** WATERMARK"); + for (int i=0; i<tokens.length; i++) { + Log.i(WindowManagerService.TAG, " TOKEN #" + i + ": " + tokens[i]); + } + } + + mTokens = tokens; + + StringBuilder builder = new StringBuilder(32); + int len = mTokens[0].length(); + len = len & ~1; + for (int i=0; i<len; i+=2) { + int c1 = mTokens[0].charAt(i); + int c2 = mTokens[0].charAt(i+1); + if (c1 >= 'a' && c1 <= 'f') c1 = c1 - 'a' + 10; + else if (c1 >= 'A' && c1 <= 'F') c1 = c1 - 'A' + 10; + else c1 -= '0'; + if (c2 >= 'a' && c2 <= 'f') c2 = c2 - 'a' + 10; + else if (c2 >= 'A' && c2 <= 'F') c2 = c2 - 'A' + 10; + else c2 -= '0'; + builder.append((char)(255-((c1*16)+c2))); + } + mText = builder.toString(); + if (false) { + Log.i(WindowManagerService.TAG, "Final text: " + mText); + } + + int fontSize = WindowManagerService.getPropertyInt(tokens, 1, + TypedValue.COMPLEX_UNIT_DIP, 20, dm); + + mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mTextPaint.setTextSize(fontSize); + mTextPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD)); + + FontMetricsInt fm = mTextPaint.getFontMetricsInt(); + mTextWidth = (int)mTextPaint.measureText(mText); + mTextAscent = fm.ascent; + mTextDescent = fm.descent; + mTextHeight = fm.descent - fm.ascent; + + mDeltaX = WindowManagerService.getPropertyInt(tokens, 2, + TypedValue.COMPLEX_UNIT_PX, mTextWidth*2, dm); + mDeltaY = WindowManagerService.getPropertyInt(tokens, 3, + TypedValue.COMPLEX_UNIT_PX, mTextHeight*3, dm); + int shadowColor = WindowManagerService.getPropertyInt(tokens, 4, + TypedValue.COMPLEX_UNIT_PX, 0xb0000000, dm); + int color = WindowManagerService.getPropertyInt(tokens, 5, + TypedValue.COMPLEX_UNIT_PX, 0x60ffffff, dm); + int shadowRadius = WindowManagerService.getPropertyInt(tokens, 6, + TypedValue.COMPLEX_UNIT_PX, 7, dm); + int shadowDx = WindowManagerService.getPropertyInt(tokens, 8, + TypedValue.COMPLEX_UNIT_PX, 0, dm); + int shadowDy = WindowManagerService.getPropertyInt(tokens, 9, + TypedValue.COMPLEX_UNIT_PX, 0, dm); + + mTextPaint.setColor(color); + mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor); + + try { + mSurface = new Surface(session, 0, + "WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0); + mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*100); + mSurface.setPosition(0, 0); + mSurface.show(); + } catch (OutOfResourcesException e) { + } + } + + void positionSurface(int dw, int dh) { + if (mLastDW != dw || mLastDH != dh) { + mLastDW = dw; + mLastDH = dh; + mSurface.setSize(dw, dh); + mDrawNeeded = true; + } + } + + void drawIfNeeded() { + if (mDrawNeeded) { + final int dw = mLastDW; + final int dh = mLastDH; + + mDrawNeeded = false; + Rect dirty = new Rect(0, 0, dw, dh); + Canvas c = null; + try { + c = mSurface.lockCanvas(dirty); + } catch (IllegalArgumentException e) { + } catch (OutOfResourcesException e) { + } + if (c != null) { + c.drawColor(0, PorterDuff.Mode.CLEAR); + + int deltaX = mDeltaX; + int deltaY = mDeltaY; + + // deltaX shouldn't be close to a round fraction of our + // x step, or else things will line up too much. + int div = (dw+mTextWidth)/deltaX; + int rem = (dw+mTextWidth) - (div*deltaX); + int qdelta = deltaX/4; + if (rem < qdelta || rem > (deltaX-qdelta)) { + deltaX += deltaX/3; + } + + int y = -mTextHeight; + int x = -mTextWidth; + while (y < (dh+mTextHeight)) { + c.drawText(mText, x, y, mTextPaint); + x += deltaX; + if (x >= dw) { + x -= (dw+mTextWidth); + y += deltaY; + } + } + mSurface.unlockCanvasAndPost(c); + } + } + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 2613988..e3218c8 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; @@ -22,7 +22,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; @@ -42,6 +41,10 @@ import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; import com.android.internal.view.IInputMethodManager; import com.android.internal.view.WindowManagerPolicyThread; +import com.android.server.AttributeCache; +import com.android.server.EventLogTags; +import com.android.server.PowerManagerService; +import com.android.server.Watchdog; import com.android.server.am.BatteryStatsService; import android.Manifest; @@ -50,8 +53,6 @@ import android.app.IActivityManager; import android.app.StatusBarManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; -import android.content.ClipData; -import android.content.ClipDescription; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -59,17 +60,12 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; -import android.graphics.Paint; import android.graphics.PixelFormat; -import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; -import android.graphics.Typeface; -import android.graphics.Paint.FontMetricsInt; import android.os.BatteryStats; import android.os.Binder; import android.os.Bundle; @@ -98,8 +94,6 @@ import android.util.Slog; import android.util.SparseIntArray; import android.util.TypedValue; import android.view.Display; -import android.view.DragEvent; -import android.view.Gravity; import android.view.IApplicationToken; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; @@ -116,13 +110,10 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceSession; import android.view.View; -import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.WindowManagerPolicy; -import android.view.Surface.OutOfResourcesException; import android.view.WindowManager.LayoutParams; -import android.view.animation.AccelerateInterpolator; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Transformation; @@ -219,7 +210,7 @@ public class WindowManagerService extends IWindowManager.Stub private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000; // Default input dispatching timeout in nanoseconds. - private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L; + static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L; static final int UPDATE_FOCUS_NORMAL = 0; static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; @@ -285,6 +276,8 @@ public class WindowManagerService extends IWindowManager.Stub final IBatteryStats mBatteryStats; + private static final boolean mInEmulator = SystemProperties.get("ro.kernel.qemu").equals("1"); + /** * All currently active sessions with clients. */ @@ -505,336 +498,8 @@ public class WindowManagerService extends IWindowManager.Stub boolean mTurnOnScreen; - /** - * Drag/drop state - */ - class DragState { - IBinder mToken; - Surface mSurface; - int mFlags; - IBinder mLocalWin; - ClipData mData; - ClipDescription mDataDescription; - boolean mDragResult; - float mCurrentX, mCurrentY; - float mThumbOffsetX, mThumbOffsetY; - InputChannel mServerChannel, mClientChannel; - WindowState mTargetWindow; - ArrayList<WindowState> mNotifiedWindows; - boolean mDragInProgress; - - private final Region mTmpRegion = new Region(); - - DragState(IBinder token, Surface surface, int flags, IBinder localWin) { - mToken = token; - mSurface = surface; - mFlags = flags; - mLocalWin = localWin; - mNotifiedWindows = new ArrayList<WindowState>(); - } - - void reset() { - if (mSurface != null) { - mSurface.destroy(); - } - mSurface = null; - mFlags = 0; - mLocalWin = null; - mToken = null; - mData = null; - mThumbOffsetX = mThumbOffsetY = 0; - mNotifiedWindows = null; - } - - void register() { - if (DEBUG_DRAG) Slog.d(TAG, "registering drag input channel"); - if (mClientChannel != null) { - Slog.e(TAG, "Duplicate register of drag input channel"); - } else { - InputChannel[] channels = InputChannel.openInputChannelPair("drag"); - mServerChannel = channels[0]; - mClientChannel = channels[1]; - mInputManager.registerInputChannel(mServerChannel, null); - InputQueue.registerInputChannel(mClientChannel, mDragInputHandler, - mH.getLooper().getQueue()); - } - } - - void unregister() { - if (DEBUG_DRAG) Slog.d(TAG, "unregistering drag input channel"); - if (mClientChannel == null) { - Slog.e(TAG, "Unregister of nonexistent drag input channel"); - } else { - mInputManager.unregisterInputChannel(mServerChannel); - InputQueue.unregisterInputChannel(mClientChannel); - mClientChannel.dispose(); - mServerChannel.dispose(); - mClientChannel = null; - mServerChannel = null; - } - } - - int getDragLayerLw() { - return mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG) - * TYPE_LAYER_MULTIPLIER - + TYPE_LAYER_OFFSET; - } - - /* call out to each visible window/session informing it about the drag - */ - void broadcastDragStartedLw(final float touchX, final float touchY) { - // Cache a base-class instance of the clip metadata so that parceling - // works correctly in calling out to the apps. - mDataDescription = (mData != null) ? mData.getDescription() : null; - mNotifiedWindows.clear(); - mDragInProgress = true; - - if (DEBUG_DRAG) { - Slog.d(TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")"); - } - - final int N = mWindows.size(); - for (int i = 0; i < N; i++) { - sendDragStartedLw(mWindows.get(i), touchX, touchY, mDataDescription); - } - } - - /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the - * designated window is potentially a drop recipient. There are race situations - * around DRAG_ENDED broadcast, so we make sure that once we've declared that - * the drag has ended, we never send out another DRAG_STARTED for this drag action. - * - * This method clones the 'event' parameter if it's being delivered to the same - * process, so it's safe for the caller to call recycle() on the event afterwards. - */ - private void sendDragStartedLw(WindowState newWin, float touchX, float touchY, - ClipDescription desc) { - // Don't actually send the event if the drag is supposed to be pinned - // to the originating window but 'newWin' is not that window. - if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) { - final IBinder winBinder = newWin.mClient.asBinder(); - if (winBinder != mLocalWin) { - if (DEBUG_DRAG) { - Slog.d(TAG, "Not dispatching local DRAG_STARTED to " + newWin); - } - return; - } - } - - if (mDragInProgress && newWin.isPotentialDragTarget()) { - DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, - touchX - newWin.mFrame.left, touchY - newWin.mFrame.top, - null, desc, null, false); - try { - newWin.mClient.dispatchDragEvent(event); - // track each window that we've notified that the drag is starting - mNotifiedWindows.add(newWin); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to drag-start window " + newWin); - } finally { - // if the callee was local, the dispatch has already recycled the event - if (Process.myPid() != newWin.mSession.mPid) { - event.recycle(); - } - } - } - } - - /* helper - construct and send a DRAG_STARTED event only if the window has not - * previously been notified, i.e. it became visible after the drag operation - * was begun. This is a rare case. - */ - private void sendDragStartedIfNeededLw(WindowState newWin) { - if (mDragInProgress) { - // If we have sent the drag-started, we needn't do so again - for (WindowState ws : mNotifiedWindows) { - if (ws == newWin) { - return; - } - } - if (DEBUG_DRAG) { - Slog.d(TAG, "need to send DRAG_STARTED to new window " + newWin); - } - sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription); - } - } - - void broadcastDragEndedLw() { - if (DEBUG_DRAG) { - Slog.d(TAG, "broadcasting DRAG_ENDED"); - } - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, - 0, 0, null, null, null, mDragResult); - for (WindowState ws: mNotifiedWindows) { - try { - ws.mClient.dispatchDragEvent(evt); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to drag-end window " + ws); - } - } - mNotifiedWindows.clear(); - mDragInProgress = false; - evt.recycle(); - } - - void endDragLw() { - mDragState.broadcastDragEndedLw(); - - // stop intercepting input - mDragState.unregister(); - mInputMonitor.updateInputWindowsLw(true /*force*/); - - // free our resources and drop all the object references - mDragState.reset(); - mDragState = null; - - if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-drag rotation"); - boolean changed = setRotationUncheckedLocked( - WindowManagerPolicy.USE_LAST_ROTATION, 0, false); - if (changed) { - mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); - } - } - - void notifyMoveLw(float x, float y) { - final int myPid = Process.myPid(); - - // Move the surface to the given touch - if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION notifyMoveLw"); - Surface.openTransaction(); - try { - mSurface.setPosition((int)(x - mThumbOffsetX), (int)(y - mThumbOffsetY)); - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DRAG " - + mSurface + ": pos=(" + - (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")"); - } finally { - Surface.closeTransaction(); - if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION notifyMoveLw"); - } - - // Tell the affected window - WindowState touchedWin = getTouchedWinAtPointLw(x, y); - if (touchedWin == null) { - if (DEBUG_DRAG) Slog.d(TAG, "No touched win at x=" + x + " y=" + y); - return; - } - if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) { - final IBinder touchedBinder = touchedWin.mClient.asBinder(); - if (touchedBinder != mLocalWin) { - // This drag is pinned only to the originating window, but the drag - // point is outside that window. Pretend it's over empty space. - touchedWin = null; - } - } - try { - // have we dragged over a new window? - if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) { - if (DEBUG_DRAG) { - Slog.d(TAG, "sending DRAG_EXITED to " + mTargetWindow); - } - // force DRAG_EXITED_EVENT if appropriate - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED, - x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top, - null, null, null, false); - mTargetWindow.mClient.dispatchDragEvent(evt); - if (myPid != mTargetWindow.mSession.mPid) { - evt.recycle(); - } - } - if (touchedWin != null) { - if (false && DEBUG_DRAG) { - Slog.d(TAG, "sending DRAG_LOCATION to " + touchedWin); - } - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION, - x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, - null, null, null, false); - touchedWin.mClient.dispatchDragEvent(evt); - if (myPid != touchedWin.mSession.mPid) { - evt.recycle(); - } - } - } catch (RemoteException e) { - Slog.w(TAG, "can't send drag notification to windows"); - } - mTargetWindow = touchedWin; - } - - // Tell the drop target about the data. Returns 'true' if we can immediately - // dispatch the global drag-ended message, 'false' if we need to wait for a - // result from the recipient. - boolean notifyDropLw(float x, float y) { - WindowState touchedWin = getTouchedWinAtPointLw(x, y); - if (touchedWin == null) { - // "drop" outside a valid window -- no recipient to apply a - // timeout to, and we can send the drag-ended message immediately. - mDragResult = false; - return true; - } - - if (DEBUG_DRAG) { - Slog.d(TAG, "sending DROP to " + touchedWin); - } - final int myPid = Process.myPid(); - final IBinder token = touchedWin.mClient.asBinder(); - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP, - x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, - null, null, mData, false); - try { - touchedWin.mClient.dispatchDragEvent(evt); - - // 5 second timeout for this window to respond to the drop - mH.removeMessages(H.DRAG_END_TIMEOUT, token); - Message msg = mH.obtainMessage(H.DRAG_END_TIMEOUT, token); - mH.sendMessageDelayed(msg, 5000); - } catch (RemoteException e) { - Slog.w(TAG, "can't send drop notification to win " + touchedWin); - return true; - } finally { - if (myPid != touchedWin.mSession.mPid) { - evt.recycle(); - } - } - mToken = token; - return false; - } - - // Find the visible, touch-deliverable window under the given point - private WindowState getTouchedWinAtPointLw(float xf, float yf) { - WindowState touchedWin = null; - final int x = (int) xf; - final int y = (int) yf; - final ArrayList<WindowState> windows = mWindows; - final int N = windows.size(); - for (int i = N - 1; i >= 0; i--) { - WindowState child = windows.get(i); - final int flags = child.mAttrs.flags; - if (!child.isVisibleLw()) { - // not visible == don't tell about drags - continue; - } - if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { - // not touchable == don't tell about drags - continue; - } - - child.getTouchableRegion(mTmpRegion); - - final int touchFlags = flags & - (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); - if (mTmpRegion.contains(x, y) || touchFlags == 0) { - // Found it - touchedWin = child; - break; - } - } - - return touchedWin; - } - } - DragState mDragState = null; - private final InputHandler mDragInputHandler = new BaseInputHandler() { + final InputHandler mDragInputHandler = new BaseInputHandler() { @Override public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) { boolean handled = false; @@ -2298,7 +1963,7 @@ public class WindowManagerService extends IWindowManager.Stub + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } - token = new WindowToken(attrs.token, -1, false); + token = new WindowToken(this, attrs.token, -1, false); addToken = true; } else if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW) { @@ -2332,7 +1997,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - win = new WindowState(session, client, token, + win = new WindowState(this, session, client, token, attachedWindow, attrs, viewVisibility); if (win.mDeathRecipient == null) { // Client has apparently died, so there is no reason to @@ -2630,7 +2295,7 @@ public class WindowManagerService extends IWindowManager.Stub mInputMonitor.updateInputWindowsLw(true /*force*/); } - private static void logSurface(WindowState w, String msg, RuntimeException where) { + static void logSurface(WindowState w, String msg, RuntimeException where) { String str = " SURFACE " + Integer.toHexString(w.hashCode()) + ": " + msg + " / " + w.mAttrs.getTitle(); if (where != null) { @@ -2640,7 +2305,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - private void setTransparentRegionWindow(Session session, IWindow client, Region region) { + void setTransparentRegionWindow(Session session, IWindow client, Region region) { long origId = Binder.clearCallingIdentity(); try { synchronized (mWindowMap) { @@ -3094,7 +2759,7 @@ public class WindowManagerService extends IWindowManager.Stub return null; } - private void applyEnterAnimationLocked(WindowState win) { + void applyEnterAnimationLocked(WindowState win) { int transit = WindowManagerPolicy.TRANSIT_SHOW; if (win.mEnterAnimationPending) { win.mEnterAnimationPending = false; @@ -3104,7 +2769,7 @@ public class WindowManagerService extends IWindowManager.Stub applyAnimationLocked(win, transit, true); } - private boolean applyAnimationLocked(WindowState win, + boolean applyAnimationLocked(WindowState win, int transit, boolean isEntrance) { if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) { // If we are trying to apply an animation, but already running @@ -3360,7 +3025,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG, "Attempted to add existing input method token: " + token); return; } - wtoken = new WindowToken(token, type, true); + wtoken = new WindowToken(this, token, type, true); mTokenMap.put(token, wtoken); if (type == TYPE_WALLPAPER) { mWallpaperTokens.add(wtoken); @@ -3448,7 +3113,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG, "Attempted to add existing app token: " + token); return; } - wtoken = new AppWindowToken(token); + wtoken = new AppWindowToken(this, token); wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; wtoken.groupId = groupId; wtoken.appFullscreen = fullscreen; @@ -5165,21 +4830,25 @@ public class WindowManagerService extends IWindowManager.Stub public void freezeRotation() { if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, - "setRotation()")) { + "freezeRotation()")) { throw new SecurityException("Requires SET_ORIENTATION permission"); } + if (DEBUG_ORIENTATION) Slog.v(TAG, "freezeRotation: mRotation=" + mRotation); + mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED, mRotation); setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false, 0); } public void thawRotation() { if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, - "setRotation()")) { + "thawRotation()")) { throw new SecurityException("Requires SET_ORIENTATION permission"); } - mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE, 0); + if (DEBUG_ORIENTATION) Slog.v(TAG, "thawRotation: mRotation=" + mRotation); + + mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE, 777); // rot not used setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false, 0); } @@ -5196,7 +4865,9 @@ public class WindowManagerService extends IWindowManager.Stub public void setRotationUnchecked(int rotation, boolean alwaysSendConfiguration, int animFlags) { if(DEBUG_ORIENTATION) Slog.v(TAG, - "alwaysSendConfiguration set to "+alwaysSendConfiguration); + "setRotationUnchecked(rotation=" + rotation + + " alwaysSendConfiguration=" + alwaysSendConfiguration + + " animFlags=" + animFlags); long origId = Binder.clearCallingIdentity(); boolean changed; @@ -5267,7 +4938,9 @@ public class WindowManagerService extends IWindowManager.Stub Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags); mInputManager.setDisplayOrientation(0, rotation); if (mDisplayEnabled) { - if (CUSTOM_SCREEN_ROTATION) { + // NOTE: We disable the rotation in the emulator because + // it doesn't support hardware OpenGL emulation yet. + if (CUSTOM_SCREEN_ROTATION && !mInEmulator) { Surface.freezeDisplay(0); if (!inTransaction) { if (SHOW_TRANSACTIONS) Slog.i(TAG, @@ -5349,8 +5022,8 @@ public class WindowManagerService extends IWindowManager.Stub * * @return True if the server was successfully started, false otherwise. * - * @see com.android.server.ViewServer - * @see com.android.server.ViewServer#VIEW_SERVER_DEFAULT_PORT + * @see com.android.server.wm.ViewServer + * @see com.android.server.wm.ViewServer#VIEW_SERVER_DEFAULT_PORT */ public boolean startViewServer(int port) { if (isSystemSecure()) { @@ -5396,7 +5069,7 @@ public class WindowManagerService extends IWindowManager.Stub * @return True if the server stopped, false if it wasn't started or * couldn't be stopped. * - * @see com.android.server.ViewServer + * @see com.android.server.wm.ViewServer */ public boolean stopViewServer() { if (isSystemSecure()) { @@ -5418,7 +5091,7 @@ public class WindowManagerService extends IWindowManager.Stub * * @return True if the server is running, false otherwise. * - * @see com.android.server.ViewServer + * @see com.android.server.wm.ViewServer */ public boolean isViewServerRunning() { if (isSystemSecure()) { @@ -5580,7 +5253,7 @@ public class WindowManagerService extends IWindowManager.Stub parameters = ""; } - final WindowManagerService.WindowState window = findWindow(hashCode); + final WindowState window = findWindow(hashCode); if (window == null) { return false; } @@ -5891,8 +5564,7 @@ public class WindowManagerService extends IWindowManager.Stub outSurface.copyFrom(surface); final IBinder winBinder = window.asBinder(); token = new Binder(); - // TODO: preserve flags param in DragState - mDragState = new DragState(token, surface, 0, winBinder); + mDragState = new DragState(this, token, surface, /*flags*/ 0, winBinder); mDragState.mSurface = surface; token = mDragState.mToken = new Binder(); @@ -5922,355 +5594,8 @@ public class WindowManagerService extends IWindowManager.Stub // Input Events and Focus Management // ------------------------------------------------------------- - InputMonitor mInputMonitor = new InputMonitor(); + final InputMonitor mInputMonitor = new InputMonitor(this); - /* Tracks the progress of input dispatch and ensures that input dispatch state - * is kept in sync with changes in window focus, visibility, registration, and - * other relevant Window Manager state transitions. */ - final class InputMonitor { - // Current window with input focus for keys and other non-touch events. May be null. - private WindowState mInputFocus; - - // When true, prevents input dispatch from proceeding until set to false again. - private boolean mInputDispatchFrozen; - - // When true, input dispatch proceeds normally. Otherwise all events are dropped. - private boolean mInputDispatchEnabled = true; - - // When true, need to call updateInputWindowsLw(). - private boolean mUpdateInputWindowsNeeded = true; - - // Temporary list of windows information to provide to the input dispatcher. - private InputWindowList mTempInputWindows = new InputWindowList(); - - // Temporary input application object to provide to the input dispatcher. - private InputApplication mTempInputApplication = new InputApplication(); - - // Set to true when the first input device configuration change notification - // is received to indicate that the input devices are ready. - private final Object mInputDevicesReadyMonitor = new Object(); - private boolean mInputDevicesReady; - - /* Notifies the window manager about a broken input channel. - * - * Called by the InputManager. - */ - public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) { - if (inputWindowHandle == null) { - return; - } - - synchronized (mWindowMap) { - WindowState windowState = (WindowState) inputWindowHandle.windowState; - Slog.i(TAG, "WINDOW DIED " + windowState); - removeWindowLocked(windowState.mSession, windowState); - } - } - - /* Notifies the window manager about an application that is not responding. - * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. - * - * Called by the InputManager. - */ - public long notifyANR(InputApplicationHandle inputApplicationHandle, - InputWindowHandle inputWindowHandle) { - AppWindowToken appWindowToken = null; - if (inputWindowHandle != null) { - synchronized (mWindowMap) { - WindowState windowState = (WindowState) inputWindowHandle.windowState; - if (windowState != null) { - Slog.i(TAG, "Input event dispatching timed out sending to " - + windowState.mAttrs.getTitle()); - appWindowToken = windowState.mAppToken; - } - } - } - - if (appWindowToken == null && inputApplicationHandle != null) { - appWindowToken = inputApplicationHandle.appWindowToken; - Slog.i(TAG, "Input event dispatching timed out sending to application " - + appWindowToken.stringName); - } - - if (appWindowToken != null && appWindowToken.appToken != null) { - try { - // Notify the activity manager about the timeout and let it decide whether - // to abort dispatching or keep waiting. - boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(); - if (! abort) { - // The activity manager declined to abort dispatching. - // Wait a bit longer and timeout again later. - return appWindowToken.inputDispatchingTimeoutNanos; - } - } catch (RemoteException ex) { - } - } - return 0; // abort dispatching - } - - private void addDragInputWindowLw(InputWindowList windowList) { - final InputWindow inputWindow = windowList.add(); - inputWindow.inputChannel = mDragState.mServerChannel; - inputWindow.name = "drag"; - inputWindow.layoutParamsFlags = 0; - inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; - inputWindow.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; - inputWindow.visible = true; - inputWindow.canReceiveKeys = false; - inputWindow.hasFocus = true; - inputWindow.hasWallpaper = false; - inputWindow.paused = false; - inputWindow.layer = mDragState.getDragLayerLw(); - inputWindow.ownerPid = Process.myPid(); - inputWindow.ownerUid = Process.myUid(); - - // The drag window covers the entire display - inputWindow.frameLeft = 0; - inputWindow.frameTop = 0; - inputWindow.frameRight = mDisplay.getWidth(); - inputWindow.frameBottom = mDisplay.getHeight(); - - // The drag window cannot receive new touches. - inputWindow.touchableRegion.setEmpty(); - } - - public void setUpdateInputWindowsNeededLw() { - mUpdateInputWindowsNeeded = true; - } - - /* Updates the cached window information provided to the input dispatcher. */ - public void updateInputWindowsLw(boolean force) { - if (!force && !mUpdateInputWindowsNeeded) { - return; - } - mUpdateInputWindowsNeeded = false; - - // Populate the input window list with information about all of the windows that - // could potentially receive input. - // As an optimization, we could try to prune the list of windows but this turns - // out to be difficult because only the native code knows for sure which window - // currently has touch focus. - final ArrayList<WindowState> windows = mWindows; - - // If there's a drag in flight, provide a pseudowindow to catch drag input - final boolean inDrag = (mDragState != null); - if (inDrag) { - if (DEBUG_DRAG) { - Log.d(TAG, "Inserting drag window"); - } - addDragInputWindowLw(mTempInputWindows); - } - - final int N = windows.size(); - for (int i = N - 1; i >= 0; i--) { - final WindowState child = windows.get(i); - if (child.mInputChannel == null || child.mRemoved) { - // Skip this window because it cannot possibly receive input. - continue; - } - - final int flags = child.mAttrs.flags; - final int type = child.mAttrs.type; - - final boolean hasFocus = (child == mInputFocus); - final boolean isVisible = child.isVisibleLw(); - final boolean hasWallpaper = (child == mWallpaperTarget) - && (type != WindowManager.LayoutParams.TYPE_KEYGUARD); - - // If there's a drag in progress and 'child' is a potential drop target, - // make sure it's been told about the drag - if (inDrag && isVisible) { - mDragState.sendDragStartedIfNeededLw(child); - } - - // Add a window to our list of input windows. - final InputWindow inputWindow = mTempInputWindows.add(); - inputWindow.inputWindowHandle = child.mInputWindowHandle; - inputWindow.inputChannel = child.mInputChannel; - inputWindow.name = child.toString(); - inputWindow.layoutParamsFlags = flags; - inputWindow.layoutParamsType = type; - inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); - inputWindow.visible = isVisible; - inputWindow.canReceiveKeys = child.canReceiveKeys(); - inputWindow.hasFocus = hasFocus; - inputWindow.hasWallpaper = hasWallpaper; - inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false; - inputWindow.layer = child.mLayer; - inputWindow.ownerPid = child.mSession.mPid; - inputWindow.ownerUid = child.mSession.mUid; - - final Rect frame = child.mFrame; - inputWindow.frameLeft = frame.left; - inputWindow.frameTop = frame.top; - inputWindow.frameRight = frame.right; - inputWindow.frameBottom = frame.bottom; - - child.getTouchableRegion(inputWindow.touchableRegion); - } - - // Send windows to native code. - mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray()); - - // Clear the list in preparation for the next round. - // Also avoids keeping InputChannel objects referenced unnecessarily. - mTempInputWindows.clear(); - } - - /* Notifies that the input device configuration has changed. */ - public void notifyConfigurationChanged() { - sendNewConfiguration(); - - synchronized (mInputDevicesReadyMonitor) { - if (!mInputDevicesReady) { - mInputDevicesReady = true; - mInputDevicesReadyMonitor.notifyAll(); - } - } - } - - /* Waits until the built-in input devices have been configured. */ - public boolean waitForInputDevicesReady(long timeoutMillis) { - synchronized (mInputDevicesReadyMonitor) { - if (!mInputDevicesReady) { - try { - mInputDevicesReadyMonitor.wait(timeoutMillis); - } catch (InterruptedException ex) { - } - } - return mInputDevicesReady; - } - } - - /* Notifies that the lid switch changed state. */ - public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { - mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen); - } - - /* Provides an opportunity for the window manager policy to intercept early key - * processing as soon as the key has been read from the device. */ - public int interceptKeyBeforeQueueing( - KeyEvent event, int policyFlags, boolean isScreenOn) { - return mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn); - } - - /* Provides an opportunity for the window manager policy to process a key before - * ordinary dispatch. */ - public boolean interceptKeyBeforeDispatching( - InputWindowHandle focus, KeyEvent event, int policyFlags) { - WindowState windowState = focus != null ? (WindowState) focus.windowState : null; - return mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags); - } - - /* Provides an opportunity for the window manager policy to process a key that - * the application did not handle. */ - public KeyEvent dispatchUnhandledKey( - InputWindowHandle focus, KeyEvent event, int policyFlags) { - WindowState windowState = focus != null ? (WindowState) focus.windowState : null; - return mPolicy.dispatchUnhandledKey(windowState, event, policyFlags); - } - - /* Called when the current input focus changes. - * Layer assignment is assumed to be complete by the time this is called. - */ - public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { - if (DEBUG_INPUT) { - Slog.d(TAG, "Input focus has changed to " + newWindow); - } - - if (newWindow != mInputFocus) { - if (newWindow != null && newWindow.canReceiveKeys()) { - // Displaying a window implicitly causes dispatching to be unpaused. - // This is to protect against bugs if someone pauses dispatching but - // forgets to resume. - newWindow.mToken.paused = false; - } - - mInputFocus = newWindow; - setUpdateInputWindowsNeededLw(); - - if (updateInputWindows) { - updateInputWindowsLw(false /*force*/); - } - } - } - - public void setFocusedAppLw(AppWindowToken newApp) { - // Focused app has changed. - if (newApp == null) { - mInputManager.setFocusedApplication(null); - } else { - mTempInputApplication.inputApplicationHandle = newApp.mInputApplicationHandle; - mTempInputApplication.name = newApp.toString(); - mTempInputApplication.dispatchingTimeoutNanos = - newApp.inputDispatchingTimeoutNanos; - - mInputManager.setFocusedApplication(mTempInputApplication); - - mTempInputApplication.recycle(); - } - } - - public void pauseDispatchingLw(WindowToken window) { - if (! window.paused) { - if (DEBUG_INPUT) { - Slog.v(TAG, "Pausing WindowToken " + window); - } - - window.paused = true; - updateInputWindowsLw(true /*force*/); - } - } - - public void resumeDispatchingLw(WindowToken window) { - if (window.paused) { - if (DEBUG_INPUT) { - Slog.v(TAG, "Resuming WindowToken " + window); - } - - window.paused = false; - updateInputWindowsLw(true /*force*/); - } - } - - public void freezeInputDispatchingLw() { - if (! mInputDispatchFrozen) { - if (DEBUG_INPUT) { - Slog.v(TAG, "Freezing input dispatching"); - } - - mInputDispatchFrozen = true; - updateInputDispatchModeLw(); - } - } - - public void thawInputDispatchingLw() { - if (mInputDispatchFrozen) { - if (DEBUG_INPUT) { - Slog.v(TAG, "Thawing input dispatching"); - } - - mInputDispatchFrozen = false; - updateInputDispatchModeLw(); - } - } - - public void setEventDispatchingLw(boolean enabled) { - if (mInputDispatchEnabled != enabled) { - if (DEBUG_INPUT) { - Slog.v(TAG, "Setting event dispatching to " + enabled); - } - - mInputDispatchEnabled = enabled; - updateInputDispatchModeLw(); - } - } - - private void updateInputDispatchModeLw() { - mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen); - } - } - public void pauseKeyDispatching(IBinder _token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "pauseKeyDispatching()")) { @@ -6487,8 +5812,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplay = wm.getDefaultDisplay(); mInitialDisplayWidth = mDisplay.getWidth(); mInitialDisplayHeight = mDisplay.getHeight(); - mInputManager.setDisplaySize(0, Display.unmapDisplaySize(mInitialDisplayWidth), - Display.unmapDisplaySize(mInitialDisplayHeight)); + mInputManager.setDisplaySize(0, mDisplay.getRealWidth(), mDisplay.getRealHeight()); } try { @@ -6499,2409 +5823,6 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.systemReady(); } - // ------------------------------------------------------------- - // Client Session State - // ------------------------------------------------------------- - - private final class Session extends IWindowSession.Stub - implements IBinder.DeathRecipient { - final IInputMethodClient mClient; - final IInputContext mInputContext; - final int mUid; - final int mPid; - final String mStringName; - SurfaceSession mSurfaceSession; - int mNumWindow = 0; - boolean mClientDead = false; - - public Session(IInputMethodClient client, IInputContext inputContext) { - mClient = client; - mInputContext = inputContext; - mUid = Binder.getCallingUid(); - mPid = Binder.getCallingPid(); - StringBuilder sb = new StringBuilder(); - sb.append("Session{"); - sb.append(Integer.toHexString(System.identityHashCode(this))); - sb.append(" uid "); - sb.append(mUid); - sb.append("}"); - mStringName = sb.toString(); - - synchronized (mWindowMap) { - if (mInputMethodManager == null && mHaveInputMethods) { - IBinder b = ServiceManager.getService( - Context.INPUT_METHOD_SERVICE); - mInputMethodManager = IInputMethodManager.Stub.asInterface(b); - } - } - long ident = Binder.clearCallingIdentity(); - try { - // Note: it is safe to call in to the input method manager - // here because we are not holding our lock. - if (mInputMethodManager != null) { - mInputMethodManager.addClient(client, inputContext, - mUid, mPid); - } else { - client.setUsingInputMethod(false); - } - client.asBinder().linkToDeath(this, 0); - } catch (RemoteException e) { - // The caller has died, so we can just forget about this. - try { - if (mInputMethodManager != null) { - mInputMethodManager.removeClient(client); - } - } catch (RemoteException ee) { - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - try { - return super.onTransact(code, data, reply, flags); - } catch (RuntimeException e) { - // Log all 'real' exceptions thrown to the caller - if (!(e instanceof SecurityException)) { - Slog.e(TAG, "Window Session Crash", e); - } - throw e; - } - } - - public void binderDied() { - // Note: it is safe to call in to the input method manager - // here because we are not holding our lock. - try { - if (mInputMethodManager != null) { - mInputMethodManager.removeClient(mClient); - } - } catch (RemoteException e) { - } - synchronized(mWindowMap) { - mClient.asBinder().unlinkToDeath(this, 0); - mClientDead = true; - killSessionLocked(); - } - } - - public int add(IWindow window, WindowManager.LayoutParams attrs, - int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) { - return addWindow(this, window, attrs, viewVisibility, outContentInsets, - outInputChannel); - } - - public int addWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs, - int viewVisibility, Rect outContentInsets) { - return addWindow(this, window, attrs, viewVisibility, outContentInsets, null); - } - - public void remove(IWindow window) { - removeWindow(this, window); - } - - public int relayout(IWindow window, WindowManager.LayoutParams attrs, - int requestedWidth, int requestedHeight, int viewFlags, - boolean insetsPending, Rect outFrame, Rect outContentInsets, - Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { - //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); - int res = relayoutWindow(this, window, attrs, - requestedWidth, requestedHeight, viewFlags, insetsPending, - outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface); - //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); - return res; - } - - public void setTransparentRegion(IWindow window, Region region) { - setTransparentRegionWindow(this, window, region); - } - - public void setInsets(IWindow window, int touchableInsets, - Rect contentInsets, Rect visibleInsets, Region touchableArea) { - setInsetsWindow(this, window, touchableInsets, contentInsets, - visibleInsets, touchableArea); - } - - public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { - getWindowDisplayFrame(this, window, outDisplayFrame); - } - - public void finishDrawing(IWindow window) { - if (localLOGV) Slog.v( - TAG, "IWindow finishDrawing called for " + window); - finishDrawingWindow(this, window); - } - - public void setInTouchMode(boolean mode) { - synchronized(mWindowMap) { - mInTouchMode = mode; - } - } - - public boolean getInTouchMode() { - synchronized(mWindowMap) { - return mInTouchMode; - } - } - - public boolean performHapticFeedback(IWindow window, int effectId, - boolean always) { - synchronized(mWindowMap) { - long ident = Binder.clearCallingIdentity(); - try { - return mPolicy.performHapticFeedbackLw( - windowForClientLocked(this, window, true), - effectId, always); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - /* Drag/drop */ - public IBinder prepareDrag(IWindow window, int flags, - int width, int height, Surface outSurface) { - return prepareDragSurface(window, mSurfaceSession, flags, - width, height, outSurface); - } - - public boolean performDrag(IWindow window, IBinder dragToken, - float touchX, float touchY, float thumbCenterX, float thumbCenterY, - ClipData data) { - if (DEBUG_DRAG) { - Slog.d(TAG, "perform drag: win=" + window + " data=" + data); - } - - synchronized (mWindowMap) { - if (mDragState == null) { - Slog.w(TAG, "No drag prepared"); - throw new IllegalStateException("performDrag() without prepareDrag()"); - } - - if (dragToken != mDragState.mToken) { - Slog.w(TAG, "Performing mismatched drag"); - throw new IllegalStateException("performDrag() does not match prepareDrag()"); - } - - WindowState callingWin = windowForClientLocked(null, window, false); - if (callingWin == null) { - Slog.w(TAG, "Bad requesting window " + window); - return false; // !!! TODO: throw here? - } - - // !!! TODO: if input is not still focused on the initiating window, fail - // the drag initiation (e.g. an alarm window popped up just as the application - // called performDrag() - - mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder()); - - // !!! TODO: extract the current touch (x, y) in screen coordinates. That - // will let us eliminate the (touchX,touchY) parameters from the API. - - // !!! FIXME: put all this heavy stuff onto the mH looper, as well as - // the actual drag event dispatch stuff in the dragstate - - mDragState.register(); - mInputMonitor.updateInputWindowsLw(true /*force*/); - if (!mInputManager.transferTouchFocus(callingWin.mInputChannel, - mDragState.mServerChannel)) { - Slog.e(TAG, "Unable to transfer touch focus"); - mDragState.unregister(); - mDragState = null; - mInputMonitor.updateInputWindowsLw(true /*force*/); - return false; - } - - mDragState.mData = data; - mDragState.mCurrentX = touchX; - mDragState.mCurrentY = touchY; - mDragState.broadcastDragStartedLw(touchX, touchY); - - // remember the thumb offsets for later - mDragState.mThumbOffsetX = thumbCenterX; - mDragState.mThumbOffsetY = thumbCenterY; - - // Make the surface visible at the proper location - final Surface surface = mDragState.mSurface; - if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION performDrag"); - Surface.openTransaction(); - try { - surface.setPosition((int)(touchX - thumbCenterX), - (int)(touchY - thumbCenterY)); - surface.setAlpha(.7071f); - surface.setLayer(mDragState.getDragLayerLw()); - surface.show(); - } finally { - Surface.closeTransaction(); - if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION performDrag"); - } - } - - return true; // success! - } - - public void reportDropResult(IWindow window, boolean consumed) { - IBinder token = window.asBinder(); - if (DEBUG_DRAG) { - Slog.d(TAG, "Drop result=" + consumed + " reported by " + token); - } - - synchronized (mWindowMap) { - long ident = Binder.clearCallingIdentity(); - try { - if (mDragState == null || mDragState.mToken != token) { - Slog.w(TAG, "Invalid drop-result claim by " + window); - throw new IllegalStateException("reportDropResult() by non-recipient"); - } - - // The right window has responded, even if it's no longer around, - // so be sure to halt the timeout even if the later WindowState - // lookup fails. - mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder()); - WindowState callingWin = windowForClientLocked(null, window, false); - if (callingWin == null) { - Slog.w(TAG, "Bad result-reporting window " + window); - return; // !!! TODO: throw here? - } - - mDragState.mDragResult = consumed; - mDragState.endDragLw(); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - public void dragRecipientEntered(IWindow window) { - if (DEBUG_DRAG) { - Slog.d(TAG, "Drag into new candidate view @ " + window.asBinder()); - } - } - - public void dragRecipientExited(IWindow window) { - if (DEBUG_DRAG) { - Slog.d(TAG, "Drag from old candidate view @ " + window.asBinder()); - } - } - - public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) { - synchronized(mWindowMap) { - long ident = Binder.clearCallingIdentity(); - try { - setWindowWallpaperPositionLocked( - windowForClientLocked(this, window, true), - x, y, xStep, yStep); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - public void wallpaperOffsetsComplete(IBinder window) { - WindowManagerService.this.wallpaperOffsetsComplete(window); - } - - public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, - int z, Bundle extras, boolean sync) { - synchronized(mWindowMap) { - long ident = Binder.clearCallingIdentity(); - try { - return sendWindowWallpaperCommandLocked( - windowForClientLocked(this, window, true), - action, x, y, z, extras, sync); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - public void wallpaperCommandComplete(IBinder window, Bundle result) { - WindowManagerService.this.wallpaperCommandComplete(window, result); - } - - void windowAddedLocked() { - if (mSurfaceSession == null) { - if (localLOGV) Slog.v( - TAG, "First window added to " + this + ", creating SurfaceSession"); - mSurfaceSession = new SurfaceSession(); - if (SHOW_TRANSACTIONS) Slog.i( - TAG, " NEW SURFACE SESSION " + mSurfaceSession); - mSessions.add(this); - } - mNumWindow++; - } - - void windowRemovedLocked() { - mNumWindow--; - killSessionLocked(); - } - - void killSessionLocked() { - if (mNumWindow <= 0 && mClientDead) { - mSessions.remove(this); - if (mSurfaceSession != null) { - if (localLOGV) Slog.v( - TAG, "Last window removed from " + this - + ", destroying " + mSurfaceSession); - if (SHOW_TRANSACTIONS) Slog.i( - TAG, " KILL SURFACE SESSION " + mSurfaceSession); - try { - mSurfaceSession.kill(); - } catch (Exception e) { - Slog.w(TAG, "Exception thrown when killing surface session " - + mSurfaceSession + " in session " + this - + ": " + e.toString()); - } - mSurfaceSession = null; - } - } - } - - void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow); - pw.print(" mClientDead="); pw.print(mClientDead); - pw.print(" mSurfaceSession="); pw.println(mSurfaceSession); - } - - @Override - public String toString() { - return mStringName; - } - } - - // ------------------------------------------------------------- - // Client Window State - // ------------------------------------------------------------- - - private final class WindowState implements WindowManagerPolicy.WindowState { - final Session mSession; - final IWindow mClient; - WindowToken mToken; - WindowToken mRootToken; - AppWindowToken mAppToken; - AppWindowToken mTargetAppToken; - final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams(); - final DeathRecipient mDeathRecipient; - final WindowState mAttachedWindow; - final ArrayList<WindowState> mChildWindows = new ArrayList<WindowState>(); - final int mBaseLayer; - final int mSubLayer; - final boolean mLayoutAttached; - final boolean mIsImWindow; - final boolean mIsWallpaper; - final boolean mIsFloatingLayer; - int mViewVisibility; - boolean mPolicyVisibility = true; - boolean mPolicyVisibilityAfterAnim = true; - boolean mAppFreezing; - Surface mSurface; - boolean mReportDestroySurface; - boolean mSurfacePendingDestroy; - boolean mAttachedHidden; // is our parent window hidden? - boolean mLastHidden; // was this window last hidden? - boolean mWallpaperVisible; // for wallpaper, what was last vis report? - int mRequestedWidth; - int mRequestedHeight; - int mLastRequestedWidth; - int mLastRequestedHeight; - int mLayer; - int mAnimLayer; - int mLastLayer; - boolean mHaveFrame; - boolean mObscured; - boolean mTurnOnScreen; - - int mLayoutSeq = -1; - - Configuration mConfiguration = null; - - // Actual frame shown on-screen (may be modified by animation) - final Rect mShownFrame = new Rect(); - final Rect mLastShownFrame = new Rect(); - - /** - * Set when we have changed the size of the surface, to know that - * we must tell them application to resize (and thus redraw itself). - */ - boolean mSurfaceResized; - - /** - * Insets that determine the actually visible area - */ - final Rect mVisibleInsets = new Rect(); - final Rect mLastVisibleInsets = new Rect(); - boolean mVisibleInsetsChanged; - - /** - * Insets that are covered by system windows - */ - final Rect mContentInsets = new Rect(); - final Rect mLastContentInsets = new Rect(); - boolean mContentInsetsChanged; - - /** - * Set to true if we are waiting for this window to receive its - * given internal insets before laying out other windows based on it. - */ - boolean mGivenInsetsPending; - - /** - * These are the content insets that were given during layout for - * this window, to be applied to windows behind it. - */ - final Rect mGivenContentInsets = new Rect(); - - /** - * These are the visible insets that were given during layout for - * this window, to be applied to windows behind it. - */ - final Rect mGivenVisibleInsets = new Rect(); - - /** - * This is the given touchable area relative to the window frame, or null if none. - */ - final Region mGivenTouchableRegion = new Region(); - - /** - * Flag indicating whether the touchable region should be adjusted by - * the visible insets; if false the area outside the visible insets is - * NOT touchable, so we must use those to adjust the frame during hit - * tests. - */ - int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; - - // Current transformation being applied. - boolean mHaveMatrix; - float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1; - float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1; - float mHScale=1, mVScale=1; - float mLastHScale=1, mLastVScale=1; - final Matrix mTmpMatrix = new Matrix(); - - // "Real" frame that the application sees. - final Rect mFrame = new Rect(); - final Rect mLastFrame = new Rect(); - - final Rect mContainingFrame = new Rect(); - final Rect mDisplayFrame = new Rect(); - final Rect mContentFrame = new Rect(); - final Rect mParentFrame = new Rect(); - final Rect mVisibleFrame = new Rect(); - - boolean mContentChanged; - - float mShownAlpha = 1; - float mAlpha = 1; - float mLastAlpha = 1; - - // Set to true if, when the window gets displayed, it should perform - // an enter animation. - boolean mEnterAnimationPending; - - // Currently running animation. - boolean mAnimating; - boolean mLocalAnimating; - Animation mAnimation; - boolean mAnimationIsEntrance; - boolean mHasTransformation; - boolean mHasLocalTransformation; - final Transformation mTransformation = new Transformation(); - - // If a window showing a wallpaper: the requested offset for the - // wallpaper; if a wallpaper window: the currently applied offset. - float mWallpaperX = -1; - float mWallpaperY = -1; - - // If a window showing a wallpaper: what fraction of the offset - // range corresponds to a full virtual screen. - float mWallpaperXStep = -1; - float mWallpaperYStep = -1; - - // Wallpaper windows: pixels offset based on above variables. - int mXOffset; - int mYOffset; - - // This is set after IWindowSession.relayout() has been called at - // least once for the window. It allows us to detect the situation - // where we don't yet have a surface, but should have one soon, so - // we can give the window focus before waiting for the relayout. - boolean mRelayoutCalled; - - // This is set after the Surface has been created but before the - // window has been drawn. During this time the surface is hidden. - boolean mDrawPending; - - // This is set after the window has finished drawing for the first - // time but before its surface is shown. The surface will be - // displayed when the next layout is run. - boolean mCommitDrawPending; - - // This is set during the time after the window's drawing has been - // committed, and before its surface is actually shown. It is used - // to delay showing the surface until all windows in a token are ready - // to be shown. - boolean mReadyToShow; - - // Set when the window has been shown in the screen the first time. - boolean mHasDrawn; - - // Currently running an exit animation? - boolean mExiting; - - // Currently on the mDestroySurface list? - boolean mDestroying; - - // Completely remove from window manager after exit animation? - boolean mRemoveOnExit; - - // Set when the orientation is changing and this window has not yet - // been updated for the new orientation. - boolean mOrientationChanging; - - // Is this window now (or just being) removed? - boolean mRemoved; - - // Temp for keeping track of windows that have been removed when - // rebuilding window list. - boolean mRebuilding; - - // For debugging, this is the last information given to the surface flinger. - boolean mSurfaceShown; - int mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH; - int mSurfaceLayer; - float mSurfaceAlpha; - - // Input channel and input window handle used by the input dispatcher. - InputWindowHandle mInputWindowHandle; - InputChannel mInputChannel; - - // Used to improve performance of toString() - String mStringNameCache; - CharSequence mLastTitle; - boolean mWasPaused; - - WindowState(Session s, IWindow c, WindowToken token, - WindowState attachedWindow, WindowManager.LayoutParams a, - int viewVisibility) { - mSession = s; - mClient = c; - mToken = token; - mAttrs.copyFrom(a); - mViewVisibility = viewVisibility; - DeathRecipient deathRecipient = new DeathRecipient(); - mAlpha = a.alpha; - if (localLOGV) Slog.v( - TAG, "Window " + this + " client=" + c.asBinder() - + " token=" + token + " (" + mAttrs.token + ")"); - try { - c.asBinder().linkToDeath(deathRecipient, 0); - } catch (RemoteException e) { - mDeathRecipient = null; - mAttachedWindow = null; - mLayoutAttached = false; - mIsImWindow = false; - mIsWallpaper = false; - mIsFloatingLayer = false; - mBaseLayer = 0; - mSubLayer = 0; - return; - } - mDeathRecipient = deathRecipient; - - if ((mAttrs.type >= FIRST_SUB_WINDOW && - mAttrs.type <= LAST_SUB_WINDOW)) { - // The multiplier here is to reserve space for multiple - // windows in the same type layer. - mBaseLayer = mPolicy.windowTypeToLayerLw( - attachedWindow.mAttrs.type) * TYPE_LAYER_MULTIPLIER - + TYPE_LAYER_OFFSET; - mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type); - mAttachedWindow = attachedWindow; - if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + mAttachedWindow); - mAttachedWindow.mChildWindows.add(this); - mLayoutAttached = mAttrs.type != - WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; - mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD - || attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG; - mIsWallpaper = attachedWindow.mAttrs.type == TYPE_WALLPAPER; - mIsFloatingLayer = mIsImWindow || mIsWallpaper; - } else { - // The multiplier here is to reserve space for multiple - // windows in the same type layer. - mBaseLayer = mPolicy.windowTypeToLayerLw(a.type) - * TYPE_LAYER_MULTIPLIER - + TYPE_LAYER_OFFSET; - mSubLayer = 0; - mAttachedWindow = null; - mLayoutAttached = false; - mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD - || mAttrs.type == TYPE_INPUT_METHOD_DIALOG; - mIsWallpaper = mAttrs.type == TYPE_WALLPAPER; - mIsFloatingLayer = mIsImWindow || mIsWallpaper; - } - - WindowState appWin = this; - while (appWin.mAttachedWindow != null) { - appWin = mAttachedWindow; - } - WindowToken appToken = appWin.mToken; - while (appToken.appWindowToken == null) { - WindowToken parent = mTokenMap.get(appToken.token); - if (parent == null || appToken == parent) { - break; - } - appToken = parent; - } - mRootToken = appToken; - mAppToken = appToken.appWindowToken; - - mSurface = null; - mRequestedWidth = 0; - mRequestedHeight = 0; - mLastRequestedWidth = 0; - mLastRequestedHeight = 0; - mXOffset = 0; - mYOffset = 0; - mLayer = 0; - mAnimLayer = 0; - mLastLayer = 0; - mInputWindowHandle = new InputWindowHandle( - mAppToken != null ? mAppToken.mInputApplicationHandle : null, this); - } - - void attach() { - if (localLOGV) Slog.v( - TAG, "Attaching " + this + " token=" + mToken - + ", list=" + mToken.windows); - mSession.windowAddedLocked(); - } - - public void computeFrameLw(Rect pf, Rect df, Rect cf, Rect vf) { - mHaveFrame = true; - - final Rect container = mContainingFrame; - container.set(pf); - - final Rect display = mDisplayFrame; - display.set(df); - - if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) { - container.intersect(mCompatibleScreenFrame); - if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) { - display.intersect(mCompatibleScreenFrame); - } - } - - final int pw = container.right - container.left; - final int ph = container.bottom - container.top; - - int w,h; - if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) { - w = mAttrs.width < 0 ? pw : mAttrs.width; - h = mAttrs.height< 0 ? ph : mAttrs.height; - } else { - w = mAttrs.width == mAttrs.MATCH_PARENT ? pw : mRequestedWidth; - h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight; - } - - if (!mParentFrame.equals(pf)) { - //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame - // + " to " + pf); - mParentFrame.set(pf); - mContentChanged = true; - } - - final Rect content = mContentFrame; - content.set(cf); - - final Rect visible = mVisibleFrame; - visible.set(vf); - - final Rect frame = mFrame; - final int fw = frame.width(); - final int fh = frame.height(); - - //System.out.println("In: w=" + w + " h=" + h + " container=" + - // container + " x=" + mAttrs.x + " y=" + mAttrs.y); - - Gravity.apply(mAttrs.gravity, w, h, container, - (int) (mAttrs.x + mAttrs.horizontalMargin * pw), - (int) (mAttrs.y + mAttrs.verticalMargin * ph), frame); - - //System.out.println("Out: " + mFrame); - - // Now make sure the window fits in the overall display. - Gravity.applyDisplay(mAttrs.gravity, df, frame); - - // Make sure the content and visible frames are inside of the - // final window frame. - if (content.left < frame.left) content.left = frame.left; - if (content.top < frame.top) content.top = frame.top; - if (content.right > frame.right) content.right = frame.right; - if (content.bottom > frame.bottom) content.bottom = frame.bottom; - if (visible.left < frame.left) visible.left = frame.left; - if (visible.top < frame.top) visible.top = frame.top; - if (visible.right > frame.right) visible.right = frame.right; - if (visible.bottom > frame.bottom) visible.bottom = frame.bottom; - - final Rect contentInsets = mContentInsets; - contentInsets.left = content.left-frame.left; - contentInsets.top = content.top-frame.top; - contentInsets.right = frame.right-content.right; - contentInsets.bottom = frame.bottom-content.bottom; - - final Rect visibleInsets = mVisibleInsets; - visibleInsets.left = visible.left-frame.left; - visibleInsets.top = visible.top-frame.top; - visibleInsets.right = frame.right-visible.right; - visibleInsets.bottom = frame.bottom-visible.bottom; - - if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) { - updateWallpaperOffsetLocked(this, mDisplay.getWidth(), - mDisplay.getHeight(), false); - } - - if (localLOGV) { - //if ("com.google.android.youtube".equals(mAttrs.packageName) - // && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { - Slog.v(TAG, "Resolving (mRequestedWidth=" - + mRequestedWidth + ", mRequestedheight=" - + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph - + "): frame=" + mFrame.toShortString() - + " ci=" + contentInsets.toShortString() - + " vi=" + visibleInsets.toShortString()); - //} - } - } - - public Rect getFrameLw() { - return mFrame; - } - - public Rect getShownFrameLw() { - return mShownFrame; - } - - public Rect getDisplayFrameLw() { - return mDisplayFrame; - } - - public Rect getContentFrameLw() { - return mContentFrame; - } - - public Rect getVisibleFrameLw() { - return mVisibleFrame; - } - - public boolean getGivenInsetsPendingLw() { - return mGivenInsetsPending; - } - - public Rect getGivenContentInsetsLw() { - return mGivenContentInsets; - } - - public Rect getGivenVisibleInsetsLw() { - return mGivenVisibleInsets; - } - - public WindowManager.LayoutParams getAttrs() { - return mAttrs; - } - - public int getSurfaceLayer() { - return mLayer; - } - - public IApplicationToken getAppToken() { - return mAppToken != null ? mAppToken.appToken : null; - } - - public long getInputDispatchingTimeoutNanos() { - return mAppToken != null - ? mAppToken.inputDispatchingTimeoutNanos - : DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; - } - - public boolean hasAppShownWindows() { - return mAppToken != null ? mAppToken.firstWindowDrawn : false; - } - - public void setAnimation(Animation anim) { - if (localLOGV) Slog.v( - TAG, "Setting animation in " + this + ": " + anim); - mAnimating = false; - mLocalAnimating = false; - mAnimation = anim; - mAnimation.restrictDuration(MAX_ANIMATION_DURATION); - mAnimation.scaleCurrentDuration(mWindowAnimationScale); - } - - public void clearAnimation() { - if (mAnimation != null) { - mAnimating = true; - mLocalAnimating = false; - mAnimation.cancel(); - mAnimation = null; - } - } - - Surface createSurfaceLocked() { - if (mSurface == null) { - mReportDestroySurface = false; - mSurfacePendingDestroy = false; - mDrawPending = true; - mCommitDrawPending = false; - mReadyToShow = false; - if (mAppToken != null) { - mAppToken.allDrawn = false; - } - - int flags = 0; - - if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) { - flags |= Surface.SECURE; - } - if (DEBUG_VISIBILITY) Slog.v( - TAG, "Creating surface in session " - + mSession.mSurfaceSession + " window " + this - + " w=" + mFrame.width() - + " h=" + mFrame.height() + " format=" - + mAttrs.format + " flags=" + flags); - - int w = mFrame.width(); - int h = mFrame.height(); - if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) { - // for a scaled surface, we always want the requested - // size. - w = mRequestedWidth; - h = mRequestedHeight; - } - - // Something is wrong and SurfaceFlinger will not like this, - // try to revert to sane values - if (w <= 0) w = 1; - if (h <= 0) h = 1; - - mSurfaceShown = false; - mSurfaceLayer = 0; - mSurfaceAlpha = 1; - mSurfaceX = 0; - mSurfaceY = 0; - mSurfaceW = w; - mSurfaceH = h; - try { - final boolean isHwAccelerated = (mAttrs.flags & - WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; - final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : mAttrs.format; - if (isHwAccelerated && mAttrs.format == PixelFormat.OPAQUE) { - flags |= Surface.OPAQUE; - } - mSurface = new Surface( - mSession.mSurfaceSession, mSession.mPid, - mAttrs.getTitle().toString(), - 0, w, h, format, flags); - if (SHOW_TRANSACTIONS) Slog.i(TAG, " CREATE SURFACE " - + mSurface + " IN SESSION " - + mSession.mSurfaceSession - + ": pid=" + mSession.mPid + " format=" - + mAttrs.format + " flags=0x" - + Integer.toHexString(flags) - + " / " + this); - } catch (Surface.OutOfResourcesException e) { - Slog.w(TAG, "OutOfResourcesException creating surface"); - reclaimSomeSurfaceMemoryLocked(this, "create"); - return null; - } catch (Exception e) { - Slog.e(TAG, "Exception creating surface", e); - return null; - } - - if (localLOGV) Slog.v( - TAG, "Got surface: " + mSurface - + ", set left=" + mFrame.left + " top=" + mFrame.top - + ", animLayer=" + mAnimLayer); - if (SHOW_TRANSACTIONS) { - Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked"); - logSurface(this, "CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" + - mFrame.width() + "x" + mFrame.height() + "), layer=" + - mAnimLayer + " HIDE", null); - } - Surface.openTransaction(); - try { - try { - mSurfaceX = mFrame.left + mXOffset; - mSurfaceY = mFrame.top + mYOffset; - mSurface.setPosition(mSurfaceX, mSurfaceY); - mSurfaceLayer = mAnimLayer; - mSurface.setLayer(mAnimLayer); - mSurfaceShown = false; - mSurface.hide(); - if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) { - if (SHOW_TRANSACTIONS) logSurface(this, "DITHER", null); - mSurface.setFlags(Surface.SURFACE_DITHER, - Surface.SURFACE_DITHER); - } - } catch (RuntimeException e) { - Slog.w(TAG, "Error creating surface in " + w, e); - reclaimSomeSurfaceMemoryLocked(this, "create-init"); - } - mLastHidden = true; - } finally { - Surface.closeTransaction(); - if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION createSurfaceLocked"); - } - if (localLOGV) Slog.v( - TAG, "Created surface " + this); - } - return mSurface; - } - - void destroySurfaceLocked() { - if (mAppToken != null && this == mAppToken.startingWindow) { - mAppToken.startingDisplayed = false; - } - - if (mSurface != null) { - mDrawPending = false; - mCommitDrawPending = false; - mReadyToShow = false; - - int i = mChildWindows.size(); - while (i > 0) { - i--; - WindowState c = mChildWindows.get(i); - c.mAttachedHidden = true; - } - - if (mReportDestroySurface) { - mReportDestroySurface = false; - mSurfacePendingDestroy = true; - try { - mClient.dispatchGetNewSurface(); - // We'll really destroy on the next time around. - return; - } catch (RemoteException e) { - } - } - - try { - if (DEBUG_VISIBILITY) { - RuntimeException e = null; - if (!HIDE_STACK_CRAWLS) { - e = new RuntimeException(); - e.fillInStackTrace(); - } - Slog.w(TAG, "Window " + this + " destroying surface " - + mSurface + ", session " + mSession, e); - } - if (SHOW_TRANSACTIONS) { - RuntimeException e = null; - if (!HIDE_STACK_CRAWLS) { - e = new RuntimeException(); - e.fillInStackTrace(); - } - if (SHOW_TRANSACTIONS) logSurface(this, "DESTROY", e); - } - mSurface.destroy(); - } catch (RuntimeException e) { - Slog.w(TAG, "Exception thrown when destroying Window " + this - + " surface " + mSurface + " session " + mSession - + ": " + e.toString()); - } - - mSurfaceShown = false; - mSurface = null; - } - } - - boolean finishDrawingLocked() { - if (mDrawPending) { - if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Slog.v( - TAG, "finishDrawingLocked: " + mSurface); - mCommitDrawPending = true; - mDrawPending = false; - return true; - } - return false; - } - - // This must be called while inside a transaction. - boolean commitFinishDrawingLocked(long currentTime) { - //Slog.i(TAG, "commitFinishDrawingLocked: " + mSurface); - if (!mCommitDrawPending) { - return false; - } - mCommitDrawPending = false; - mReadyToShow = true; - final boolean starting = mAttrs.type == TYPE_APPLICATION_STARTING; - final AppWindowToken atoken = mAppToken; - if (atoken == null || atoken.allDrawn || starting) { - performShowLocked(); - } - return true; - } - - // This must be called while inside a transaction. - boolean performShowLocked() { - if (DEBUG_VISIBILITY) { - RuntimeException e = null; - if (!HIDE_STACK_CRAWLS) { - e = new RuntimeException(); - e.fillInStackTrace(); - } - Slog.v(TAG, "performShow on " + this - + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay() - + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e); - } - if (mReadyToShow && isReadyForDisplay()) { - if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) logSurface(this, - "SHOW (performShowLocked)", null); - if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this - + " during animation: policyVis=" + mPolicyVisibility - + " attHidden=" + mAttachedHidden - + " tok.hiddenRequested=" - + (mAppToken != null ? mAppToken.hiddenRequested : false) - + " tok.hidden=" - + (mAppToken != null ? mAppToken.hidden : false) - + " animating=" + mAnimating - + " tok animating=" - + (mAppToken != null ? mAppToken.animating : false)); - if (!showSurfaceRobustlyLocked(this)) { - return false; - } - mLastAlpha = -1; - mHasDrawn = true; - mLastHidden = false; - mReadyToShow = false; - enableScreenIfNeededLocked(); - - applyEnterAnimationLocked(this); - - int i = mChildWindows.size(); - while (i > 0) { - i--; - WindowState c = mChildWindows.get(i); - if (c.mAttachedHidden) { - c.mAttachedHidden = false; - if (c.mSurface != null) { - c.performShowLocked(); - // It hadn't been shown, which means layout not - // performed on it, so now we want to make sure to - // do a layout. If called from within the transaction - // loop, this will cause it to restart with a new - // layout. - mLayoutNeeded = true; - } - } - } - - if (mAttrs.type != TYPE_APPLICATION_STARTING - && mAppToken != null) { - mAppToken.firstWindowDrawn = true; - - if (mAppToken.startingData != null) { - if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG, - "Finish starting " + mToken - + ": first real window is shown, no animation"); - // If this initial window is animating, stop it -- we - // will do an animation to reveal it from behind the - // starting window, so there is no need for it to also - // be doing its own stuff. - if (mAnimation != null) { - mAnimation.cancel(); - mAnimation = null; - // Make sure we clean up the animation. - mAnimating = true; - } - mFinishedStarting.add(mAppToken); - mH.sendEmptyMessage(H.FINISHED_STARTING); - } - mAppToken.updateReportedVisibilityLocked(); - } - } - return true; - } - - // This must be called while inside a transaction. Returns true if - // there is more animation to run. - boolean stepAnimationLocked(long currentTime, int dw, int dh) { - if (!mDisplayFrozen && mPolicy.isScreenOn()) { - // We will run animations as long as the display isn't frozen. - - if (!mDrawPending && !mCommitDrawPending && mAnimation != null) { - mHasTransformation = true; - mHasLocalTransformation = true; - if (!mLocalAnimating) { - if (DEBUG_ANIM) Slog.v( - TAG, "Starting animation in " + this + - " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() + - " dw=" + dw + " dh=" + dh + " scale=" + mWindowAnimationScale); - mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh); - mAnimation.setStartTime(currentTime); - mLocalAnimating = true; - mAnimating = true; - } - mTransformation.clear(); - final boolean more = mAnimation.getTransformation( - currentTime, mTransformation); - if (DEBUG_ANIM) Slog.v( - TAG, "Stepped animation in " + this + - ": more=" + more + ", xform=" + mTransformation); - if (more) { - // we're not done! - return true; - } - if (DEBUG_ANIM) Slog.v( - TAG, "Finished animation in " + this + - " @ " + currentTime); - - if (mAnimation != null) { - mAnimation.cancel(); - mAnimation = null; - } - //WindowManagerService.this.dump(); - } - mHasLocalTransformation = false; - if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null - && mAppToken.animation != null) { - // When our app token is animating, we kind-of pretend like - // we are as well. Note the mLocalAnimating mAnimationIsEntrance - // part of this check means that we will only do this if - // our window is not currently exiting, or it is not - // locally animating itself. The idea being that one that - // is exiting and doing a local animation should be removed - // once that animation is done. - mAnimating = true; - mHasTransformation = true; - mTransformation.clear(); - return false; - } else if (mHasTransformation) { - // Little trick to get through the path below to act like - // we have finished an animation. - mAnimating = true; - } else if (isAnimating()) { - mAnimating = true; - } - } else if (mAnimation != null) { - // If the display is frozen, and there is a pending animation, - // clear it and make sure we run the cleanup code. - mAnimating = true; - mLocalAnimating = true; - mAnimation.cancel(); - mAnimation = null; - } - - if (!mAnimating && !mLocalAnimating) { - return false; - } - - if (DEBUG_ANIM) Slog.v( - TAG, "Animation done in " + this + ": exiting=" + mExiting - + ", reportedVisible=" - + (mAppToken != null ? mAppToken.reportedVisible : false)); - - mAnimating = false; - mLocalAnimating = false; - if (mAnimation != null) { - mAnimation.cancel(); - mAnimation = null; - } - mAnimLayer = mLayer; - if (mIsImWindow) { - mAnimLayer += mInputMethodAnimLayerAdjustment; - } else if (mIsWallpaper) { - mAnimLayer += mWallpaperAnimLayerAdjustment; - } - if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this - + " anim layer: " + mAnimLayer); - mHasTransformation = false; - mHasLocalTransformation = false; - if (mPolicyVisibility != mPolicyVisibilityAfterAnim) { - if (DEBUG_VISIBILITY) { - Slog.v(TAG, "Policy visibility changing after anim in " + this + ": " - + mPolicyVisibilityAfterAnim); - } - mPolicyVisibility = mPolicyVisibilityAfterAnim; - if (!mPolicyVisibility) { - if (mCurrentFocus == this) { - mFocusMayChange = true; - } - // Window is no longer visible -- make sure if we were waiting - // for it to be displayed before enabling the display, that - // we allow the display to be enabled now. - enableScreenIfNeededLocked(); - } - } - mTransformation.clear(); - if (mHasDrawn - && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING - && mAppToken != null - && mAppToken.firstWindowDrawn - && mAppToken.startingData != null) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Finish starting " - + mToken + ": first real window done animating"); - mFinishedStarting.add(mAppToken); - mH.sendEmptyMessage(H.FINISHED_STARTING); - } - - finishExit(); - - if (mAppToken != null) { - mAppToken.updateReportedVisibilityLocked(); - } - - return false; - } - - void finishExit() { - if (DEBUG_ANIM) Slog.v( - TAG, "finishExit in " + this - + ": exiting=" + mExiting - + " remove=" + mRemoveOnExit - + " windowAnimating=" + isWindowAnimating()); - - final int N = mChildWindows.size(); - for (int i=0; i<N; i++) { - mChildWindows.get(i).finishExit(); - } - - if (!mExiting) { - return; - } - - if (isWindowAnimating()) { - return; - } - - if (localLOGV) Slog.v( - TAG, "Exit animation finished in " + this - + ": remove=" + mRemoveOnExit); - if (mSurface != null) { - mDestroySurface.add(this); - mDestroying = true; - if (SHOW_TRANSACTIONS) logSurface(this, "HIDE (finishExit)", null); - mSurfaceShown = false; - try { - mSurface.hide(); - } catch (RuntimeException e) { - Slog.w(TAG, "Error hiding surface in " + this, e); - } - mLastHidden = true; - } - mExiting = false; - if (mRemoveOnExit) { - mPendingRemove.add(this); - mRemoveOnExit = false; - } - } - - boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { - if (dsdx < .99999f || dsdx > 1.00001f) return false; - if (dtdy < .99999f || dtdy > 1.00001f) return false; - if (dtdx < -.000001f || dtdx > .000001f) return false; - if (dsdy < -.000001f || dsdy > .000001f) return false; - return true; - } - - void computeShownFrameLocked() { - final boolean selfTransformation = mHasLocalTransformation; - Transformation attachedTransformation = - (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation) - ? mAttachedWindow.mTransformation : null; - Transformation appTransformation = - (mAppToken != null && mAppToken.hasTransformation) - ? mAppToken.transformation : null; - - // Wallpapers are animated based on the "real" window they - // are currently targeting. - if (mAttrs.type == TYPE_WALLPAPER && mLowerWallpaperTarget == null - && mWallpaperTarget != null) { - if (mWallpaperTarget.mHasLocalTransformation && - mWallpaperTarget.mAnimation != null && - !mWallpaperTarget.mAnimation.getDetachWallpaper()) { - attachedTransformation = mWallpaperTarget.mTransformation; - if (DEBUG_WALLPAPER && attachedTransformation != null) { - Slog.v(TAG, "WP target attached xform: " + attachedTransformation); - } - } - if (mWallpaperTarget.mAppToken != null && - mWallpaperTarget.mAppToken.hasTransformation && - mWallpaperTarget.mAppToken.animation != null && - !mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) { - appTransformation = mWallpaperTarget.mAppToken.transformation; - if (DEBUG_WALLPAPER && appTransformation != null) { - Slog.v(TAG, "WP target app xform: " + appTransformation); - } - } - } - - final boolean screenAnimation = mScreenRotationAnimation != null - && mScreenRotationAnimation.isAnimating(); - if (selfTransformation || attachedTransformation != null - || appTransformation != null || screenAnimation) { - // cache often used attributes locally - final Rect frame = mFrame; - final float tmpFloats[] = mTmpFloats; - final Matrix tmpMatrix = mTmpMatrix; - - // Compute the desired transformation. - tmpMatrix.setTranslate(0, 0); - if (selfTransformation) { - tmpMatrix.postConcat(mTransformation.getMatrix()); - } - tmpMatrix.postTranslate(frame.left + mXOffset, frame.top + mYOffset); - if (attachedTransformation != null) { - tmpMatrix.postConcat(attachedTransformation.getMatrix()); - } - if (appTransformation != null) { - tmpMatrix.postConcat(appTransformation.getMatrix()); - } - if (screenAnimation) { - tmpMatrix.postConcat( - mScreenRotationAnimation.getEnterTransformation().getMatrix()); - } - - // "convert" it into SurfaceFlinger's format - // (a 2x2 matrix + an offset) - // Here we must not transform the position of the surface - // since it is already included in the transformation. - //Slog.i(TAG, "Transform: " + matrix); - - mHaveMatrix = true; - tmpMatrix.getValues(tmpFloats); - mDsDx = tmpFloats[Matrix.MSCALE_X]; - mDtDx = tmpFloats[Matrix.MSKEW_Y]; - mDsDy = tmpFloats[Matrix.MSKEW_X]; - mDtDy = tmpFloats[Matrix.MSCALE_Y]; - int x = (int)tmpFloats[Matrix.MTRANS_X]; - int y = (int)tmpFloats[Matrix.MTRANS_Y]; - int w = frame.width(); - int h = frame.height(); - mShownFrame.set(x, y, x+w, y+h); - - // Now set the alpha... but because our current hardware - // can't do alpha transformation on a non-opaque surface, - // turn it off if we are running an animation that is also - // transforming since it is more important to have that - // animation be smooth. - mShownAlpha = mAlpha; - if (!mLimitedAlphaCompositing - || (!PixelFormat.formatHasAlpha(mAttrs.format) - || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) - && x == frame.left && y == frame.top))) { - //Slog.i(TAG, "Applying alpha transform"); - if (selfTransformation) { - mShownAlpha *= mTransformation.getAlpha(); - } - if (attachedTransformation != null) { - mShownAlpha *= attachedTransformation.getAlpha(); - } - if (appTransformation != null) { - mShownAlpha *= appTransformation.getAlpha(); - } - if (screenAnimation) { - mShownAlpha *= - mScreenRotationAnimation.getEnterTransformation().getAlpha(); - } - } else { - //Slog.i(TAG, "Not applying alpha transform"); - } - - if (localLOGV) Slog.v( - TAG, "Continuing animation in " + this + - ": " + mShownFrame + - ", alpha=" + mTransformation.getAlpha()); - return; - } - - mShownFrame.set(mFrame); - if (mXOffset != 0 || mYOffset != 0) { - mShownFrame.offset(mXOffset, mYOffset); - } - mShownAlpha = mAlpha; - mHaveMatrix = false; - mDsDx = 1; - mDtDx = 0; - mDsDy = 0; - mDtDy = 1; - } - - /** - * Is this window visible? It is not visible if there is no - * surface, or we are in the process of running an exit animation - * that will remove the surface, or its app token has been hidden. - */ - public boolean isVisibleLw() { - final AppWindowToken atoken = mAppToken; - return mSurface != null && mPolicyVisibility && !mAttachedHidden - && (atoken == null || !atoken.hiddenRequested) - && !mExiting && !mDestroying; - } - - /** - * Like {@link #isVisibleLw}, but also counts a window that is currently - * "hidden" behind the keyguard as visible. This allows us to apply - * things like window flags that impact the keyguard. - * XXX I am starting to think we need to have ANOTHER visibility flag - * for this "hidden behind keyguard" state rather than overloading - * mPolicyVisibility. Ungh. - */ - public boolean isVisibleOrBehindKeyguardLw() { - final AppWindowToken atoken = mAppToken; - return mSurface != null && !mAttachedHidden - && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested) - && !mDrawPending && !mCommitDrawPending - && !mExiting && !mDestroying; - } - - /** - * Is this window visible, ignoring its app token? It is not visible - * if there is no surface, or we are in the process of running an exit animation - * that will remove the surface. - */ - public boolean isWinVisibleLw() { - final AppWindowToken atoken = mAppToken; - return mSurface != null && mPolicyVisibility && !mAttachedHidden - && (atoken == null || !atoken.hiddenRequested || atoken.animating) - && !mExiting && !mDestroying; - } - - /** - * The same as isVisible(), but follows the current hidden state of - * the associated app token, not the pending requested hidden state. - */ - boolean isVisibleNow() { - return mSurface != null && mPolicyVisibility && !mAttachedHidden - && !mRootToken.hidden && !mExiting && !mDestroying; - } - - /** - * Can this window possibly be a drag/drop target? The test here is - * a combination of the above "visible now" with the check that the - * Input Manager uses when discarding windows from input consideration. - */ - boolean isPotentialDragTarget() { - return isVisibleNow() && (mInputChannel != null) && !mRemoved; - } - - /** - * Same as isVisible(), but we also count it as visible between the - * call to IWindowSession.add() and the first relayout(). - */ - boolean isVisibleOrAdding() { - final AppWindowToken atoken = mAppToken; - return ((mSurface != null && !mReportDestroySurface) - || (!mRelayoutCalled && mViewVisibility == View.VISIBLE)) - && mPolicyVisibility && !mAttachedHidden - && (atoken == null || !atoken.hiddenRequested) - && !mExiting && !mDestroying; - } - - /** - * Is this window currently on-screen? It is on-screen either if it - * is visible or it is currently running an animation before no longer - * being visible. - */ - boolean isOnScreen() { - final AppWindowToken atoken = mAppToken; - if (atoken != null) { - return mSurface != null && mPolicyVisibility && !mDestroying - && ((!mAttachedHidden && !atoken.hiddenRequested) - || mAnimation != null || atoken.animation != null); - } else { - return mSurface != null && mPolicyVisibility && !mDestroying - && (!mAttachedHidden || mAnimation != null); - } - } - - /** - * Like isOnScreen(), but we don't return true if the window is part - * of a transition that has not yet been started. - */ - boolean isReadyForDisplay() { - if (mRootToken.waitingToShow && - mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { - return false; - } - final AppWindowToken atoken = mAppToken; - final boolean animating = atoken != null - ? (atoken.animation != null) : false; - return mSurface != null && mPolicyVisibility && !mDestroying - && ((!mAttachedHidden && mViewVisibility == View.VISIBLE - && !mRootToken.hidden) - || mAnimation != null || animating); - } - - /** Is the window or its container currently animating? */ - boolean isAnimating() { - final WindowState attached = mAttachedWindow; - final AppWindowToken atoken = mAppToken; - return mAnimation != null - || (attached != null && attached.mAnimation != null) - || (atoken != null && - (atoken.animation != null - || atoken.inPendingTransaction)); - } - - /** Is this window currently animating? */ - boolean isWindowAnimating() { - return mAnimation != null; - } - - /** - * Like isOnScreen, but returns false if the surface hasn't yet - * been drawn. - */ - public boolean isDisplayedLw() { - final AppWindowToken atoken = mAppToken; - return mSurface != null && mPolicyVisibility && !mDestroying - && !mDrawPending && !mCommitDrawPending - && ((!mAttachedHidden && - (atoken == null || !atoken.hiddenRequested)) - || mAnimating); - } - - /** - * Returns true if the window has a surface that it has drawn a - * complete UI in to. - */ - public boolean isDrawnLw() { - final AppWindowToken atoken = mAppToken; - return mSurface != null && !mDestroying - && !mDrawPending && !mCommitDrawPending; - } - - /** - * Return true if the window is opaque and fully drawn. This indicates - * it may obscure windows behind it. - */ - boolean isOpaqueDrawn() { - return (mAttrs.format == PixelFormat.OPAQUE - || mAttrs.type == TYPE_WALLPAPER) - && mSurface != null && mAnimation == null - && (mAppToken == null || mAppToken.animation == null) - && !mDrawPending && !mCommitDrawPending; - } - - /** - * Return whether this window is wanting to have a translation - * animation applied to it for an in-progress move. (Only makes - * sense to call from performLayoutAndPlaceSurfacesLockedInner().) - */ - boolean shouldAnimateMove() { - return mContentChanged && !mExiting && !mLastHidden && !mDisplayFrozen - && (mFrame.top != mLastFrame.top - || mFrame.left != mLastFrame.left) - && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove()) - && mPolicy.isScreenOn(); - } - - boolean needsBackgroundFiller(int screenWidth, int screenHeight) { - return - // only if the application is requesting compatible window - (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0 && - // only if it's visible - mHasDrawn && mViewVisibility == View.VISIBLE && - // and only if the application fills the compatible screen - mFrame.left <= mCompatibleScreenFrame.left && - mFrame.top <= mCompatibleScreenFrame.top && - mFrame.right >= mCompatibleScreenFrame.right && - mFrame.bottom >= mCompatibleScreenFrame.bottom; - } - - boolean isFullscreen(int screenWidth, int screenHeight) { - return mFrame.left <= 0 && mFrame.top <= 0 && - mFrame.right >= screenWidth && mFrame.bottom >= screenHeight; - } - - void removeLocked() { - disposeInputChannel(); - - if (mAttachedWindow != null) { - if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing " + this + " from " + mAttachedWindow); - mAttachedWindow.mChildWindows.remove(this); - } - destroySurfaceLocked(); - mSession.windowRemovedLocked(); - try { - mClient.asBinder().unlinkToDeath(mDeathRecipient, 0); - } catch (RuntimeException e) { - // Ignore if it has already been removed (usually because - // we are doing this as part of processing a death note.) - } - } - - void disposeInputChannel() { - if (mInputChannel != null) { - mInputManager.unregisterInputChannel(mInputChannel); - - mInputChannel.dispose(); - mInputChannel = null; - } - } - - private class DeathRecipient implements IBinder.DeathRecipient { - public void binderDied() { - try { - synchronized(mWindowMap) { - WindowState win = windowForClientLocked(mSession, mClient, false); - Slog.i(TAG, "WIN DEATH: " + win); - if (win != null) { - removeWindowLocked(mSession, win); - } - } - } catch (IllegalArgumentException ex) { - // This will happen if the window has already been - // removed. - } - } - } - - /** Returns true if this window desires key events. */ - public final boolean canReceiveKeys() { - return isVisibleOrAdding() - && (mViewVisibility == View.VISIBLE) - && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0); - } - - public boolean hasDrawnLw() { - return mHasDrawn; - } - - public boolean showLw(boolean doAnimation) { - return showLw(doAnimation, true); - } - - boolean showLw(boolean doAnimation, boolean requestAnim) { - if (mPolicyVisibility && mPolicyVisibilityAfterAnim) { - return false; - } - if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this); - if (doAnimation) { - if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility=" - + mPolicyVisibility + " mAnimation=" + mAnimation); - if (mDisplayFrozen || !mPolicy.isScreenOn()) { - doAnimation = false; - } else if (mPolicyVisibility && mAnimation == null) { - // Check for the case where we are currently visible and - // not animating; we do not want to do animation at such a - // point to become visible when we already are. - doAnimation = false; - } - } - mPolicyVisibility = true; - mPolicyVisibilityAfterAnim = true; - if (doAnimation) { - applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true); - } - if (requestAnim) { - requestAnimationLocked(0); - } - return true; - } - - public boolean hideLw(boolean doAnimation) { - return hideLw(doAnimation, true); - } - - boolean hideLw(boolean doAnimation, boolean requestAnim) { - if (doAnimation) { - if (mDisplayFrozen || !mPolicy.isScreenOn()) { - doAnimation = false; - } - } - boolean current = doAnimation ? mPolicyVisibilityAfterAnim - : mPolicyVisibility; - if (!current) { - return false; - } - if (doAnimation) { - applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false); - if (mAnimation == null) { - doAnimation = false; - } - } - if (doAnimation) { - mPolicyVisibilityAfterAnim = false; - } else { - if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this); - mPolicyVisibilityAfterAnim = false; - mPolicyVisibility = false; - // Window is no longer visible -- make sure if we were waiting - // for it to be displayed before enabling the display, that - // we allow the display to be enabled now. - enableScreenIfNeededLocked(); - if (mCurrentFocus == this) { - mFocusMayChange = true; - } - } - if (requestAnim) { - requestAnimationLocked(0); - } - return true; - } - - public void getTouchableRegion(Region outRegion) { - final Rect frame = mFrame; - switch (mTouchableInsets) { - default: - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME: - outRegion.set(frame); - break; - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: { - final Rect inset = mGivenContentInsets; - outRegion.set( - frame.left + inset.left, frame.top + inset.top, - frame.right - inset.right, frame.bottom - inset.bottom); - break; - } - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: { - final Rect inset = mGivenVisibleInsets; - outRegion.set( - frame.left + inset.left, frame.top + inset.top, - frame.right - inset.right, frame.bottom - inset.bottom); - break; - } - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: { - final Region givenTouchableRegion = mGivenTouchableRegion; - outRegion.set(givenTouchableRegion); - outRegion.translate(frame.left, frame.top); - break; - } - } - } - - void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("mSession="); pw.print(mSession); - pw.print(" mClient="); pw.println(mClient.asBinder()); - pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs); - if (mAttachedWindow != null || mLayoutAttached) { - pw.print(prefix); pw.print("mAttachedWindow="); pw.print(mAttachedWindow); - pw.print(" mLayoutAttached="); pw.println(mLayoutAttached); - } - if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) { - pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow); - pw.print(" mIsWallpaper="); pw.print(mIsWallpaper); - pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer); - pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible); - } - pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer); - pw.print(" mSubLayer="); pw.print(mSubLayer); - pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+"); - pw.print((mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment - : (mAppToken != null ? mAppToken.animLayerAdjustment : 0))); - pw.print("="); pw.print(mAnimLayer); - pw.print(" mLastLayer="); pw.println(mLastLayer); - if (mSurface != null) { - pw.print(prefix); pw.print("mSurface="); pw.println(mSurface); - pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown); - pw.print(" layer="); pw.print(mSurfaceLayer); - pw.print(" alpha="); pw.print(mSurfaceAlpha); - pw.print(" rect=("); pw.print(mSurfaceX); - pw.print(","); pw.print(mSurfaceY); - pw.print(") "); pw.print(mSurfaceW); - pw.print(" x "); pw.println(mSurfaceH); - } - pw.print(prefix); pw.print("mToken="); pw.println(mToken); - pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken); - if (mAppToken != null) { - pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken); - } - if (mTargetAppToken != null) { - pw.print(prefix); pw.print("mTargetAppToken="); pw.println(mTargetAppToken); - } - pw.print(prefix); pw.print("mViewVisibility=0x"); - pw.print(Integer.toHexString(mViewVisibility)); - pw.print(" mLastHidden="); pw.print(mLastHidden); - pw.print(" mHaveFrame="); pw.print(mHaveFrame); - pw.print(" mObscured="); pw.println(mObscured); - if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || mAttachedHidden) { - pw.print(prefix); pw.print("mPolicyVisibility="); - pw.print(mPolicyVisibility); - pw.print(" mPolicyVisibilityAfterAnim="); - pw.print(mPolicyVisibilityAfterAnim); - pw.print(" mAttachedHidden="); pw.println(mAttachedHidden); - } - if (!mRelayoutCalled) { - pw.print(prefix); pw.print("mRelayoutCalled="); pw.println(mRelayoutCalled); - } - pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth); - pw.print(" h="); pw.print(mRequestedHeight); - pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); - if (mXOffset != 0 || mYOffset != 0) { - pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset); - pw.print(" y="); pw.println(mYOffset); - } - pw.print(prefix); pw.print("mGivenContentInsets="); - mGivenContentInsets.printShortString(pw); - pw.print(" mGivenVisibleInsets="); - mGivenVisibleInsets.printShortString(pw); - pw.println(); - if (mTouchableInsets != 0 || mGivenInsetsPending) { - pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets); - pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending); - } - pw.print(prefix); pw.print("mConfiguration="); pw.println(mConfiguration); - pw.print(prefix); pw.print("mShownFrame="); - mShownFrame.printShortString(pw); - pw.print(" last="); mLastShownFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw); - pw.print(" last="); mLastFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print("mContainingFrame="); - mContainingFrame.printShortString(pw); - pw.print(" mParentFrame="); - mParentFrame.printShortString(pw); - pw.print(" mDisplayFrame="); - mDisplayFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print("mContentFrame="); mContentFrame.printShortString(pw); - pw.print(" mVisibleFrame="); mVisibleFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print("mContentInsets="); mContentInsets.printShortString(pw); - pw.print(" last="); mLastContentInsets.printShortString(pw); - pw.print(" mVisibleInsets="); mVisibleInsets.printShortString(pw); - pw.print(" last="); mLastVisibleInsets.printShortString(pw); - pw.println(); - if (mAnimating || mLocalAnimating || mAnimationIsEntrance - || mAnimation != null) { - pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating); - pw.print(" mLocalAnimating="); pw.print(mLocalAnimating); - pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance); - pw.print(" mAnimation="); pw.println(mAnimation); - } - if (mHasTransformation || mHasLocalTransformation) { - pw.print(prefix); pw.print("XForm: has="); - pw.print(mHasTransformation); - pw.print(" hasLocal="); pw.print(mHasLocalTransformation); - pw.print(" "); mTransformation.printShortString(pw); - pw.println(); - } - if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) { - pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha); - pw.print(" mAlpha="); pw.print(mAlpha); - pw.print(" mLastAlpha="); pw.println(mLastAlpha); - } - if (mHaveMatrix) { - pw.print(prefix); pw.print("mDsDx="); pw.print(mDsDx); - pw.print(" mDtDx="); pw.print(mDtDx); - pw.print(" mDsDy="); pw.print(mDsDy); - pw.print(" mDtDy="); pw.println(mDtDy); - } - pw.print(prefix); pw.print("mDrawPending="); pw.print(mDrawPending); - pw.print(" mCommitDrawPending="); pw.print(mCommitDrawPending); - pw.print(" mReadyToShow="); pw.print(mReadyToShow); - pw.print(" mHasDrawn="); pw.println(mHasDrawn); - if (mExiting || mRemoveOnExit || mDestroying || mRemoved) { - pw.print(prefix); pw.print("mExiting="); pw.print(mExiting); - pw.print(" mRemoveOnExit="); pw.print(mRemoveOnExit); - pw.print(" mDestroying="); pw.print(mDestroying); - pw.print(" mRemoved="); pw.println(mRemoved); - } - if (mOrientationChanging || mAppFreezing || mTurnOnScreen) { - pw.print(prefix); pw.print("mOrientationChanging="); - pw.print(mOrientationChanging); - pw.print(" mAppFreezing="); pw.print(mAppFreezing); - pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen); - } - if (mHScale != 1 || mVScale != 1) { - pw.print(prefix); pw.print("mHScale="); pw.print(mHScale); - pw.print(" mVScale="); pw.println(mVScale); - } - if (mWallpaperX != -1 || mWallpaperY != -1) { - pw.print(prefix); pw.print("mWallpaperX="); pw.print(mWallpaperX); - pw.print(" mWallpaperY="); pw.println(mWallpaperY); - } - if (mWallpaperXStep != -1 || mWallpaperYStep != -1) { - pw.print(prefix); pw.print("mWallpaperXStep="); pw.print(mWallpaperXStep); - pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep); - } - } - - String makeInputChannelName() { - return Integer.toHexString(System.identityHashCode(this)) - + " " + mAttrs.getTitle(); - } - - @Override - public String toString() { - if (mStringNameCache == null || mLastTitle != mAttrs.getTitle() - || mWasPaused != mToken.paused) { - mLastTitle = mAttrs.getTitle(); - mWasPaused = mToken.paused; - mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this)) - + " " + mLastTitle + " paused=" + mWasPaused + "}"; - } - return mStringNameCache; - } - } - - // ------------------------------------------------------------- - // Window Token State - // ------------------------------------------------------------- - - class WindowToken { - // The actual token. - final IBinder token; - - // The type of window this token is for, as per WindowManager.LayoutParams. - final int windowType; - - // Set if this token was explicitly added by a client, so should - // not be removed when all windows are removed. - final boolean explicit; - - // For printing. - String stringName; - - // If this is an AppWindowToken, this is non-null. - AppWindowToken appWindowToken; - - // All of the windows associated with this token. - final ArrayList<WindowState> windows = new ArrayList<WindowState>(); - - // Is key dispatching paused for this token? - boolean paused = false; - - // Should this token's windows be hidden? - boolean hidden; - - // Temporary for finding which tokens no longer have visible windows. - boolean hasVisible; - - // Set to true when this token is in a pending transaction where it - // will be shown. - boolean waitingToShow; - - // Set to true when this token is in a pending transaction where it - // will be hidden. - boolean waitingToHide; - - // Set to true when this token is in a pending transaction where its - // windows will be put to the bottom of the list. - boolean sendingToBottom; - - // Set to true when this token is in a pending transaction where its - // windows will be put to the top of the list. - boolean sendingToTop; - - WindowToken(IBinder _token, int type, boolean _explicit) { - token = _token; - windowType = type; - explicit = _explicit; - } - - void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("token="); pw.println(token); - pw.print(prefix); pw.print("windows="); pw.println(windows); - pw.print(prefix); pw.print("windowType="); pw.print(windowType); - pw.print(" hidden="); pw.print(hidden); - pw.print(" hasVisible="); pw.println(hasVisible); - if (waitingToShow || waitingToHide || sendingToBottom || sendingToTop) { - pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow); - pw.print(" waitingToHide="); pw.print(waitingToHide); - pw.print(" sendingToBottom="); pw.print(sendingToBottom); - pw.print(" sendingToTop="); pw.println(sendingToTop); - } - } - - @Override - public String toString() { - if (stringName == null) { - StringBuilder sb = new StringBuilder(); - sb.append("WindowToken{"); - sb.append(Integer.toHexString(System.identityHashCode(this))); - sb.append(" token="); sb.append(token); sb.append('}'); - stringName = sb.toString(); - } - return stringName; - } - }; - - class AppWindowToken extends WindowToken { - // Non-null only for application tokens. - final IApplicationToken appToken; - - // All of the windows and child windows that are included in this - // application token. Note this list is NOT sorted! - final ArrayList<WindowState> allAppWindows = new ArrayList<WindowState>(); - - int groupId = -1; - boolean appFullscreen; - int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - - // The input dispatching timeout for this application token in nanoseconds. - long inputDispatchingTimeoutNanos; - - // These are used for determining when all windows associated with - // an activity have been drawn, so they can be made visible together - // at the same time. - int lastTransactionSequence = mTransactionSequence-1; - int numInterestingWindows; - int numDrawnWindows; - boolean inPendingTransaction; - boolean allDrawn; - - // Is this token going to be hidden in a little while? If so, it - // won't be taken into account for setting the screen orientation. - boolean willBeHidden; - - // Is this window's surface needed? This is almost like hidden, except - // it will sometimes be true a little earlier: when the token has - // been shown, but is still waiting for its app transition to execute - // before making its windows shown. - boolean hiddenRequested; - - // Have we told the window clients to hide themselves? - boolean clientHidden; - - // Last visibility state we reported to the app token. - boolean reportedVisible; - - // Set to true when the token has been removed from the window mgr. - boolean removed; - - // Have we been asked to have this token keep the screen frozen? - boolean freezingScreen; - - boolean animating; - Animation animation; - boolean hasTransformation; - final Transformation transformation = new Transformation(); - - // Offset to the window of all layers in the token, for use by - // AppWindowToken animations. - int animLayerAdjustment; - - // Information about an application starting window if displayed. - StartingData startingData; - WindowState startingWindow; - View startingView; - boolean startingDisplayed; - boolean startingMoved; - boolean firstWindowDrawn; - - // Input application handle used by the input dispatcher. - InputApplicationHandle mInputApplicationHandle; - - AppWindowToken(IApplicationToken _token) { - super(_token.asBinder(), - WindowManager.LayoutParams.TYPE_APPLICATION, true); - appWindowToken = this; - appToken = _token; - mInputApplicationHandle = new InputApplicationHandle(this); - } - - public void setAnimation(Animation anim) { - if (localLOGV) Slog.v( - TAG, "Setting animation in " + this + ": " + anim); - animation = anim; - animating = false; - anim.restrictDuration(MAX_ANIMATION_DURATION); - anim.scaleCurrentDuration(mTransitionAnimationScale); - int zorder = anim.getZAdjustment(); - int adj = 0; - if (zorder == Animation.ZORDER_TOP) { - adj = TYPE_LAYER_OFFSET; - } else if (zorder == Animation.ZORDER_BOTTOM) { - adj = -TYPE_LAYER_OFFSET; - } - - if (animLayerAdjustment != adj) { - animLayerAdjustment = adj; - updateLayers(); - } - } - - public void setDummyAnimation() { - if (animation == null) { - if (localLOGV) Slog.v( - TAG, "Setting dummy animation in " + this); - animation = sDummyAnimation; - } - } - - public void clearAnimation() { - if (animation != null) { - animation = null; - animating = true; - } - } - - void updateLayers() { - final int N = allAppWindows.size(); - final int adj = animLayerAdjustment; - for (int i=0; i<N; i++) { - WindowState w = allAppWindows.get(i); - w.mAnimLayer = w.mLayer + adj; - if (DEBUG_LAYERS) Slog.v(TAG, "Updating layer " + w + ": " - + w.mAnimLayer); - if (w == mInputMethodTarget && !mInputMethodTargetWaitingAnim) { - setInputMethodAnimLayerAdjustment(adj); - } - if (w == mWallpaperTarget && mLowerWallpaperTarget == null) { - setWallpaperAnimLayerAdjustmentLocked(adj); - } - } - } - - void sendAppVisibilityToClients() { - final int N = allAppWindows.size(); - for (int i=0; i<N; i++) { - WindowState win = allAppWindows.get(i); - if (win == startingWindow && clientHidden) { - // Don't hide the starting window. - continue; - } - try { - if (DEBUG_VISIBILITY) Slog.v(TAG, - "Setting visibility of " + win + ": " + (!clientHidden)); - win.mClient.dispatchAppVisibility(!clientHidden); - } catch (RemoteException e) { - } - } - } - - void showAllWindowsLocked() { - final int NW = allAppWindows.size(); - for (int i=0; i<NW; i++) { - WindowState w = allAppWindows.get(i); - if (DEBUG_VISIBILITY) Slog.v(TAG, - "performing show on: " + w); - w.performShowLocked(); - } - } - - // This must be called while inside a transaction. - boolean stepAnimationLocked(long currentTime, int dw, int dh) { - if (!mDisplayFrozen && mPolicy.isScreenOn()) { - // We will run animations as long as the display isn't frozen. - - if (animation == sDummyAnimation) { - // This guy is going to animate, but not yet. For now count - // it as not animating for purposes of scheduling transactions; - // when it is really time to animate, this will be set to - // a real animation and the next call will execute normally. - return false; - } - - if ((allDrawn || animating || startingDisplayed) && animation != null) { - if (!animating) { - if (DEBUG_ANIM) Slog.v( - TAG, "Starting animation in " + this + - " @ " + currentTime + ": dw=" + dw + " dh=" + dh - + " scale=" + mTransitionAnimationScale - + " allDrawn=" + allDrawn + " animating=" + animating); - animation.initialize(dw, dh, dw, dh); - animation.setStartTime(currentTime); - animating = true; - } - transformation.clear(); - final boolean more = animation.getTransformation( - currentTime, transformation); - if (DEBUG_ANIM) Slog.v( - TAG, "Stepped animation in " + this + - ": more=" + more + ", xform=" + transformation); - if (more) { - // we're done! - hasTransformation = true; - return true; - } - if (DEBUG_ANIM) Slog.v( - TAG, "Finished animation in " + this + - " @ " + currentTime); - animation = null; - } - } else if (animation != null) { - // If the display is frozen, and there is a pending animation, - // clear it and make sure we run the cleanup code. - animating = true; - animation = null; - } - - hasTransformation = false; - - if (!animating) { - return false; - } - - clearAnimation(); - animating = false; - if (animLayerAdjustment != 0) { - animLayerAdjustment = 0; - updateLayers(); - } - if (mInputMethodTarget != null && mInputMethodTarget.mAppToken == this) { - moveInputMethodWindowsIfNeededLocked(true); - } - - if (DEBUG_ANIM) Slog.v( - TAG, "Animation done in " + this - + ": reportedVisible=" + reportedVisible); - - transformation.clear(); - - final int N = windows.size(); - for (int i=0; i<N; i++) { - windows.get(i).finishExit(); - } - updateReportedVisibilityLocked(); - - return false; - } - - void updateReportedVisibilityLocked() { - if (appToken == null) { - return; - } - - int numInteresting = 0; - int numVisible = 0; - boolean nowGone = true; - - if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this); - final int N = allAppWindows.size(); - for (int i=0; i<N; i++) { - WindowState win = allAppWindows.get(i); - if (win == startingWindow || win.mAppFreezing - || win.mViewVisibility != View.VISIBLE - || win.mAttrs.type == TYPE_APPLICATION_STARTING - || win.mDestroying) { - continue; - } - if (DEBUG_VISIBILITY) { - Slog.v(TAG, "Win " + win + ": isDrawn=" - + win.isDrawnLw() - + ", isAnimating=" + win.isAnimating()); - if (!win.isDrawnLw()) { - Slog.v(TAG, "Not displayed: s=" + win.mSurface - + " pv=" + win.mPolicyVisibility - + " dp=" + win.mDrawPending - + " cdp=" + win.mCommitDrawPending - + " ah=" + win.mAttachedHidden - + " th=" - + (win.mAppToken != null - ? win.mAppToken.hiddenRequested : false) - + " a=" + win.mAnimating); - } - } - numInteresting++; - if (win.isDrawnLw()) { - if (!win.isAnimating()) { - numVisible++; - } - nowGone = false; - } else if (win.isAnimating()) { - nowGone = false; - } - } - - boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting; - if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting=" - + numInteresting + " visible=" + numVisible); - if (nowVisible != reportedVisible) { - if (DEBUG_VISIBILITY) Slog.v( - TAG, "Visibility changed in " + this - + ": vis=" + nowVisible); - reportedVisible = nowVisible; - Message m = mH.obtainMessage( - H.REPORT_APPLICATION_TOKEN_WINDOWS, - nowVisible ? 1 : 0, - nowGone ? 1 : 0, - this); - mH.sendMessage(m); - } - } - - WindowState findMainWindow() { - int j = windows.size(); - while (j > 0) { - j--; - WindowState win = windows.get(j); - if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION - || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) { - return win; - } - } - return null; - } - - void dump(PrintWriter pw, String prefix) { - super.dump(pw, prefix); - if (appToken != null) { - pw.print(prefix); pw.println("app=true"); - } - if (allAppWindows.size() > 0) { - pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows); - } - pw.print(prefix); pw.print("groupId="); pw.print(groupId); - pw.print(" appFullscreen="); pw.print(appFullscreen); - pw.print(" requestedOrientation="); pw.println(requestedOrientation); - pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested); - pw.print(" clientHidden="); pw.print(clientHidden); - pw.print(" willBeHidden="); pw.print(willBeHidden); - pw.print(" reportedVisible="); pw.println(reportedVisible); - if (paused || freezingScreen) { - pw.print(prefix); pw.print("paused="); pw.print(paused); - pw.print(" freezingScreen="); pw.println(freezingScreen); - } - if (numInterestingWindows != 0 || numDrawnWindows != 0 - || inPendingTransaction || allDrawn) { - pw.print(prefix); pw.print("numInterestingWindows="); - pw.print(numInterestingWindows); - pw.print(" numDrawnWindows="); pw.print(numDrawnWindows); - pw.print(" inPendingTransaction="); pw.print(inPendingTransaction); - pw.print(" allDrawn="); pw.println(allDrawn); - } - if (animating || animation != null) { - pw.print(prefix); pw.print("animating="); pw.print(animating); - pw.print(" animation="); pw.println(animation); - } - if (hasTransformation) { - pw.print(prefix); pw.print("XForm: "); - transformation.printShortString(pw); - pw.println(); - } - if (animLayerAdjustment != 0) { - pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment); - } - if (startingData != null || removed || firstWindowDrawn) { - pw.print(prefix); pw.print("startingData="); pw.print(startingData); - pw.print(" removed="); pw.print(removed); - pw.print(" firstWindowDrawn="); pw.println(firstWindowDrawn); - } - if (startingWindow != null || startingView != null - || startingDisplayed || startingMoved) { - pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow); - pw.print(" startingView="); pw.print(startingView); - pw.print(" startingDisplayed="); pw.print(startingDisplayed); - pw.print(" startingMoved"); pw.println(startingMoved); - } - } - - @Override - public String toString() { - if (stringName == null) { - StringBuilder sb = new StringBuilder(); - sb.append("AppWindowToken{"); - sb.append(Integer.toHexString(System.identityHashCode(this))); - sb.append(" token="); sb.append(token); sb.append('}'); - stringName = sb.toString(); - } - return stringName; - } - } - - // ------------------------------------------------------------- - // DummyAnimation - // ------------------------------------------------------------- - // This is an animation that does nothing: it just immediately finishes // itself every time it is called. It is used as a stub animation in cases // where we want to synchronize multiple things that may be animating. @@ -8916,26 +5837,7 @@ public class WindowManagerService extends IWindowManager.Stub // Async Handler // ------------------------------------------------------------- - static final class StartingData { - final String pkg; - final int theme; - final CharSequence nonLocalizedLabel; - final int labelRes; - final int icon; - final int windowFlags; - - StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel, - int _labelRes, int _icon, int _windowFlags) { - pkg = _pkg; - theme = _theme; - nonLocalizedLabel = _nonLocalizedLabel; - labelRes = _labelRes; - icon = _icon; - windowFlags = _windowFlags; - } - } - - private final class H extends Handler { + final class H extends Handler { public static final int REPORT_FOCUS_CHANGE = 2; public static final int REPORT_LOSING_FOCUS = 3; public static final int ANIMATE = 4; @@ -9345,7 +6247,7 @@ public class WindowManagerService extends IWindowManager.Stub IInputContext inputContext) { if (client == null) throw new IllegalArgumentException("null client"); if (inputContext == null) throw new IllegalArgumentException("null inputContext"); - Session session = new Session(client, inputContext); + Session session = new Session(this, client, inputContext); return session; } @@ -11479,148 +8381,6 @@ public class WindowManagerService extends IWindowManager.Stub return val; } - static class Watermark { - final String[] mTokens; - final String mText; - final Paint mTextPaint; - final int mTextWidth; - final int mTextHeight; - final int mTextAscent; - final int mTextDescent; - final int mDeltaX; - final int mDeltaY; - - Surface mSurface; - int mLastDW; - int mLastDH; - boolean mDrawNeeded; - - Watermark(Display display, SurfaceSession session, String[] tokens) { - final DisplayMetrics dm = new DisplayMetrics(); - display.getMetrics(dm); - - if (false) { - Log.i(TAG, "*********************** WATERMARK"); - for (int i=0; i<tokens.length; i++) { - Log.i(TAG, " TOKEN #" + i + ": " + tokens[i]); - } - } - - mTokens = tokens; - - StringBuilder builder = new StringBuilder(32); - int len = mTokens[0].length(); - len = len & ~1; - for (int i=0; i<len; i+=2) { - int c1 = mTokens[0].charAt(i); - int c2 = mTokens[0].charAt(i+1); - if (c1 >= 'a' && c1 <= 'f') c1 = c1 - 'a' + 10; - else if (c1 >= 'A' && c1 <= 'F') c1 = c1 - 'A' + 10; - else c1 -= '0'; - if (c2 >= 'a' && c2 <= 'f') c2 = c2 - 'a' + 10; - else if (c2 >= 'A' && c2 <= 'F') c2 = c2 - 'A' + 10; - else c2 -= '0'; - builder.append((char)(255-((c1*16)+c2))); - } - mText = builder.toString(); - if (false) { - Log.i(TAG, "Final text: " + mText); - } - - int fontSize = getPropertyInt(tokens, 1, - TypedValue.COMPLEX_UNIT_DIP, 20, dm); - - mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mTextPaint.setTextSize(fontSize); - mTextPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD)); - - FontMetricsInt fm = mTextPaint.getFontMetricsInt(); - mTextWidth = (int)mTextPaint.measureText(mText); - mTextAscent = fm.ascent; - mTextDescent = fm.descent; - mTextHeight = fm.descent - fm.ascent; - - mDeltaX = getPropertyInt(tokens, 2, - TypedValue.COMPLEX_UNIT_PX, mTextWidth*2, dm); - mDeltaY = getPropertyInt(tokens, 3, - TypedValue.COMPLEX_UNIT_PX, mTextHeight*3, dm); - int shadowColor = getPropertyInt(tokens, 4, - TypedValue.COMPLEX_UNIT_PX, 0xb0000000, dm); - int color = getPropertyInt(tokens, 5, - TypedValue.COMPLEX_UNIT_PX, 0x60ffffff, dm); - int shadowRadius = getPropertyInt(tokens, 6, - TypedValue.COMPLEX_UNIT_PX, 7, dm); - int shadowDx = getPropertyInt(tokens, 8, - TypedValue.COMPLEX_UNIT_PX, 0, dm); - int shadowDy = getPropertyInt(tokens, 9, - TypedValue.COMPLEX_UNIT_PX, 0, dm); - - mTextPaint.setColor(color); - mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor); - - try { - mSurface = new Surface(session, 0, - "WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0); - mSurface.setLayer(TYPE_LAYER_MULTIPLIER*100); - mSurface.setPosition(0, 0); - mSurface.show(); - } catch (OutOfResourcesException e) { - } - } - - void positionSurface(int dw, int dh) { - if (mLastDW != dw || mLastDH != dh) { - mLastDW = dw; - mLastDH = dh; - mSurface.setSize(dw, dh); - mDrawNeeded = true; - } - } - - void drawIfNeeded() { - if (mDrawNeeded) { - final int dw = mLastDW; - final int dh = mLastDH; - - mDrawNeeded = false; - Rect dirty = new Rect(0, 0, dw, dh); - Canvas c = null; - try { - c = mSurface.lockCanvas(dirty); - } catch (IllegalArgumentException e) { - } catch (OutOfResourcesException e) { - } - if (c != null) { - c.drawColor(0, PorterDuff.Mode.CLEAR); - - int deltaX = mDeltaX; - int deltaY = mDeltaY; - - // deltaX shouldn't be close to a round fraction of our - // x step, or else things will line up too much. - int div = (dw+mTextWidth)/deltaX; - int rem = (dw+mTextWidth) - (div*deltaX); - int qdelta = deltaX/4; - if (rem < qdelta || rem > (deltaX-qdelta)) { - deltaX += deltaX/3; - } - - int y = -mTextHeight; - int x = -mTextWidth; - while (y < (dh+mTextHeight)) { - c.drawText(mText, x, y, mTextPaint); - x += deltaX; - if (x >= dw) { - x -= (dw+mTextWidth); - y += deltaY; - } - } - mSurface.unlockCanvasAndPost(c); - } - } - } - } - void createWatermark() { if (mWatermark != null) { return; @@ -11902,190 +8662,6 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mKeyguardTokenWatcher) { } } - /** - * DimAnimator class that controls the dim animation. This holds the surface and - * all state used for dim animation. - */ - private static class DimAnimator { - Surface mDimSurface; - boolean mDimShown = false; - float mDimCurrentAlpha; - float mDimTargetAlpha; - float mDimDeltaPerMs; - long mLastDimAnimTime; - - int mLastDimWidth, mLastDimHeight; - - DimAnimator (SurfaceSession session) { - if (mDimSurface == null) { - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " - + mDimSurface + ": CREATE"); - try { - mDimSurface = new Surface(session, 0, - "DimSurface", - -1, 16, 16, PixelFormat.OPAQUE, - Surface.FX_SURFACE_DIM); - mDimSurface.setAlpha(0.0f); - } catch (Exception e) { - Slog.e(TAG, "Exception creating Dim surface", e); - } - } - } - - /** - * Show the dim surface. - */ - void show(int dw, int dh) { - if (!mDimShown) { - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" + - dw + "x" + dh + ")"); - mDimShown = true; - try { - mLastDimWidth = dw; - mLastDimHeight = dh; - mDimSurface.setPosition(0, 0); - mDimSurface.setSize(dw, dh); - mDimSurface.show(); - } catch (RuntimeException e) { - Slog.w(TAG, "Failure showing dim surface", e); - } - } else if (mLastDimWidth != dw || mLastDimHeight != dh) { - mLastDimWidth = dw; - mLastDimHeight = dh; - mDimSurface.setSize(dw, dh); - } - } - - /** - * Set's the dim surface's layer and update dim parameters that will be used in - * {@link updateSurface} after all windows are examined. - */ - void updateParameters(Resources res, WindowState w, long currentTime) { - mDimSurface.setLayer(w.mAnimLayer-1); - - final float target = w.mExiting ? 0 : w.mAttrs.dimAmount; - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface - + ": layer=" + (w.mAnimLayer-1) + " target=" + target); - if (mDimTargetAlpha != target) { - // If the desired dim level has changed, then - // start an animation to it. - mLastDimAnimTime = currentTime; - long duration = (w.mAnimating && w.mAnimation != null) - ? w.mAnimation.computeDurationHint() - : DEFAULT_DIM_DURATION; - if (target > mDimTargetAlpha) { - TypedValue tv = new TypedValue(); - res.getValue(com.android.internal.R.fraction.config_dimBehindFadeDuration, - tv, true); - if (tv.type == TypedValue.TYPE_FRACTION) { - duration = (long)tv.getFraction((float)duration, (float)duration); - } else if (tv.type >= TypedValue.TYPE_FIRST_INT - && tv.type <= TypedValue.TYPE_LAST_INT) { - duration = tv.data; - } - } - if (duration < 1) { - // Don't divide by zero - duration = 1; - } - mDimTargetAlpha = target; - mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha) / duration; - } - } - - /** - * Updating the surface's alpha. Returns true if the animation continues, or returns - * false when the animation is finished and the dim surface is hidden. - */ - boolean updateSurface(boolean dimming, long currentTime, boolean displayFrozen) { - if (!dimming) { - if (mDimTargetAlpha != 0) { - mLastDimAnimTime = currentTime; - mDimTargetAlpha = 0; - mDimDeltaPerMs = (-mDimCurrentAlpha) / DEFAULT_DIM_DURATION; - } - } - - boolean animating = false; - if (mLastDimAnimTime != 0) { - mDimCurrentAlpha += mDimDeltaPerMs - * (currentTime-mLastDimAnimTime); - boolean more = true; - if (displayFrozen) { - // If the display is frozen, there is no reason to animate. - more = false; - } else if (mDimDeltaPerMs > 0) { - if (mDimCurrentAlpha > mDimTargetAlpha) { - more = false; - } - } else if (mDimDeltaPerMs < 0) { - if (mDimCurrentAlpha < mDimTargetAlpha) { - more = false; - } - } else { - more = false; - } - - // Do we need to continue animating? - if (more) { - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " - + mDimSurface + ": alpha=" + mDimCurrentAlpha); - mLastDimAnimTime = currentTime; - mDimSurface.setAlpha(mDimCurrentAlpha); - animating = true; - } else { - mDimCurrentAlpha = mDimTargetAlpha; - mLastDimAnimTime = 0; - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " - + mDimSurface + ": final alpha=" + mDimCurrentAlpha); - mDimSurface.setAlpha(mDimCurrentAlpha); - if (!dimming) { - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface - + ": HIDE"); - try { - mDimSurface.hide(); - } catch (RuntimeException e) { - Slog.w(TAG, "Illegal argument exception hiding dim surface"); - } - mDimShown = false; - } - } - } - return animating; - } - - public void printTo(PrintWriter pw) { - pw.print(" mDimShown="); pw.print(mDimShown); - pw.print(" current="); pw.print(mDimCurrentAlpha); - pw.print(" target="); pw.print(mDimTargetAlpha); - pw.print(" delta="); pw.print(mDimDeltaPerMs); - pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime); - } - } - - /** - * Animation that fade in after 0.5 interpolate time, or fade out in reverse order. - * This is used for opening/closing transition for apps in compatible mode. - */ - private static class FadeInOutAnimation extends Animation { - boolean mFadeIn; - - public FadeInOutAnimation(boolean fadeIn) { - setInterpolator(new AccelerateInterpolator()); - setDuration(DEFAULT_FADE_IN_OUT_DURATION); - mFadeIn = fadeIn; - } - - @Override - protected void applyTransformation(float interpolatedTime, Transformation t) { - float x = interpolatedTime; - if (!mFadeIn) { - x = 1.0f - x; // reverse the interpolation for fade out - } - t.setAlpha(x); - } - } - public interface OnHardKeyboardStatusChangeListener { public void onHardKeyboardStatusChange(boolean available, boolean enabled); } diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java new file mode 100644 index 0000000..d0eec89 --- /dev/null +++ b/services/java/com/android/server/wm/WindowState.java @@ -0,0 +1,1623 @@ +/* + * 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.wm; + +import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; +import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; + +import com.android.server.wm.WindowManagerService.H; + +import android.content.res.Configuration; +import android.graphics.Matrix; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; +import android.view.Gravity; +import android.view.IApplicationToken; +import android.view.IWindow; +import android.view.InputChannel; +import android.view.Surface; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.view.WindowManagerPolicy; +import android.view.WindowManager.LayoutParams; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * A window in the window manager. + */ +final class WindowState implements WindowManagerPolicy.WindowState { + final WindowManagerService mService; + final Session mSession; + final IWindow mClient; + WindowToken mToken; + WindowToken mRootToken; + AppWindowToken mAppToken; + AppWindowToken mTargetAppToken; + final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams(); + final DeathRecipient mDeathRecipient; + final WindowState mAttachedWindow; + final ArrayList<WindowState> mChildWindows = new ArrayList<WindowState>(); + final int mBaseLayer; + final int mSubLayer; + final boolean mLayoutAttached; + final boolean mIsImWindow; + final boolean mIsWallpaper; + final boolean mIsFloatingLayer; + int mViewVisibility; + boolean mPolicyVisibility = true; + boolean mPolicyVisibilityAfterAnim = true; + boolean mAppFreezing; + Surface mSurface; + boolean mReportDestroySurface; + boolean mSurfacePendingDestroy; + boolean mAttachedHidden; // is our parent window hidden? + boolean mLastHidden; // was this window last hidden? + boolean mWallpaperVisible; // for wallpaper, what was last vis report? + int mRequestedWidth; + int mRequestedHeight; + int mLastRequestedWidth; + int mLastRequestedHeight; + int mLayer; + int mAnimLayer; + int mLastLayer; + boolean mHaveFrame; + boolean mObscured; + boolean mTurnOnScreen; + + int mLayoutSeq = -1; + + Configuration mConfiguration = null; + + // Actual frame shown on-screen (may be modified by animation) + final Rect mShownFrame = new Rect(); + final Rect mLastShownFrame = new Rect(); + + /** + * Set when we have changed the size of the surface, to know that + * we must tell them application to resize (and thus redraw itself). + */ + boolean mSurfaceResized; + + /** + * Insets that determine the actually visible area + */ + final Rect mVisibleInsets = new Rect(); + final Rect mLastVisibleInsets = new Rect(); + boolean mVisibleInsetsChanged; + + /** + * Insets that are covered by system windows + */ + final Rect mContentInsets = new Rect(); + final Rect mLastContentInsets = new Rect(); + boolean mContentInsetsChanged; + + /** + * Set to true if we are waiting for this window to receive its + * given internal insets before laying out other windows based on it. + */ + boolean mGivenInsetsPending; + + /** + * These are the content insets that were given during layout for + * this window, to be applied to windows behind it. + */ + final Rect mGivenContentInsets = new Rect(); + + /** + * These are the visible insets that were given during layout for + * this window, to be applied to windows behind it. + */ + final Rect mGivenVisibleInsets = new Rect(); + + /** + * This is the given touchable area relative to the window frame, or null if none. + */ + final Region mGivenTouchableRegion = new Region(); + + /** + * Flag indicating whether the touchable region should be adjusted by + * the visible insets; if false the area outside the visible insets is + * NOT touchable, so we must use those to adjust the frame during hit + * tests. + */ + int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; + + // Current transformation being applied. + boolean mHaveMatrix; + float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1; + float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1; + float mHScale=1, mVScale=1; + float mLastHScale=1, mLastVScale=1; + final Matrix mTmpMatrix = new Matrix(); + + // "Real" frame that the application sees. + final Rect mFrame = new Rect(); + final Rect mLastFrame = new Rect(); + + final Rect mContainingFrame = new Rect(); + final Rect mDisplayFrame = new Rect(); + final Rect mContentFrame = new Rect(); + final Rect mParentFrame = new Rect(); + final Rect mVisibleFrame = new Rect(); + + boolean mContentChanged; + + float mShownAlpha = 1; + float mAlpha = 1; + float mLastAlpha = 1; + + // Set to true if, when the window gets displayed, it should perform + // an enter animation. + boolean mEnterAnimationPending; + + // Currently running animation. + boolean mAnimating; + boolean mLocalAnimating; + Animation mAnimation; + boolean mAnimationIsEntrance; + boolean mHasTransformation; + boolean mHasLocalTransformation; + final Transformation mTransformation = new Transformation(); + + // If a window showing a wallpaper: the requested offset for the + // wallpaper; if a wallpaper window: the currently applied offset. + float mWallpaperX = -1; + float mWallpaperY = -1; + + // If a window showing a wallpaper: what fraction of the offset + // range corresponds to a full virtual screen. + float mWallpaperXStep = -1; + float mWallpaperYStep = -1; + + // Wallpaper windows: pixels offset based on above variables. + int mXOffset; + int mYOffset; + + // This is set after IWindowSession.relayout() has been called at + // least once for the window. It allows us to detect the situation + // where we don't yet have a surface, but should have one soon, so + // we can give the window focus before waiting for the relayout. + boolean mRelayoutCalled; + + // This is set after the Surface has been created but before the + // window has been drawn. During this time the surface is hidden. + boolean mDrawPending; + + // This is set after the window has finished drawing for the first + // time but before its surface is shown. The surface will be + // displayed when the next layout is run. + boolean mCommitDrawPending; + + // This is set during the time after the window's drawing has been + // committed, and before its surface is actually shown. It is used + // to delay showing the surface until all windows in a token are ready + // to be shown. + boolean mReadyToShow; + + // Set when the window has been shown in the screen the first time. + boolean mHasDrawn; + + // Currently running an exit animation? + boolean mExiting; + + // Currently on the mDestroySurface list? + boolean mDestroying; + + // Completely remove from window manager after exit animation? + boolean mRemoveOnExit; + + // Set when the orientation is changing and this window has not yet + // been updated for the new orientation. + boolean mOrientationChanging; + + // Is this window now (or just being) removed? + boolean mRemoved; + + // Temp for keeping track of windows that have been removed when + // rebuilding window list. + boolean mRebuilding; + + // For debugging, this is the last information given to the surface flinger. + boolean mSurfaceShown; + int mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH; + int mSurfaceLayer; + float mSurfaceAlpha; + + // Input channel and input window handle used by the input dispatcher. + InputWindowHandle mInputWindowHandle; + InputChannel mInputChannel; + + // Used to improve performance of toString() + String mStringNameCache; + CharSequence mLastTitle; + boolean mWasPaused; + + WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, + WindowState attachedWindow, WindowManager.LayoutParams a, + int viewVisibility) { + mService = service; + mSession = s; + mClient = c; + mToken = token; + mAttrs.copyFrom(a); + mViewVisibility = viewVisibility; + DeathRecipient deathRecipient = new DeathRecipient(); + mAlpha = a.alpha; + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Window " + this + " client=" + c.asBinder() + + " token=" + token + " (" + mAttrs.token + ")"); + try { + c.asBinder().linkToDeath(deathRecipient, 0); + } catch (RemoteException e) { + mDeathRecipient = null; + mAttachedWindow = null; + mLayoutAttached = false; + mIsImWindow = false; + mIsWallpaper = false; + mIsFloatingLayer = false; + mBaseLayer = 0; + mSubLayer = 0; + return; + } + mDeathRecipient = deathRecipient; + + if ((mAttrs.type >= FIRST_SUB_WINDOW && + mAttrs.type <= LAST_SUB_WINDOW)) { + // The multiplier here is to reserve space for multiple + // windows in the same type layer. + mBaseLayer = mService.mPolicy.windowTypeToLayerLw( + attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER + + WindowManagerService.TYPE_LAYER_OFFSET; + mSubLayer = mService.mPolicy.subWindowTypeToLayerLw(a.type); + mAttachedWindow = attachedWindow; + if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(WindowManagerService.TAG, "Adding " + this + " to " + mAttachedWindow); + mAttachedWindow.mChildWindows.add(this); + mLayoutAttached = mAttrs.type != + WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; + mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD + || attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG; + mIsWallpaper = attachedWindow.mAttrs.type == TYPE_WALLPAPER; + mIsFloatingLayer = mIsImWindow || mIsWallpaper; + } else { + // The multiplier here is to reserve space for multiple + // windows in the same type layer. + mBaseLayer = mService.mPolicy.windowTypeToLayerLw(a.type) + * WindowManagerService.TYPE_LAYER_MULTIPLIER + + WindowManagerService.TYPE_LAYER_OFFSET; + mSubLayer = 0; + mAttachedWindow = null; + mLayoutAttached = false; + mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD + || mAttrs.type == TYPE_INPUT_METHOD_DIALOG; + mIsWallpaper = mAttrs.type == TYPE_WALLPAPER; + mIsFloatingLayer = mIsImWindow || mIsWallpaper; + } + + WindowState appWin = this; + while (appWin.mAttachedWindow != null) { + appWin = mAttachedWindow; + } + WindowToken appToken = appWin.mToken; + while (appToken.appWindowToken == null) { + WindowToken parent = mService.mTokenMap.get(appToken.token); + if (parent == null || appToken == parent) { + break; + } + appToken = parent; + } + mRootToken = appToken; + mAppToken = appToken.appWindowToken; + + mSurface = null; + mRequestedWidth = 0; + mRequestedHeight = 0; + mLastRequestedWidth = 0; + mLastRequestedHeight = 0; + mXOffset = 0; + mYOffset = 0; + mLayer = 0; + mAnimLayer = 0; + mLastLayer = 0; + mInputWindowHandle = new InputWindowHandle( + mAppToken != null ? mAppToken.mInputApplicationHandle : null, this); + } + + void attach() { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Attaching " + this + " token=" + mToken + + ", list=" + mToken.windows); + mSession.windowAddedLocked(); + } + + public void computeFrameLw(Rect pf, Rect df, Rect cf, Rect vf) { + mHaveFrame = true; + + final Rect container = mContainingFrame; + container.set(pf); + + final Rect display = mDisplayFrame; + display.set(df); + + if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) { + container.intersect(mService.mCompatibleScreenFrame); + if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) { + display.intersect(mService.mCompatibleScreenFrame); + } + } + + final int pw = container.right - container.left; + final int ph = container.bottom - container.top; + + int w,h; + if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) { + w = mAttrs.width < 0 ? pw : mAttrs.width; + h = mAttrs.height< 0 ? ph : mAttrs.height; + } else { + w = mAttrs.width == mAttrs.MATCH_PARENT ? pw : mRequestedWidth; + h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight; + } + + if (!mParentFrame.equals(pf)) { + //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame + // + " to " + pf); + mParentFrame.set(pf); + mContentChanged = true; + } + + final Rect content = mContentFrame; + content.set(cf); + + final Rect visible = mVisibleFrame; + visible.set(vf); + + final Rect frame = mFrame; + final int fw = frame.width(); + final int fh = frame.height(); + + //System.out.println("In: w=" + w + " h=" + h + " container=" + + // container + " x=" + mAttrs.x + " y=" + mAttrs.y); + + Gravity.apply(mAttrs.gravity, w, h, container, + (int) (mAttrs.x + mAttrs.horizontalMargin * pw), + (int) (mAttrs.y + mAttrs.verticalMargin * ph), frame); + + //System.out.println("Out: " + mFrame); + + // Now make sure the window fits in the overall display. + Gravity.applyDisplay(mAttrs.gravity, df, frame); + + // Make sure the content and visible frames are inside of the + // final window frame. + if (content.left < frame.left) content.left = frame.left; + if (content.top < frame.top) content.top = frame.top; + if (content.right > frame.right) content.right = frame.right; + if (content.bottom > frame.bottom) content.bottom = frame.bottom; + if (visible.left < frame.left) visible.left = frame.left; + if (visible.top < frame.top) visible.top = frame.top; + if (visible.right > frame.right) visible.right = frame.right; + if (visible.bottom > frame.bottom) visible.bottom = frame.bottom; + + final Rect contentInsets = mContentInsets; + contentInsets.left = content.left-frame.left; + contentInsets.top = content.top-frame.top; + contentInsets.right = frame.right-content.right; + contentInsets.bottom = frame.bottom-content.bottom; + + final Rect visibleInsets = mVisibleInsets; + visibleInsets.left = visible.left-frame.left; + visibleInsets.top = visible.top-frame.top; + visibleInsets.right = frame.right-visible.right; + visibleInsets.bottom = frame.bottom-visible.bottom; + + if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) { + mService.updateWallpaperOffsetLocked(this, mService.mDisplay.getWidth(), + mService.mDisplay.getHeight(), false); + } + + if (WindowManagerService.localLOGV) { + //if ("com.google.android.youtube".equals(mAttrs.packageName) + // && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { + Slog.v(WindowManagerService.TAG, "Resolving (mRequestedWidth=" + + mRequestedWidth + ", mRequestedheight=" + + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph + + "): frame=" + mFrame.toShortString() + + " ci=" + contentInsets.toShortString() + + " vi=" + visibleInsets.toShortString()); + //} + } + } + + public Rect getFrameLw() { + return mFrame; + } + + public Rect getShownFrameLw() { + return mShownFrame; + } + + public Rect getDisplayFrameLw() { + return mDisplayFrame; + } + + public Rect getContentFrameLw() { + return mContentFrame; + } + + public Rect getVisibleFrameLw() { + return mVisibleFrame; + } + + public boolean getGivenInsetsPendingLw() { + return mGivenInsetsPending; + } + + public Rect getGivenContentInsetsLw() { + return mGivenContentInsets; + } + + public Rect getGivenVisibleInsetsLw() { + return mGivenVisibleInsets; + } + + public WindowManager.LayoutParams getAttrs() { + return mAttrs; + } + + public int getSurfaceLayer() { + return mLayer; + } + + public IApplicationToken getAppToken() { + return mAppToken != null ? mAppToken.appToken : null; + } + + public long getInputDispatchingTimeoutNanos() { + return mAppToken != null + ? mAppToken.inputDispatchingTimeoutNanos + : WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + } + + public boolean hasAppShownWindows() { + return mAppToken != null ? mAppToken.firstWindowDrawn : false; + } + + public void setAnimation(Animation anim) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Setting animation in " + this + ": " + anim); + mAnimating = false; + mLocalAnimating = false; + mAnimation = anim; + mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION); + mAnimation.scaleCurrentDuration(mService.mWindowAnimationScale); + } + + public void clearAnimation() { + if (mAnimation != null) { + mAnimating = true; + mLocalAnimating = false; + mAnimation.cancel(); + mAnimation = null; + } + } + + Surface createSurfaceLocked() { + if (mSurface == null) { + mReportDestroySurface = false; + mSurfacePendingDestroy = false; + mDrawPending = true; + mCommitDrawPending = false; + mReadyToShow = false; + if (mAppToken != null) { + mAppToken.allDrawn = false; + } + + int flags = 0; + + if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) { + flags |= Surface.SECURE; + } + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v( + WindowManagerService.TAG, "Creating surface in session " + + mSession.mSurfaceSession + " window " + this + + " w=" + mFrame.width() + + " h=" + mFrame.height() + " format=" + + mAttrs.format + " flags=" + flags); + + int w = mFrame.width(); + int h = mFrame.height(); + if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) { + // for a scaled surface, we always want the requested + // size. + w = mRequestedWidth; + h = mRequestedHeight; + } + + // Something is wrong and SurfaceFlinger will not like this, + // try to revert to sane values + if (w <= 0) w = 1; + if (h <= 0) h = 1; + + mSurfaceShown = false; + mSurfaceLayer = 0; + mSurfaceAlpha = 1; + mSurfaceX = 0; + mSurfaceY = 0; + mSurfaceW = w; + mSurfaceH = h; + try { + final boolean isHwAccelerated = (mAttrs.flags & + WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; + final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : mAttrs.format; + if (isHwAccelerated && mAttrs.format == PixelFormat.OPAQUE) { + flags |= Surface.OPAQUE; + } + mSurface = new Surface( + mSession.mSurfaceSession, mSession.mPid, + mAttrs.getTitle().toString(), + 0, w, h, format, flags); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " CREATE SURFACE " + + mSurface + " IN SESSION " + + mSession.mSurfaceSession + + ": pid=" + mSession.mPid + " format=" + + mAttrs.format + " flags=0x" + + Integer.toHexString(flags) + + " / " + this); + } catch (Surface.OutOfResourcesException e) { + Slog.w(WindowManagerService.TAG, "OutOfResourcesException creating surface"); + mService.reclaimSomeSurfaceMemoryLocked(this, "create"); + return null; + } catch (Exception e) { + Slog.e(WindowManagerService.TAG, "Exception creating surface", e); + return null; + } + + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Got surface: " + mSurface + + ", set left=" + mFrame.left + " top=" + mFrame.top + + ", animLayer=" + mAnimLayer); + if (WindowManagerService.SHOW_TRANSACTIONS) { + Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION createSurfaceLocked"); + WindowManagerService.logSurface(this, "CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" + + mFrame.width() + "x" + mFrame.height() + "), layer=" + + mAnimLayer + " HIDE", null); + } + Surface.openTransaction(); + try { + try { + mSurfaceX = mFrame.left + mXOffset; + mSurfaceY = mFrame.top + mYOffset; + mSurface.setPosition(mSurfaceX, mSurfaceY); + mSurfaceLayer = mAnimLayer; + mSurface.setLayer(mAnimLayer); + mSurfaceShown = false; + mSurface.hide(); + if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) { + if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(this, "DITHER", null); + mSurface.setFlags(Surface.SURFACE_DITHER, + Surface.SURFACE_DITHER); + } + } catch (RuntimeException e) { + Slog.w(WindowManagerService.TAG, "Error creating surface in " + w, e); + mService.reclaimSomeSurfaceMemoryLocked(this, "create-init"); + } + mLastHidden = true; + } finally { + Surface.closeTransaction(); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION createSurfaceLocked"); + } + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Created surface " + this); + } + return mSurface; + } + + void destroySurfaceLocked() { + if (mAppToken != null && this == mAppToken.startingWindow) { + mAppToken.startingDisplayed = false; + } + + if (mSurface != null) { + mDrawPending = false; + mCommitDrawPending = false; + mReadyToShow = false; + + int i = mChildWindows.size(); + while (i > 0) { + i--; + WindowState c = mChildWindows.get(i); + c.mAttachedHidden = true; + } + + if (mReportDestroySurface) { + mReportDestroySurface = false; + mSurfacePendingDestroy = true; + try { + mClient.dispatchGetNewSurface(); + // We'll really destroy on the next time around. + return; + } catch (RemoteException e) { + } + } + + try { + if (WindowManagerService.DEBUG_VISIBILITY) { + RuntimeException e = null; + if (!WindowManagerService.HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.w(WindowManagerService.TAG, "Window " + this + " destroying surface " + + mSurface + ", session " + mSession, e); + } + if (WindowManagerService.SHOW_TRANSACTIONS) { + RuntimeException e = null; + if (!WindowManagerService.HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(this, "DESTROY", e); + } + mSurface.destroy(); + } catch (RuntimeException e) { + Slog.w(WindowManagerService.TAG, "Exception thrown when destroying Window " + this + + " surface " + mSurface + " session " + mSession + + ": " + e.toString()); + } + + mSurfaceShown = false; + mSurface = null; + } + } + + boolean finishDrawingLocked() { + if (mDrawPending) { + if (WindowManagerService.SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION) Slog.v( + WindowManagerService.TAG, "finishDrawingLocked: " + mSurface); + mCommitDrawPending = true; + mDrawPending = false; + return true; + } + return false; + } + + // This must be called while inside a transaction. + boolean commitFinishDrawingLocked(long currentTime) { + //Slog.i(TAG, "commitFinishDrawingLocked: " + mSurface); + if (!mCommitDrawPending) { + return false; + } + mCommitDrawPending = false; + mReadyToShow = true; + final boolean starting = mAttrs.type == TYPE_APPLICATION_STARTING; + final AppWindowToken atoken = mAppToken; + if (atoken == null || atoken.allDrawn || starting) { + performShowLocked(); + } + return true; + } + + // This must be called while inside a transaction. + boolean performShowLocked() { + if (WindowManagerService.DEBUG_VISIBILITY) { + RuntimeException e = null; + if (!WindowManagerService.HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.v(WindowManagerService.TAG, "performShow on " + this + + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay() + + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e); + } + if (mReadyToShow && isReadyForDisplay()) { + if (WindowManagerService.SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION) WindowManagerService.logSurface(this, + "SHOW (performShowLocked)", null); + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Showing " + this + + " during animation: policyVis=" + mPolicyVisibility + + " attHidden=" + mAttachedHidden + + " tok.hiddenRequested=" + + (mAppToken != null ? mAppToken.hiddenRequested : false) + + " tok.hidden=" + + (mAppToken != null ? mAppToken.hidden : false) + + " animating=" + mAnimating + + " tok animating=" + + (mAppToken != null ? mAppToken.animating : false)); + if (!mService.showSurfaceRobustlyLocked(this)) { + return false; + } + mLastAlpha = -1; + mHasDrawn = true; + mLastHidden = false; + mReadyToShow = false; + mService.enableScreenIfNeededLocked(); + + mService.applyEnterAnimationLocked(this); + + int i = mChildWindows.size(); + while (i > 0) { + i--; + WindowState c = mChildWindows.get(i); + if (c.mAttachedHidden) { + c.mAttachedHidden = false; + if (c.mSurface != null) { + c.performShowLocked(); + // It hadn't been shown, which means layout not + // performed on it, so now we want to make sure to + // do a layout. If called from within the transaction + // loop, this will cause it to restart with a new + // layout. + mService.mLayoutNeeded = true; + } + } + } + + if (mAttrs.type != TYPE_APPLICATION_STARTING + && mAppToken != null) { + mAppToken.firstWindowDrawn = true; + + if (mAppToken.startingData != null) { + if (WindowManagerService.DEBUG_STARTING_WINDOW || WindowManagerService.DEBUG_ANIM) Slog.v(WindowManagerService.TAG, + "Finish starting " + mToken + + ": first real window is shown, no animation"); + // If this initial window is animating, stop it -- we + // will do an animation to reveal it from behind the + // starting window, so there is no need for it to also + // be doing its own stuff. + if (mAnimation != null) { + mAnimation.cancel(); + mAnimation = null; + // Make sure we clean up the animation. + mAnimating = true; + } + mService.mFinishedStarting.add(mAppToken); + mService.mH.sendEmptyMessage(H.FINISHED_STARTING); + } + mAppToken.updateReportedVisibilityLocked(); + } + } + return true; + } + + // This must be called while inside a transaction. Returns true if + // there is more animation to run. + boolean stepAnimationLocked(long currentTime, int dw, int dh) { + if (!mService.mDisplayFrozen && mService.mPolicy.isScreenOn()) { + // We will run animations as long as the display isn't frozen. + + if (!mDrawPending && !mCommitDrawPending && mAnimation != null) { + mHasTransformation = true; + mHasLocalTransformation = true; + if (!mLocalAnimating) { + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Starting animation in " + this + + " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() + + " dw=" + dw + " dh=" + dh + " scale=" + mService.mWindowAnimationScale); + mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh); + mAnimation.setStartTime(currentTime); + mLocalAnimating = true; + mAnimating = true; + } + mTransformation.clear(); + final boolean more = mAnimation.getTransformation( + currentTime, mTransformation); + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Stepped animation in " + this + + ": more=" + more + ", xform=" + mTransformation); + if (more) { + // we're not done! + return true; + } + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Finished animation in " + this + + " @ " + currentTime); + + if (mAnimation != null) { + mAnimation.cancel(); + mAnimation = null; + } + //WindowManagerService.this.dump(); + } + mHasLocalTransformation = false; + if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null + && mAppToken.animation != null) { + // When our app token is animating, we kind-of pretend like + // we are as well. Note the mLocalAnimating mAnimationIsEntrance + // part of this check means that we will only do this if + // our window is not currently exiting, or it is not + // locally animating itself. The idea being that one that + // is exiting and doing a local animation should be removed + // once that animation is done. + mAnimating = true; + mHasTransformation = true; + mTransformation.clear(); + return false; + } else if (mHasTransformation) { + // Little trick to get through the path below to act like + // we have finished an animation. + mAnimating = true; + } else if (isAnimating()) { + mAnimating = true; + } + } else if (mAnimation != null) { + // If the display is frozen, and there is a pending animation, + // clear it and make sure we run the cleanup code. + mAnimating = true; + mLocalAnimating = true; + mAnimation.cancel(); + mAnimation = null; + } + + if (!mAnimating && !mLocalAnimating) { + return false; + } + + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Animation done in " + this + ": exiting=" + mExiting + + ", reportedVisible=" + + (mAppToken != null ? mAppToken.reportedVisible : false)); + + mAnimating = false; + mLocalAnimating = false; + if (mAnimation != null) { + mAnimation.cancel(); + mAnimation = null; + } + mAnimLayer = mLayer; + if (mIsImWindow) { + mAnimLayer += mService.mInputMethodAnimLayerAdjustment; + } else if (mIsWallpaper) { + mAnimLayer += mService.mWallpaperAnimLayerAdjustment; + } + if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Stepping win " + this + + " anim layer: " + mAnimLayer); + mHasTransformation = false; + mHasLocalTransformation = false; + if (mPolicyVisibility != mPolicyVisibilityAfterAnim) { + if (WindowManagerService.DEBUG_VISIBILITY) { + Slog.v(WindowManagerService.TAG, "Policy visibility changing after anim in " + this + ": " + + mPolicyVisibilityAfterAnim); + } + mPolicyVisibility = mPolicyVisibilityAfterAnim; + if (!mPolicyVisibility) { + if (mService.mCurrentFocus == this) { + mService.mFocusMayChange = true; + } + // Window is no longer visible -- make sure if we were waiting + // for it to be displayed before enabling the display, that + // we allow the display to be enabled now. + mService.enableScreenIfNeededLocked(); + } + } + mTransformation.clear(); + if (mHasDrawn + && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING + && mAppToken != null + && mAppToken.firstWindowDrawn + && mAppToken.startingData != null) { + if (WindowManagerService.DEBUG_STARTING_WINDOW) Slog.v(WindowManagerService.TAG, "Finish starting " + + mToken + ": first real window done animating"); + mService.mFinishedStarting.add(mAppToken); + mService.mH.sendEmptyMessage(H.FINISHED_STARTING); + } + + finishExit(); + + if (mAppToken != null) { + mAppToken.updateReportedVisibilityLocked(); + } + + return false; + } + + void finishExit() { + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "finishExit in " + this + + ": exiting=" + mExiting + + " remove=" + mRemoveOnExit + + " windowAnimating=" + isWindowAnimating()); + + final int N = mChildWindows.size(); + for (int i=0; i<N; i++) { + mChildWindows.get(i).finishExit(); + } + + if (!mExiting) { + return; + } + + if (isWindowAnimating()) { + return; + } + + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Exit animation finished in " + this + + ": remove=" + mRemoveOnExit); + if (mSurface != null) { + mService.mDestroySurface.add(this); + mDestroying = true; + if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(this, "HIDE (finishExit)", null); + mSurfaceShown = false; + try { + mSurface.hide(); + } catch (RuntimeException e) { + Slog.w(WindowManagerService.TAG, "Error hiding surface in " + this, e); + } + mLastHidden = true; + } + mExiting = false; + if (mRemoveOnExit) { + mService.mPendingRemove.add(this); + mRemoveOnExit = false; + } + } + + boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { + if (dsdx < .99999f || dsdx > 1.00001f) return false; + if (dtdy < .99999f || dtdy > 1.00001f) return false; + if (dtdx < -.000001f || dtdx > .000001f) return false; + if (dsdy < -.000001f || dsdy > .000001f) return false; + return true; + } + + void computeShownFrameLocked() { + final boolean selfTransformation = mHasLocalTransformation; + Transformation attachedTransformation = + (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation) + ? mAttachedWindow.mTransformation : null; + Transformation appTransformation = + (mAppToken != null && mAppToken.hasTransformation) + ? mAppToken.transformation : null; + + // Wallpapers are animated based on the "real" window they + // are currently targeting. + if (mAttrs.type == TYPE_WALLPAPER && mService.mLowerWallpaperTarget == null + && mService.mWallpaperTarget != null) { + if (mService.mWallpaperTarget.mHasLocalTransformation && + mService.mWallpaperTarget.mAnimation != null && + !mService.mWallpaperTarget.mAnimation.getDetachWallpaper()) { + attachedTransformation = mService.mWallpaperTarget.mTransformation; + if (WindowManagerService.DEBUG_WALLPAPER && attachedTransformation != null) { + Slog.v(WindowManagerService.TAG, "WP target attached xform: " + attachedTransformation); + } + } + if (mService.mWallpaperTarget.mAppToken != null && + mService.mWallpaperTarget.mAppToken.hasTransformation && + mService.mWallpaperTarget.mAppToken.animation != null && + !mService.mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) { + appTransformation = mService.mWallpaperTarget.mAppToken.transformation; + if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) { + Slog.v(WindowManagerService.TAG, "WP target app xform: " + appTransformation); + } + } + } + + final boolean screenAnimation = mService.mScreenRotationAnimation != null + && mService.mScreenRotationAnimation.isAnimating(); + if (selfTransformation || attachedTransformation != null + || appTransformation != null || screenAnimation) { + // cache often used attributes locally + final Rect frame = mFrame; + final float tmpFloats[] = mService.mTmpFloats; + final Matrix tmpMatrix = mTmpMatrix; + + // Compute the desired transformation. + tmpMatrix.setTranslate(0, 0); + if (selfTransformation) { + tmpMatrix.postConcat(mTransformation.getMatrix()); + } + tmpMatrix.postTranslate(frame.left + mXOffset, frame.top + mYOffset); + if (attachedTransformation != null) { + tmpMatrix.postConcat(attachedTransformation.getMatrix()); + } + if (appTransformation != null) { + tmpMatrix.postConcat(appTransformation.getMatrix()); + } + if (screenAnimation) { + tmpMatrix.postConcat( + mService.mScreenRotationAnimation.getEnterTransformation().getMatrix()); + } + + // "convert" it into SurfaceFlinger's format + // (a 2x2 matrix + an offset) + // Here we must not transform the position of the surface + // since it is already included in the transformation. + //Slog.i(TAG, "Transform: " + matrix); + + mHaveMatrix = true; + tmpMatrix.getValues(tmpFloats); + mDsDx = tmpFloats[Matrix.MSCALE_X]; + mDtDx = tmpFloats[Matrix.MSKEW_Y]; + mDsDy = tmpFloats[Matrix.MSKEW_X]; + mDtDy = tmpFloats[Matrix.MSCALE_Y]; + int x = (int)tmpFloats[Matrix.MTRANS_X]; + int y = (int)tmpFloats[Matrix.MTRANS_Y]; + int w = frame.width(); + int h = frame.height(); + mShownFrame.set(x, y, x+w, y+h); + + // Now set the alpha... but because our current hardware + // can't do alpha transformation on a non-opaque surface, + // turn it off if we are running an animation that is also + // transforming since it is more important to have that + // animation be smooth. + mShownAlpha = mAlpha; + if (!mService.mLimitedAlphaCompositing + || (!PixelFormat.formatHasAlpha(mAttrs.format) + || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) + && x == frame.left && y == frame.top))) { + //Slog.i(TAG, "Applying alpha transform"); + if (selfTransformation) { + mShownAlpha *= mTransformation.getAlpha(); + } + if (attachedTransformation != null) { + mShownAlpha *= attachedTransformation.getAlpha(); + } + if (appTransformation != null) { + mShownAlpha *= appTransformation.getAlpha(); + } + if (screenAnimation) { + mShownAlpha *= + mService.mScreenRotationAnimation.getEnterTransformation().getAlpha(); + } + } else { + //Slog.i(TAG, "Not applying alpha transform"); + } + + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Continuing animation in " + this + + ": " + mShownFrame + + ", alpha=" + mTransformation.getAlpha()); + return; + } + + mShownFrame.set(mFrame); + if (mXOffset != 0 || mYOffset != 0) { + mShownFrame.offset(mXOffset, mYOffset); + } + mShownAlpha = mAlpha; + mHaveMatrix = false; + mDsDx = 1; + mDtDx = 0; + mDsDy = 0; + mDtDy = 1; + } + + /** + * Is this window visible? It is not visible if there is no + * surface, or we are in the process of running an exit animation + * that will remove the surface, or its app token has been hidden. + */ + public boolean isVisibleLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && mPolicyVisibility && !mAttachedHidden + && (atoken == null || !atoken.hiddenRequested) + && !mExiting && !mDestroying; + } + + /** + * Like {@link #isVisibleLw}, but also counts a window that is currently + * "hidden" behind the keyguard as visible. This allows us to apply + * things like window flags that impact the keyguard. + * XXX I am starting to think we need to have ANOTHER visibility flag + * for this "hidden behind keyguard" state rather than overloading + * mPolicyVisibility. Ungh. + */ + public boolean isVisibleOrBehindKeyguardLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && !mAttachedHidden + && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested) + && !mDrawPending && !mCommitDrawPending + && !mExiting && !mDestroying; + } + + /** + * Is this window visible, ignoring its app token? It is not visible + * if there is no surface, or we are in the process of running an exit animation + * that will remove the surface. + */ + public boolean isWinVisibleLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && mPolicyVisibility && !mAttachedHidden + && (atoken == null || !atoken.hiddenRequested || atoken.animating) + && !mExiting && !mDestroying; + } + + /** + * The same as isVisible(), but follows the current hidden state of + * the associated app token, not the pending requested hidden state. + */ + boolean isVisibleNow() { + return mSurface != null && mPolicyVisibility && !mAttachedHidden + && !mRootToken.hidden && !mExiting && !mDestroying; + } + + /** + * Can this window possibly be a drag/drop target? The test here is + * a combination of the above "visible now" with the check that the + * Input Manager uses when discarding windows from input consideration. + */ + boolean isPotentialDragTarget() { + return isVisibleNow() && (mInputChannel != null) && !mRemoved; + } + + /** + * Same as isVisible(), but we also count it as visible between the + * call to IWindowSession.add() and the first relayout(). + */ + boolean isVisibleOrAdding() { + final AppWindowToken atoken = mAppToken; + return ((mSurface != null && !mReportDestroySurface) + || (!mRelayoutCalled && mViewVisibility == View.VISIBLE)) + && mPolicyVisibility && !mAttachedHidden + && (atoken == null || !atoken.hiddenRequested) + && !mExiting && !mDestroying; + } + + /** + * Is this window currently on-screen? It is on-screen either if it + * is visible or it is currently running an animation before no longer + * being visible. + */ + boolean isOnScreen() { + final AppWindowToken atoken = mAppToken; + if (atoken != null) { + return mSurface != null && mPolicyVisibility && !mDestroying + && ((!mAttachedHidden && !atoken.hiddenRequested) + || mAnimation != null || atoken.animation != null); + } else { + return mSurface != null && mPolicyVisibility && !mDestroying + && (!mAttachedHidden || mAnimation != null); + } + } + + /** + * Like isOnScreen(), but we don't return true if the window is part + * of a transition that has not yet been started. + */ + boolean isReadyForDisplay() { + if (mRootToken.waitingToShow && + mService.mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { + return false; + } + final AppWindowToken atoken = mAppToken; + final boolean animating = atoken != null + ? (atoken.animation != null) : false; + return mSurface != null && mPolicyVisibility && !mDestroying + && ((!mAttachedHidden && mViewVisibility == View.VISIBLE + && !mRootToken.hidden) + || mAnimation != null || animating); + } + + /** Is the window or its container currently animating? */ + boolean isAnimating() { + final WindowState attached = mAttachedWindow; + final AppWindowToken atoken = mAppToken; + return mAnimation != null + || (attached != null && attached.mAnimation != null) + || (atoken != null && + (atoken.animation != null + || atoken.inPendingTransaction)); + } + + /** Is this window currently animating? */ + boolean isWindowAnimating() { + return mAnimation != null; + } + + /** + * Like isOnScreen, but returns false if the surface hasn't yet + * been drawn. + */ + public boolean isDisplayedLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && mPolicyVisibility && !mDestroying + && !mDrawPending && !mCommitDrawPending + && ((!mAttachedHidden && + (atoken == null || !atoken.hiddenRequested)) + || mAnimating); + } + + /** + * Returns true if the window has a surface that it has drawn a + * complete UI in to. + */ + public boolean isDrawnLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && !mDestroying + && !mDrawPending && !mCommitDrawPending; + } + + /** + * Return true if the window is opaque and fully drawn. This indicates + * it may obscure windows behind it. + */ + boolean isOpaqueDrawn() { + return (mAttrs.format == PixelFormat.OPAQUE + || mAttrs.type == TYPE_WALLPAPER) + && mSurface != null && mAnimation == null + && (mAppToken == null || mAppToken.animation == null) + && !mDrawPending && !mCommitDrawPending; + } + + /** + * Return whether this window is wanting to have a translation + * animation applied to it for an in-progress move. (Only makes + * sense to call from performLayoutAndPlaceSurfacesLockedInner().) + */ + boolean shouldAnimateMove() { + return mContentChanged && !mExiting && !mLastHidden && !mService.mDisplayFrozen + && (mFrame.top != mLastFrame.top + || mFrame.left != mLastFrame.left) + && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove()) + && mService.mPolicy.isScreenOn(); + } + + boolean needsBackgroundFiller(int screenWidth, int screenHeight) { + return + // only if the application is requesting compatible window + (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0 && + // only if it's visible + mHasDrawn && mViewVisibility == View.VISIBLE && + // and only if the application fills the compatible screen + mFrame.left <= mService.mCompatibleScreenFrame.left && + mFrame.top <= mService.mCompatibleScreenFrame.top && + mFrame.right >= mService.mCompatibleScreenFrame.right && + mFrame.bottom >= mService.mCompatibleScreenFrame.bottom; + } + + boolean isFullscreen(int screenWidth, int screenHeight) { + return mFrame.left <= 0 && mFrame.top <= 0 && + mFrame.right >= screenWidth && mFrame.bottom >= screenHeight; + } + + void removeLocked() { + disposeInputChannel(); + + if (mAttachedWindow != null) { + if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(WindowManagerService.TAG, "Removing " + this + " from " + mAttachedWindow); + mAttachedWindow.mChildWindows.remove(this); + } + destroySurfaceLocked(); + mSession.windowRemovedLocked(); + try { + mClient.asBinder().unlinkToDeath(mDeathRecipient, 0); + } catch (RuntimeException e) { + // Ignore if it has already been removed (usually because + // we are doing this as part of processing a death note.) + } + } + + void disposeInputChannel() { + if (mInputChannel != null) { + mService.mInputManager.unregisterInputChannel(mInputChannel); + + mInputChannel.dispose(); + mInputChannel = null; + } + } + + private class DeathRecipient implements IBinder.DeathRecipient { + public void binderDied() { + try { + synchronized(mService.mWindowMap) { + WindowState win = mService.windowForClientLocked(mSession, mClient, false); + Slog.i(WindowManagerService.TAG, "WIN DEATH: " + win); + if (win != null) { + mService.removeWindowLocked(mSession, win); + } + } + } catch (IllegalArgumentException ex) { + // This will happen if the window has already been + // removed. + } + } + } + + /** Returns true if this window desires key events. */ + public final boolean canReceiveKeys() { + return isVisibleOrAdding() + && (mViewVisibility == View.VISIBLE) + && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0); + } + + public boolean hasDrawnLw() { + return mHasDrawn; + } + + public boolean showLw(boolean doAnimation) { + return showLw(doAnimation, true); + } + + boolean showLw(boolean doAnimation, boolean requestAnim) { + if (mPolicyVisibility && mPolicyVisibilityAfterAnim) { + return false; + } + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Policy visibility true: " + this); + if (doAnimation) { + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "doAnimation: mPolicyVisibility=" + + mPolicyVisibility + " mAnimation=" + mAnimation); + if (mService.mDisplayFrozen || !mService.mPolicy.isScreenOn()) { + doAnimation = false; + } else if (mPolicyVisibility && mAnimation == null) { + // Check for the case where we are currently visible and + // not animating; we do not want to do animation at such a + // point to become visible when we already are. + doAnimation = false; + } + } + mPolicyVisibility = true; + mPolicyVisibilityAfterAnim = true; + if (doAnimation) { + mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true); + } + if (requestAnim) { + mService.requestAnimationLocked(0); + } + return true; + } + + public boolean hideLw(boolean doAnimation) { + return hideLw(doAnimation, true); + } + + boolean hideLw(boolean doAnimation, boolean requestAnim) { + if (doAnimation) { + if (mService.mDisplayFrozen || !mService.mPolicy.isScreenOn()) { + doAnimation = false; + } + } + boolean current = doAnimation ? mPolicyVisibilityAfterAnim + : mPolicyVisibility; + if (!current) { + return false; + } + if (doAnimation) { + mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false); + if (mAnimation == null) { + doAnimation = false; + } + } + if (doAnimation) { + mPolicyVisibilityAfterAnim = false; + } else { + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Policy visibility false: " + this); + mPolicyVisibilityAfterAnim = false; + mPolicyVisibility = false; + // Window is no longer visible -- make sure if we were waiting + // for it to be displayed before enabling the display, that + // we allow the display to be enabled now. + mService.enableScreenIfNeededLocked(); + if (mService.mCurrentFocus == this) { + mService.mFocusMayChange = true; + } + } + if (requestAnim) { + mService.requestAnimationLocked(0); + } + return true; + } + + public void getTouchableRegion(Region outRegion) { + final Rect frame = mFrame; + switch (mTouchableInsets) { + default: + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME: + outRegion.set(frame); + break; + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: { + final Rect inset = mGivenContentInsets; + outRegion.set( + frame.left + inset.left, frame.top + inset.top, + frame.right - inset.right, frame.bottom - inset.bottom); + break; + } + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: { + final Rect inset = mGivenVisibleInsets; + outRegion.set( + frame.left + inset.left, frame.top + inset.top, + frame.right - inset.right, frame.bottom - inset.bottom); + break; + } + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: { + final Region givenTouchableRegion = mGivenTouchableRegion; + outRegion.set(givenTouchableRegion); + outRegion.translate(frame.left, frame.top); + break; + } + } + } + + void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("mSession="); pw.print(mSession); + pw.print(" mClient="); pw.println(mClient.asBinder()); + pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs); + if (mAttachedWindow != null || mLayoutAttached) { + pw.print(prefix); pw.print("mAttachedWindow="); pw.print(mAttachedWindow); + pw.print(" mLayoutAttached="); pw.println(mLayoutAttached); + } + if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) { + pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow); + pw.print(" mIsWallpaper="); pw.print(mIsWallpaper); + pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer); + pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible); + } + pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer); + pw.print(" mSubLayer="); pw.print(mSubLayer); + pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+"); + pw.print((mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment + : (mAppToken != null ? mAppToken.animLayerAdjustment : 0))); + pw.print("="); pw.print(mAnimLayer); + pw.print(" mLastLayer="); pw.println(mLastLayer); + if (mSurface != null) { + pw.print(prefix); pw.print("mSurface="); pw.println(mSurface); + pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown); + pw.print(" layer="); pw.print(mSurfaceLayer); + pw.print(" alpha="); pw.print(mSurfaceAlpha); + pw.print(" rect=("); pw.print(mSurfaceX); + pw.print(","); pw.print(mSurfaceY); + pw.print(") "); pw.print(mSurfaceW); + pw.print(" x "); pw.println(mSurfaceH); + } + pw.print(prefix); pw.print("mToken="); pw.println(mToken); + pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken); + if (mAppToken != null) { + pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken); + } + if (mTargetAppToken != null) { + pw.print(prefix); pw.print("mTargetAppToken="); pw.println(mTargetAppToken); + } + pw.print(prefix); pw.print("mViewVisibility=0x"); + pw.print(Integer.toHexString(mViewVisibility)); + pw.print(" mLastHidden="); pw.print(mLastHidden); + pw.print(" mHaveFrame="); pw.print(mHaveFrame); + pw.print(" mObscured="); pw.println(mObscured); + if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || mAttachedHidden) { + pw.print(prefix); pw.print("mPolicyVisibility="); + pw.print(mPolicyVisibility); + pw.print(" mPolicyVisibilityAfterAnim="); + pw.print(mPolicyVisibilityAfterAnim); + pw.print(" mAttachedHidden="); pw.println(mAttachedHidden); + } + if (!mRelayoutCalled) { + pw.print(prefix); pw.print("mRelayoutCalled="); pw.println(mRelayoutCalled); + } + pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth); + pw.print(" h="); pw.print(mRequestedHeight); + pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); + if (mXOffset != 0 || mYOffset != 0) { + pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset); + pw.print(" y="); pw.println(mYOffset); + } + pw.print(prefix); pw.print("mGivenContentInsets="); + mGivenContentInsets.printShortString(pw); + pw.print(" mGivenVisibleInsets="); + mGivenVisibleInsets.printShortString(pw); + pw.println(); + if (mTouchableInsets != 0 || mGivenInsetsPending) { + pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets); + pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending); + } + pw.print(prefix); pw.print("mConfiguration="); pw.println(mConfiguration); + pw.print(prefix); pw.print("mShownFrame="); + mShownFrame.printShortString(pw); + pw.print(" last="); mLastShownFrame.printShortString(pw); + pw.println(); + pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw); + pw.print(" last="); mLastFrame.printShortString(pw); + pw.println(); + pw.print(prefix); pw.print("mContainingFrame="); + mContainingFrame.printShortString(pw); + pw.print(" mParentFrame="); + mParentFrame.printShortString(pw); + pw.print(" mDisplayFrame="); + mDisplayFrame.printShortString(pw); + pw.println(); + pw.print(prefix); pw.print("mContentFrame="); mContentFrame.printShortString(pw); + pw.print(" mVisibleFrame="); mVisibleFrame.printShortString(pw); + pw.println(); + pw.print(prefix); pw.print("mContentInsets="); mContentInsets.printShortString(pw); + pw.print(" last="); mLastContentInsets.printShortString(pw); + pw.print(" mVisibleInsets="); mVisibleInsets.printShortString(pw); + pw.print(" last="); mLastVisibleInsets.printShortString(pw); + pw.println(); + if (mAnimating || mLocalAnimating || mAnimationIsEntrance + || mAnimation != null) { + pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating); + pw.print(" mLocalAnimating="); pw.print(mLocalAnimating); + pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance); + pw.print(" mAnimation="); pw.println(mAnimation); + } + if (mHasTransformation || mHasLocalTransformation) { + pw.print(prefix); pw.print("XForm: has="); + pw.print(mHasTransformation); + pw.print(" hasLocal="); pw.print(mHasLocalTransformation); + pw.print(" "); mTransformation.printShortString(pw); + pw.println(); + } + if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) { + pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha); + pw.print(" mAlpha="); pw.print(mAlpha); + pw.print(" mLastAlpha="); pw.println(mLastAlpha); + } + if (mHaveMatrix) { + pw.print(prefix); pw.print("mDsDx="); pw.print(mDsDx); + pw.print(" mDtDx="); pw.print(mDtDx); + pw.print(" mDsDy="); pw.print(mDsDy); + pw.print(" mDtDy="); pw.println(mDtDy); + } + pw.print(prefix); pw.print("mDrawPending="); pw.print(mDrawPending); + pw.print(" mCommitDrawPending="); pw.print(mCommitDrawPending); + pw.print(" mReadyToShow="); pw.print(mReadyToShow); + pw.print(" mHasDrawn="); pw.println(mHasDrawn); + if (mExiting || mRemoveOnExit || mDestroying || mRemoved) { + pw.print(prefix); pw.print("mExiting="); pw.print(mExiting); + pw.print(" mRemoveOnExit="); pw.print(mRemoveOnExit); + pw.print(" mDestroying="); pw.print(mDestroying); + pw.print(" mRemoved="); pw.println(mRemoved); + } + if (mOrientationChanging || mAppFreezing || mTurnOnScreen) { + pw.print(prefix); pw.print("mOrientationChanging="); + pw.print(mOrientationChanging); + pw.print(" mAppFreezing="); pw.print(mAppFreezing); + pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen); + } + if (mHScale != 1 || mVScale != 1) { + pw.print(prefix); pw.print("mHScale="); pw.print(mHScale); + pw.print(" mVScale="); pw.println(mVScale); + } + if (mWallpaperX != -1 || mWallpaperY != -1) { + pw.print(prefix); pw.print("mWallpaperX="); pw.print(mWallpaperX); + pw.print(" mWallpaperY="); pw.println(mWallpaperY); + } + if (mWallpaperXStep != -1 || mWallpaperYStep != -1) { + pw.print(prefix); pw.print("mWallpaperXStep="); pw.print(mWallpaperXStep); + pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep); + } + } + + String makeInputChannelName() { + return Integer.toHexString(System.identityHashCode(this)) + + " " + mAttrs.getTitle(); + } + + @Override + public String toString() { + if (mStringNameCache == null || mLastTitle != mAttrs.getTitle() + || mWasPaused != mToken.paused) { + mLastTitle = mAttrs.getTitle(); + mWasPaused = mToken.paused; + mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this)) + + " " + mLastTitle + " paused=" + mWasPaused + "}"; + } + return mStringNameCache; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/WindowToken.java b/services/java/com/android/server/wm/WindowToken.java new file mode 100644 index 0000000..3cd256e --- /dev/null +++ b/services/java/com/android/server/wm/WindowToken.java @@ -0,0 +1,110 @@ +/* + * 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.wm; + +import android.os.IBinder; + +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Container of a set of related windows in the window manager. Often this + * is an AppWindowToken, which is the handle for an Activity that it uses + * to display windows. For nested windows, there is a WindowToken created for + * the parent window to manage its children. + */ +class WindowToken { + // The window manager! + final WindowManagerService service; + + // The actual token. + final IBinder token; + + // The type of window this token is for, as per WindowManager.LayoutParams. + final int windowType; + + // Set if this token was explicitly added by a client, so should + // not be removed when all windows are removed. + final boolean explicit; + + // For printing. + String stringName; + + // If this is an AppWindowToken, this is non-null. + AppWindowToken appWindowToken; + + // All of the windows associated with this token. + final ArrayList<WindowState> windows = new ArrayList<WindowState>(); + + // Is key dispatching paused for this token? + boolean paused = false; + + // Should this token's windows be hidden? + boolean hidden; + + // Temporary for finding which tokens no longer have visible windows. + boolean hasVisible; + + // Set to true when this token is in a pending transaction where it + // will be shown. + boolean waitingToShow; + + // Set to true when this token is in a pending transaction where it + // will be hidden. + boolean waitingToHide; + + // Set to true when this token is in a pending transaction where its + // windows will be put to the bottom of the list. + boolean sendingToBottom; + + // Set to true when this token is in a pending transaction where its + // windows will be put to the top of the list. + boolean sendingToTop; + + WindowToken(WindowManagerService _service, IBinder _token, int type, boolean _explicit) { + service = _service; + token = _token; + windowType = type; + explicit = _explicit; + } + + void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("token="); pw.println(token); + pw.print(prefix); pw.print("windows="); pw.println(windows); + pw.print(prefix); pw.print("windowType="); pw.print(windowType); + pw.print(" hidden="); pw.print(hidden); + pw.print(" hasVisible="); pw.println(hasVisible); + if (waitingToShow || waitingToHide || sendingToBottom || sendingToTop) { + pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow); + pw.print(" waitingToHide="); pw.print(waitingToHide); + pw.print(" sendingToBottom="); pw.print(sendingToBottom); + pw.print(" sendingToTop="); pw.println(sendingToTop); + } + } + + @Override + public String toString() { + if (stringName == null) { + StringBuilder sb = new StringBuilder(); + sb.append("WindowToken{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" token="); sb.append(token); sb.append('}'); + stringName = sb.toString(); + } + return stringName; + } +}
\ No newline at end of file diff --git a/services/jni/Android.mk b/services/jni/Android.mk index f5a5b4d..be37d5d 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -12,6 +12,7 @@ LOCAL_SRC_FILES:= \ com_android_server_LightsService.cpp \ com_android_server_PowerManagerService.cpp \ com_android_server_SystemServer.cpp \ + com_android_server_UsbService.cpp \ com_android_server_VibratorService.cpp \ com_android_server_location_GpsLocationProvider.cpp \ onload.cpp diff --git a/services/jni/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp index 0e162bd..aa8c9b3 100644 --- a/services/jni/com_android_server_AlarmManagerService.cpp +++ b/services/jni/com_android_server_AlarmManagerService.cpp @@ -33,7 +33,7 @@ #include <errno.h> #include <unistd.h> -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/ioctl.h> #include <linux/android_alarm.h> #endif @@ -42,7 +42,7 @@ namespace android { static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv* env, jobject obj, jint fd, jint minswest) { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS struct timezone tz; tz.tz_minuteswest = minswest; @@ -64,7 +64,7 @@ static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv* env, jo static jint android_server_AlarmManagerService_init(JNIEnv* env, jobject obj) { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS return open("/dev/alarm", O_RDWR); #else return -1; @@ -73,14 +73,14 @@ static jint android_server_AlarmManagerService_init(JNIEnv* env, jobject obj) static void android_server_AlarmManagerService_close(JNIEnv* env, jobject obj, jint fd) { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS close(fd); #endif } static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong seconds, jlong nanoseconds) { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS struct timespec ts; ts.tv_sec = seconds; ts.tv_nsec = nanoseconds; @@ -95,7 +95,7 @@ static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jin static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv* env, jobject obj, jint fd) { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS int result = 0; do diff --git a/services/jni/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp index d4513e9..98d0d92 100644 --- a/services/jni/com_android_server_BatteryService.cpp +++ b/services/jni/com_android_server_BatteryService.cpp @@ -33,7 +33,7 @@ #include <unistd.h> #include <dirent.h> -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/ioctl.h> #endif diff --git a/services/jni/com_android_server_InputApplication.cpp b/services/jni/com_android_server_InputApplication.cpp index a46a162..e64ec4e 100644 --- a/services/jni/com_android_server_InputApplication.cpp +++ b/services/jni/com_android_server_InputApplication.cpp @@ -77,11 +77,11 @@ void android_server_InputApplication_toNative( LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputApplication(JNIEnv* env) { - FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/InputApplication"); + FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/wm/InputApplication"); GET_FIELD_ID(gInputApplicationClassInfo.inputApplicationHandle, gInputApplicationClassInfo.clazz, - "inputApplicationHandle", "Lcom/android/server/InputApplicationHandle;"); + "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;"); GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz, "name", "Ljava/lang/String;"); diff --git a/services/jni/com_android_server_InputApplicationHandle.cpp b/services/jni/com_android_server_InputApplicationHandle.cpp index ab82635..3a1214f 100644 --- a/services/jni/com_android_server_InputApplicationHandle.cpp +++ b/services/jni/com_android_server_InputApplicationHandle.cpp @@ -106,11 +106,11 @@ static JNINativeMethod gInputApplicationHandleMethods[] = { LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputApplicationHandle(JNIEnv* env) { - int res = jniRegisterNativeMethods(env, "com/android/server/InputApplicationHandle", + int res = jniRegisterNativeMethods(env, "com/android/server/wm/InputApplicationHandle", gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); - FIND_CLASS(gInputApplicationHandleClassInfo.clazz, "com/android/server/InputApplicationHandle"); + FIND_CLASS(gInputApplicationHandleClassInfo.clazz, "com/android/server/wm/InputApplicationHandle"); GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, gInputApplicationHandleClassInfo.clazz, "ptr", "I"); diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 5b329bb..0a50ff8 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -87,7 +87,6 @@ static struct { jfieldID mName; jfieldID mSources; jfieldID mKeyboardType; - jfieldID mMotionRanges; } gInputDeviceClassInfo; static struct { @@ -1080,7 +1079,7 @@ static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) static JNINativeMethod gInputManagerMethods[] = { /* name, signature, funcPtr */ - { "nativeInit", "(Lcom/android/server/InputManager$Callbacks;)V", + { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;)V", (void*) android_server_InputManager_nativeInit }, { "nativeStart", "()V", (void*) android_server_InputManager_nativeStart }, @@ -1097,15 +1096,15 @@ static JNINativeMethod gInputManagerMethods[] = { { "nativeHasKeys", "(II[I[Z)Z", (void*) android_server_InputManager_nativeHasKeys }, { "nativeRegisterInputChannel", - "(Landroid/view/InputChannel;Lcom/android/server/InputWindowHandle;Z)V", + "(Landroid/view/InputChannel;Lcom/android/server/wm/InputWindowHandle;Z)V", (void*) android_server_InputManager_nativeRegisterInputChannel }, { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V", (void*) android_server_InputManager_nativeUnregisterInputChannel }, { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIII)I", (void*) android_server_InputManager_nativeInjectInputEvent }, - { "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V", + { "nativeSetInputWindows", "([Lcom/android/server/wm/InputWindow;)V", (void*) android_server_InputManager_nativeSetInputWindows }, - { "nativeSetFocusedApplication", "(Lcom/android/server/InputApplication;)V", + { "nativeSetFocusedApplication", "(Lcom/android/server/wm/InputApplication;)V", (void*) android_server_InputManager_nativeSetFocusedApplication }, { "nativeSetInputDispatchMode", "(ZZ)V", (void*) android_server_InputManager_nativeSetInputDispatchMode }, @@ -1135,13 +1134,13 @@ static JNINativeMethod gInputManagerMethods[] = { LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputManager(JNIEnv* env) { - int res = jniRegisterNativeMethods(env, "com/android/server/InputManager", + int res = jniRegisterNativeMethods(env, "com/android/server/wm/InputManager", gInputManagerMethods, NELEM(gInputManagerMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); // Callbacks - FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks"); + FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/wm/InputManager$Callbacks"); GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz, "notifyConfigurationChanged", "(J)V"); @@ -1150,22 +1149,22 @@ int register_android_server_InputManager(JNIEnv* env) { "notifyLidSwitchChanged", "(JZ)V"); GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz, - "notifyInputChannelBroken", "(Lcom/android/server/InputWindowHandle;)V"); + "notifyInputChannelBroken", "(Lcom/android/server/wm/InputWindowHandle;)V"); GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz, "notifyANR", - "(Lcom/android/server/InputApplicationHandle;Lcom/android/server/InputWindowHandle;)J"); + "(Lcom/android/server/wm/InputApplicationHandle;Lcom/android/server/wm/InputWindowHandle;)J"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz, "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz, "interceptKeyBeforeDispatching", - "(Lcom/android/server/InputWindowHandle;Landroid/view/KeyEvent;I)Z"); + "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Z"); GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, gCallbacksClassInfo.clazz, "dispatchUnhandledKey", - "(Lcom/android/server/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;"); + "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;"); GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz, "checkInjectEventsPermission", "(II)Z"); @@ -1189,7 +1188,7 @@ int register_android_server_InputManager(JNIEnv* env) { "getPointerLayer", "()I"); GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, gCallbacksClassInfo.clazz, - "getPointerIcon", "()Lcom/android/server/InputManager$PointerIcon;"); + "getPointerIcon", "()Lcom/android/server/wm/InputManager$PointerIcon;"); // KeyEvent @@ -1221,9 +1220,6 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gInputDeviceClassInfo.mKeyboardType, gInputDeviceClassInfo.clazz, "mKeyboardType", "I"); - GET_FIELD_ID(gInputDeviceClassInfo.mMotionRanges, gInputDeviceClassInfo.clazz, - "mMotionRanges", "[Landroid/view/InputDevice$MotionRange;"); - // Configuration FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration"); @@ -1239,7 +1235,7 @@ int register_android_server_InputManager(JNIEnv* env) { // PointerIcon - FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/InputManager$PointerIcon"); + FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/wm/InputManager$PointerIcon"); GET_FIELD_ID(gPointerIconClassInfo.bitmap, gPointerIconClassInfo.clazz, "bitmap", "Landroid/graphics/Bitmap;"); diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp index 7515456..8548b47 100644 --- a/services/jni/com_android_server_InputWindow.cpp +++ b/services/jni/com_android_server_InputWindow.cpp @@ -144,10 +144,10 @@ void android_server_InputWindow_toNative( LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputWindow(JNIEnv* env) { - FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow"); + FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/wm/InputWindow"); GET_FIELD_ID(gInputWindowClassInfo.inputWindowHandle, gInputWindowClassInfo.clazz, - "inputWindowHandle", "Lcom/android/server/InputWindowHandle;"); + "inputWindowHandle", "Lcom/android/server/wm/InputWindowHandle;"); GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz, "inputChannel", "Landroid/view/InputChannel;"); diff --git a/services/jni/com_android_server_InputWindowHandle.cpp b/services/jni/com_android_server_InputWindowHandle.cpp index 4d66212..5b74e43 100644 --- a/services/jni/com_android_server_InputWindowHandle.cpp +++ b/services/jni/com_android_server_InputWindowHandle.cpp @@ -116,18 +116,18 @@ static JNINativeMethod gInputWindowHandleMethods[] = { LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputWindowHandle(JNIEnv* env) { - int res = jniRegisterNativeMethods(env, "com/android/server/InputWindowHandle", + int res = jniRegisterNativeMethods(env, "com/android/server/wm/InputWindowHandle", gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); - FIND_CLASS(gInputWindowHandleClassInfo.clazz, "com/android/server/InputWindowHandle"); + FIND_CLASS(gInputWindowHandleClassInfo.clazz, "com/android/server/wm/InputWindowHandle"); GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, gInputWindowHandleClassInfo.clazz, "ptr", "I"); GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle, gInputWindowHandleClassInfo.clazz, - "inputApplicationHandle", "Lcom/android/server/InputApplicationHandle;"); + "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;"); return 0; } diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbService.cpp new file mode 100644 index 0000000..3c49e54 --- /dev/null +++ b/services/jni/com_android_server_UsbService.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2010 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 "UsbService" +#include "utils/Log.h" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" +#include "utils/Vector.h" + +#include <usbhost/usbhost.h> + +#include <stdio.h> +#include <asm/byteorder.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/usb/f_accessory.h> + +#define DRIVER_NAME "/dev/usb_accessory" + +namespace android +{ + +static struct file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; + jfieldID mDescriptor; +} gFileDescriptorOffsets; + +static struct parcel_file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; +} gParcelFileDescriptorOffsets; + +static jmethodID method_usbDeviceAdded; +static jmethodID method_usbDeviceRemoved; + +static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + LOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + +static int usb_device_added(const char *devname, void* client_data) { + struct usb_descriptor_header* desc; + struct usb_descriptor_iter iter; + + struct usb_device *device = usb_device_open(devname); + if (!device) { + LOGE("usb_device_open failed\n"); + return 0; + } + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject thiz = (jobject)client_data; + Vector<int> interfaceValues; + Vector<int> endpointValues; + const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device); + + uint16_t vendorId = usb_device_get_vendor_id(device); + uint16_t productId = usb_device_get_product_id(device); + uint8_t deviceClass = deviceDesc->bDeviceClass; + uint8_t deviceSubClass = deviceDesc->bDeviceSubClass; + uint8_t protocol = deviceDesc->bDeviceProtocol; + + usb_descriptor_iter_init(device, &iter); + + while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { + if (desc->bDescriptorType == USB_DT_INTERFACE) { + struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; + + // push class, subclass, protocol and number of endpoints into interfaceValues vector + interfaceValues.add(interface->bInterfaceNumber); + interfaceValues.add(interface->bInterfaceClass); + interfaceValues.add(interface->bInterfaceSubClass); + interfaceValues.add(interface->bInterfaceProtocol); + interfaceValues.add(interface->bNumEndpoints); + } else if (desc->bDescriptorType == USB_DT_ENDPOINT) { + struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc; + + // push address, attributes, max packet size and interval into endpointValues vector + endpointValues.add(endpoint->bEndpointAddress); + endpointValues.add(endpoint->bmAttributes); + endpointValues.add(__le16_to_cpu(endpoint->wMaxPacketSize)); + endpointValues.add(endpoint->bInterval); + } + } + + usb_device_close(device); + + // handle generic device notification + int length = interfaceValues.size(); + jintArray interfaceArray = env->NewIntArray(length); + env->SetIntArrayRegion(interfaceArray, 0, length, interfaceValues.array()); + + length = endpointValues.size(); + jintArray endpointArray = env->NewIntArray(length); + env->SetIntArrayRegion(endpointArray, 0, length, endpointValues.array()); + + env->CallVoidMethod(thiz, method_usbDeviceAdded, + env->NewStringUTF(devname), vendorId, productId, deviceClass, + deviceSubClass, protocol, interfaceArray, endpointArray); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + + return 0; +} + +static int usb_device_removed(const char *devname, void* client_data) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject thiz = (jobject)client_data; + + env->CallVoidMethod(thiz, method_usbDeviceRemoved, env->NewStringUTF(devname)); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return 0; +} + +static void android_server_UsbService_monitorUsbHostBus(JNIEnv *env, jobject thiz) +{ + struct usb_host_context* context = usb_host_init(); + if (!context) { + LOGE("usb_host_init failed"); + return; + } + // this will never return so it is safe to pass thiz directly + usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz); +} + +static jobject android_server_UsbService_openDevice(JNIEnv *env, jobject thiz, jstring deviceName) +{ + const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL); + struct usb_device* device = usb_device_open(deviceNameStr); + env->ReleaseStringUTFChars(deviceName, deviceNameStr); + + if (!device) + return NULL; + + int fd = usb_device_get_fd(device); + if (fd < 0) + return NULL; + int newFD = dup(fd); + usb_device_close(device); + + jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass, + gFileDescriptorOffsets.mConstructor); + if (fileDescriptor != NULL) { + env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, newFD); + } else { + return NULL; + } + return env->NewObject(gParcelFileDescriptorOffsets.mClass, + gParcelFileDescriptorOffsets.mConstructor, fileDescriptor); +} + +static void set_accessory_string(JNIEnv *env, int fd, int cmd, jobjectArray strArray, int index) +{ + char buffer[256]; + + buffer[0] = 0; + int length = ioctl(fd, cmd, buffer); + if (buffer[0]) { + jstring obj = env->NewStringUTF(buffer); + env->SetObjectArrayElement(strArray, index, obj); + env->DeleteLocalRef(obj); + } +} + + +static jobjectArray android_server_UsbService_getAccessoryStrings(JNIEnv *env, jobject thiz) +{ + int fd = open(DRIVER_NAME, O_RDWR); + if (fd < 0) { + LOGE("could not open %s", DRIVER_NAME); + return NULL; + } + jclass stringClass = env->FindClass("java/lang/String"); + jobjectArray strArray = env->NewObjectArray(4, stringClass, NULL); + if (!strArray) goto out; + set_accessory_string(env, fd, ACCESSORY_GET_STRING_MANUFACTURER, strArray, 0); + set_accessory_string(env, fd, ACCESSORY_GET_STRING_MODEL, strArray, 1); + set_accessory_string(env, fd, ACCESSORY_GET_STRING_TYPE, strArray, 2); + set_accessory_string(env, fd, ACCESSORY_GET_STRING_VERSION, strArray, 3); + +out: + close(fd); + return strArray; +} + +static jobject android_server_UsbService_openAccessory(JNIEnv *env, jobject thiz) +{ + int fd = open(DRIVER_NAME, O_RDWR); + if (fd < 0) { + LOGE("could not open %s", DRIVER_NAME); + return NULL; + } + jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass, + gFileDescriptorOffsets.mConstructor); + if (fileDescriptor != NULL) { + env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, fd); + } else { + return NULL; + } + return env->NewObject(gParcelFileDescriptorOffsets.mClass, + gParcelFileDescriptorOffsets.mConstructor, fileDescriptor); +} + +static JNINativeMethod method_table[] = { + { "monitorUsbHostBus", "()V", (void*)android_server_UsbService_monitorUsbHostBus }, + { "nativeOpenDevice", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", + (void*)android_server_UsbService_openDevice }, + { "nativeGetAccessoryStrings", "()[Ljava/lang/String;", + (void*)android_server_UsbService_getAccessoryStrings }, + { "nativeOpenAccessory","()Landroid/os/ParcelFileDescriptor;", + (void*)android_server_UsbService_openAccessory }, +}; + +int register_android_server_UsbService(JNIEnv *env) +{ + jclass clazz = env->FindClass("com/android/server/usb/UsbService"); + if (clazz == NULL) { + LOGE("Can't find com/android/server/usb/UsbService"); + return -1; + } + method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIII[I[I)V"); + if (method_usbDeviceAdded == NULL) { + LOGE("Can't find usbDeviceAdded"); + return -1; + } + method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved", "(Ljava/lang/String;)V"); + if (method_usbDeviceRemoved == NULL) { + LOGE("Can't find usbDeviceRemoved"); + return -1; + } + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V"); + gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + clazz = env->FindClass("android/os/ParcelFileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); + gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); + LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL, + "Unable to find constructor for android.os.ParcelFileDescriptor"); + + return jniRegisterNativeMethods(env, "com/android/server/usb/UsbService", + method_table, NELEM(method_table)); +} + +}; diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp index bdd6d80..37b520b 100644 --- a/services/jni/onload.cpp +++ b/services/jni/onload.cpp @@ -13,6 +13,7 @@ int register_android_server_InputWindowHandle(JNIEnv* env); int register_android_server_InputManager(JNIEnv* env); int register_android_server_LightsService(JNIEnv* env); int register_android_server_PowerManagerService(JNIEnv* env); +int register_android_server_UsbService(JNIEnv* env); int register_android_server_VibratorService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); int register_android_server_location_GpsLocationProvider(JNIEnv* env); @@ -40,6 +41,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) register_android_server_LightsService(env); register_android_server_AlarmManagerService(env); register_android_server_BatteryService(env); + register_android_server_UsbService(env); register_android_server_VibratorService(env); register_android_server_SystemServer(env); register_android_server_location_GpsLocationProvider(env); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index f64fd7b..57af001 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -56,6 +56,8 @@ Layer::Layer(SurfaceFlinger* flinger, mNeedsBlending(true), mNeedsDithering(false), mSecure(false), + mProtectedByApp(false), + mProtectedByDRM(false), mTextureManager(), mBufferManager(mTextureManager), mWidth(0), mHeight(0), mNeedsScaling(false), mFixedSize(false) @@ -140,7 +142,8 @@ void Layer::onRemoved() sp<LayerBaseClient::Surface> Layer::createSurface() const { - return mSurface; + sp<Surface> sur(new SurfaceLayer(mFlinger, const_cast<Layer *>(this))); + return sur; } status_t Layer::ditch() @@ -150,9 +153,6 @@ status_t Layer::ditch() // the layer is not on screen anymore. free as much resources as possible mFreezeLock.clear(); - // Free our own reference to ISurface - mSurface.clear(); - Mutex::Autolock _l(mLock); mWidth = mHeight = 0; return NO_ERROR; @@ -190,6 +190,8 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, mReqHeight = h; mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; + mProtectedByApp = (flags & ISurfaceComposer::eProtectedByApp) ? true : false; + mProtectedByDRM = (flags & ISurfaceComposer::eProtectedByDRM) ? true : false; mNeedsBlending = (info.h_alpha - info.l_alpha) > 0 && (flags & ISurfaceComposer::eOpaque) == 0; @@ -198,7 +200,6 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED); mNeedsDithering = layerRedsize > displayRedSize; - mSurface = new SurfaceLayer(mFlinger, this); return NO_ERROR; } @@ -340,6 +341,45 @@ void Layer::onDraw(const Region& clip) const drawWithOpenGL(clip, tex); } +// As documented in libhardware header, formats in the range +// 0x100 - 0x1FF are specific to the HAL implementation, and +// are known to have no alpha channel +// TODO: move definition for device-specific range into +// hardware.h, instead of using hard-coded values here. +#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF) + +bool Layer::needsBlending(const sp<GraphicBuffer>& buffer) const +{ + // If buffers where set with eOpaque flag, all buffers are known to + // be opaque without having to check their actual format + if (mNeedsBlending && buffer != NULL) { + PixelFormat format = buffer->getPixelFormat(); + + if (HARDWARE_IS_DEVICE_FORMAT(format)) { + return false; + } + + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + if (!err && info.h_alpha <= info.l_alpha) { + return false; + } + } + + // Return opacity as determined from flags and format options + // passed to setBuffers() + return mNeedsBlending; +} + +bool Layer::needsBlending() const +{ + if (mBufferManager.hasActiveBuffer()) { + return needsBlending(mBufferManager.getActiveBuffer()); + } + + return mNeedsBlending; +} + bool Layer::needsFiltering() const { if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { @@ -475,6 +515,10 @@ uint32_t Layer::getEffectiveUsage(uint32_t usage) const // request EGLImage for all buffers usage |= GraphicBuffer::USAGE_HW_TEXTURE; } + if (mProtectedByApp || mProtectedByDRM) { + // need a hardware-protected path to external video sink + usage |= GraphicBuffer::USAGE_PROTECTED; + } return usage; } @@ -583,6 +627,9 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) // we retired a buffer, which becomes the new front buffer const bool noActiveBuffer = !mBufferManager.hasActiveBuffer(); + const bool activeBlending = + noActiveBuffer ? true : needsBlending(mBufferManager.getActiveBuffer()); + if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) { LOGE("retireAndLock() buffer index (%d) out of range", int(buf)); mPostedDirtyRegion.clear(); @@ -597,6 +644,12 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); if (newFrontBuffer != NULL) { + if (!noActiveBuffer && activeBlending != needsBlending(newFrontBuffer)) { + // new buffer has different opacity than previous active buffer, need + // to recompute visible regions accordingly + recomputeVisibleRegions = true; + } + // get the dirty region // compute the posted region const Region dirty(lcblk->getDirtyRegion(buf)); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 2908119..bccc900 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -75,10 +75,13 @@ public: virtual uint32_t doTransaction(uint32_t transactionFlags); virtual void lockPageFlip(bool& recomputeVisibleRegions); virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); - virtual bool needsBlending() const { return mNeedsBlending; } + virtual bool needsBlending(const sp<GraphicBuffer>& buffer) const; + virtual bool needsBlending() const; virtual bool needsDithering() const { return mNeedsDithering; } virtual bool needsFiltering() const; virtual bool isSecure() const { return mSecure; } + virtual bool isProtectedByApp() const { return mProtectedByApp; } + virtual bool isProtectedByDRM() const { return mProtectedByDRM; } virtual sp<Surface> createSurface() const; virtual status_t ditch(); virtual void onRemoved(); @@ -211,14 +214,15 @@ private: ClientRef mUserClientRef; // constants - sp<Surface> mSurface; PixelFormat mFormat; const GLExtensions& mGLExtensions; bool mNeedsBlending; bool mNeedsDithering; // page-flip thread (currently main thread) - bool mSecure; + bool mSecure; // no screenshots + bool mProtectedByApp; // application requires protected path to external sink + bool mProtectedByDRM; // DRM agent requires protected path to external sink Region mPostedDirtyRegion; // page-flip thread and transaction thread (currently main thread) diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index 86057f8..6025ed4 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -540,7 +540,9 @@ int32_t LayerBaseClient::sIdentity = 1; LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client) - : LayerBase(flinger, display), mClientRef(client), + : LayerBase(flinger, display), + mHasSurface(false), + mClientRef(client), mIdentity(uint32_t(android_atomic_inc(&sIdentity))) { } @@ -557,12 +559,13 @@ sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() { sp<Surface> s; Mutex::Autolock _l(mLock); - s = mClientSurface.promote(); - if (s == 0) { - s = createSurface(); - mClientSurface = s; - mClientSurfaceBinder = s->asBinder(); - } + + LOG_ALWAYS_FATAL_IF(mHasSurface, + "LayerBaseClient::getSurface() has already been called"); + + mHasSurface = true; + s = createSurface(); + mClientSurfaceBinder = s->asBinder(); return s; } diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h index 8ed4749..bfe92e6 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -196,6 +196,18 @@ public: */ virtual bool isSecure() const { return false; } + /** + * isProtectedByApp - true if application says this surface is protected, that + * is if it requires a hardware-protected data path to an external sink. + */ + virtual bool isProtectedByApp() const { return false; } + + /** + * isProtectedByDRM - true if DRM agent says this surface is protected, that + * is if it requires a hardware-protected data path to an external sink. + */ + virtual bool isProtectedByDRM() const { return false; } + /** Called from the main thread, when the surface is removed from the * draw list */ virtual status_t ditch() { return NO_ERROR; } @@ -325,7 +337,7 @@ protected: private: mutable Mutex mLock; - mutable wp<Surface> mClientSurface; + mutable bool mHasSurface; wp<IBinder> mClientSurfaceBinder; const wp<Client> mClientRef; // only read diff --git a/services/surfaceflinger/LayerDim.h b/services/surfaceflinger/LayerDim.h index 5631c0a..75f9a89 100644 --- a/services/surfaceflinger/LayerDim.h +++ b/services/surfaceflinger/LayerDim.h @@ -37,8 +37,10 @@ public: virtual ~LayerDim(); virtual void onDraw(const Region& clip) const; - virtual bool needsBlending() const { return true; } - virtual bool isSecure() const { return false; } + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + virtual bool isProtectedByApp() const { return false; } + virtual bool isProtectedByDRM() const { return false; } virtual const char* getTypeId() const { return "LayerDim"; } }; |